Po jakim Typie danych join najszybszy?

Po jakim Typie danych join najszybszy?
LE
  • Rejestracja:prawie 10 lat
  • Ostatnio:6 miesięcy
  • Postów:68
0

Cześć Wszystkim,

Dziś mam dość ciekawe pytanie do Was ( tzn dla mnie jest ciekawa odpowiedz :))

w tabeli A mam kolumnę Data datetime
a w tabeli B mam kolumnę Artykul int, dataOd datetime i dataDo datatime.

Kopiuj
SELECT
	t1.Data 
	,t2.Artykul
FROM
	A t1
INNER JOIN
	B t2 ON t1.Data between t2.data_od and t2.data_do

i to działa dobrze, szybko średnio, indeksy mam założone.
idzie 7 sekund,

ale kiedy zrobiłem casta z datetime na inta dla wszystkich kolumn i w joinie połączyłem to po intach to idzie 5 sek, wynik ten sam, szybciej, super.

Moje pytanie brzmi czy może jest jeszcze jakiś inny typ danych który mógłby iść jeszcze szybciej?

Pozdrawiam.

AB
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 8 lat
  • Postów:229
0

Czy to zapytanie ma realizować jakieś zadanie w praktyce, czy tylko ma
testować bazę danych? Jeśli ma coś robić praktycznego, to chętnie bym
się dowiedział co.

Typy na których baza powinna działać najwydajniej, to int32, int64.

P7
  • Rejestracja:około 9 lat
  • Ostatnio:około 8 godzin
  • Lokalizacja:Warszawa
  • Postów:89
0

myślę, że jest tak dlatego, że int "waży" 4 bajty a datetime 8, i indeks sobie wolniej z tym radzi - możesz spróbować porównać z jakimś smallint-em lub tinyint-em

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
1

DATETIME jest bardzo wolnym typem danych, jeśli chodzi o obliczenia i porównania. Zabawa z castem tego nie załatwia za bardzo. Najlepiej by było, gdybyś zamiast DATETIME używał inta - jako czas uniksowy (ilość sekund, która upłynęła od któregoś tam roku). Wtedy w tabeli będą inty, join będzie po intach, porównania będą po intach. To da największą optymalizację. Poza tym używaj warunków. Zawsze pobieraj tylko taki zakres danych, jaki potrzebujesz. Ale używanie warunków też trzeba robić z głową. Bo możesz skonstruować waruenk, który przefiltruje już dane po joinach i to niewiele da. Filtrowanie danych przed joinami da mniejszy zbiór do joina - więc będzie szybciej. To tak na przyszłość. Natomiast faktycznie zastanów się nad przerobieniem datetime na int. Dodatkowo możesz stworzyć sobie widok, który w dodatkowej kolumnie zwróci Ci z tego inta datę czytelną dla człowieka.

LE
dla mnie ważne są tylko dni, na godziny nie zwracam uwagi wiec zwykły cast datetime na int daje mi ilość dni które upłynęły od 1900 roku i to mnie zadowala, a jeśli bym miał milisekundy które upłyneły to byłyby to większe wartości i to pewnie byłoby gorsze dni. Ale dzięki, każda uwaga jest dobra :)
JU
Ciebie to zadowala, ale nie bazę danych. CAST to jest dodatkowa funkcja, jaką baza musi przetrawić. I jest zupełnie niepotrzebna. Jeśli zależy Ci na optymalizacji, to nie porównuj po datach ani po stringach. Tutaj serwer widocznie jakoś sobie tego casta optymalizuje, ale Twoje rozwiązanie będzie wolne w porównaniu z tym, jakie mogłoby być.
LE
No w gruncie rzeczy tak, ale wszystkie systemy insertują daty do tabel, nikt się nie zgodzi na to aby insertowały inty a później żebym pokazywał użytkownikowi Daty. Po prostu wygodniej będzie sprawdzać cokolwiek jak nie trzeba za kazdym razem convertowac inta na date. Ale zgodzę się z Tobą, że na pewno jest to szybsze i zajmuje mniej miejsca w bazie, ale z łatwością dostępu i czytelnością już trochę gorzej. uczę się ciągle, mam dopiero 1,5 roku doświadczenia więc to nie jest zbyt dużo i na pewno jeszcze dużo rzeczy muszę się nauczyć :)
JU
Nie ma żadnego problemu z czytelnością. Do odczytu robisz sobie widok, jeśli musisz dodać rekord z palca, to posługujesz się funkcją konwertującą. W aplikacjach bazodanowych najważniejsza jest wydajność. I lepiej zmieniać daty na inty, gdy baza jest stosunkowo mała. Wierz mi. I wcale nie wszystkie systemy insertują daty. Ewentualnie nie porównują po datach.
JU
Poza tym z datami jest dodatkowy problem związany z lokalizacją. Jeśli masz inty, tego problemu nie ma.
LE
  • Rejestracja:prawie 10 lat
  • Ostatnio:6 miesięcy
  • Postów:68
0

Ok to pisze wszystko co i jak.

Więc mam zakresy dat aktywności artykułu w hurtowni lecz te daty mogą na siebie zachodzić, po prostu mam nie uporządkowane daty i chce je uporządkować.

Wrzuć sobie dane z excela do tabeli:

Kopiuj
CREATE TABLE #Source
	(
		Artykul INT
		,data_od DATE
		,data_do DATE
		,data_odInt INT
		,data_doInt INT
	) 
	
	CREATE CLUSTERED INDEX [IDX_data_od_Source] ON #Source 
	(
		data_od ASC
		
	) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

następnie z tego zakresu sobie biorę najmniejszą datę i największą ale data 9999-09-09 oznacza nieskonczonosc wiec ją dodaje jedną, wywnioskujesz po kodzie chyba.

Kopiuj
CREATE TABLE #date 
(
	[Data] DATE 
	,[DataInt] INT 
)

CREATE CLUSTERED INDEX [IDX_Date_DATE] ON #date 
	(
		[Data] ASC	
	)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
	
-- wyciągnięcie maksymalnej i minimalnej daty aby stworzyc zakres
DECLARE 
	@startDate DATETIME -- początek zakresu 
	,@stopDate DATE -- koniec zakresu
	,@maxDate1 DATE -- max data z daty od
	,@maxDate2 DATE -- max data z daty do nie uwzględniajac daty do nieskonczonosci

SELECT @startDate = MIN(data_od),@maxDate1 = MAX(data_od) FROM #Source 
SELECT @maxDate2 = MAX(data_do) FROM #Source where data_do<>'9999-09-09 00:00:00.000'
SET @stopDate= CASE WHEN @maxDate1<@maxDate2 THEN @maxDate2 ELSE @maxDate1 END

DECLARE @i INT= 1;

-- tworzenei zakresu per dzien
WHILE @startDate <= @stopDate
BEGIN
	INSERT INTO #date([Data],[DataInt])
	VALUES (@startDate,cast(@startDate as INT))
	SET @i += 1
	SET @startDate = DATEADD(day,1,@startDate)
END

--dodanie 1go wpisu z nieskonczonoscia
INSERT INTO #date([Data],[DataInt])
VALUES ('9999-09-09',cast(cast('9999-09-09' as datetime) as int))

No i teraz łączenie zakresów.

Kopiuj
SELECT 
	*
	--INTO #test2
FROM(
	SELECT 
		Artykul
		,StartDate
		,CASE WHEN lead(StartDate,1) OVER (PARTITION BY Artykul ORDER BY Artykul,StartDate)='9999-09-09' THEN lead(StartDate,1) OVER (PARTITION BY Artykul ORDER BY Artykul,StartDate) ELSE EndDate END AS EndDate
	from(
		select 
			Artykul
			,CASE WHEN lag is null  or lag<-1   THEN Data END AS StartDate
			,CASE WHEN lead is null or lead>1  THEN Data ELSE LEAD(Data,1) OVER (PARTITION BY Artykul ORDER BY Artykul,Data) END AS EndDate
		from(
			SELECT
				cast(t1.Data as DATE) as Data 
				,t2.Artykul
				,DATEDIFF(DAY,t1.Data,LAG(t1.Data,1) OVER (PARTITION BY t2.Artykul ORDER BY t2.Artykul,t1.Data)) lag
				,DATEDIFF(DAY,t1.Data,LEAD(t1.Data,1) OVER (PARTITION BY t2.Artykul ORDER BY t2.Artykul,t1.Data)) lead
			FROM
				#date t1-- daty z zakresem od do per dzien
			INNER JOIN
				#Source t2 ON t1.[DataInt] between t2.data_odInt and t2.data_doInt-- łądzenie po intach ponieważ jest szybciej
				--#Source t2 ON t1.Data between t2.data_od and t2.data_do-- łądzenie po datach ale jest wolniej
			GROUP BY
				t1.Data 
				,t2.Artykul
			)s1
		WHERE 
			lag<>-1 or lag is null or lead<>1 or lead is null
		)s2
	)s3
WHERE StartDate is not null and StartDate<>'9999-09-09'

Oczywiście mógłbym to zrobić na kursorze, ale cały myk polega na tym, że chciałem się sprawdzić i zobaczyć czy da się zrobić bez kursora :)

Ogólnie udało się jednak nie chodzi super szybko.

AB
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 8 lat
  • Postów:229
0
leonkuczma napisał(a):

Ok to pisze wszystko co i jak.

Więc mam zakresy dat aktywności artykułu w hurtowni lecz te daty mogą na siebie zachodzić, po prostu mam nie uporządkowane daty i chce je uporządkować.

Wrzuć sobie dane z excela do tabeli:

następnie z tego zakresu sobie biorę najmniejszą datę i największą ale data 9999-09-09 oznacza nieskonczonosc wiec ją dodaje jedną, wywnioskujesz po kodzie chyba.

Już po pierwszym zdaniu trudno zrozumieć :) Dlaczego uważasz że to normlanie iż zakresy dat nie nachodzą na siebie? W wielu bazach
zakresy na siebie nachodzą. Dlaczego nieuporządkowane to są po prostu nachodzące na siebie? Opisz problem, do czego to jest potrzebne?

LE
  • Rejestracja:prawie 10 lat
  • Ostatnio:6 miesięcy
  • Postów:68
0

ło Panie

1 2009-03-20 00:00:00.000 2009-04-05 00:00:00.000
1 2009-10-26 00:00:00.000 2009-11-21 00:00:00.000
1 2009-11-16 00:00:00.000 2010-04-11 00:00:00.000
1 2009-12-23 00:00:00.000 2010-01-11 00:00:00.000
1 2010-01-11 00:00:00.000 2010-04-14 00:00:00.000
1 2011-03-17 00:00:00.000 2011-07-14 00:00:00.000
1 2011-06-12 00:00:00.000 2011-08-14 00:00:00.000
1 2011-08-15 00:00:00.000 2011-08-21 00:00:00.000

a chcę tak

1 2009-03-20 2009-04-05
1 2009-10-26 2010-04-14
1 2011-03-17 2011-08-21

no to tak mniej wiecej:)

potrzebuje to zrobić aby wyświetlić juz ładnie dane użytkownikowi.

edytowany 2x, ostatnio: leonkuczma
LE
  • Rejestracja:prawie 10 lat
  • Ostatnio:6 miesięcy
  • Postów:68
0

wymagań czasowych Ci nie powiem tak samo jak pamięciowych, musi być jak najszybciej, ale bez kursora,
to trochę taki mój wymysł, chciałem coś zrobić w miarę optymalnego, więc nie mam określonych wymagań co do pamięci i szybkości.
Wiem po prostu że nie jest to najszybsze rozwiązanie bo kursor po tych samych danych chodzi w podobnym czasie, lub o jakąś sekundę czy dwie szybciej.

Danych wejściowych jest około 35k rows.
W takiej formie jak w excelu.

Dane wyjściowe muszą być połączone w jedną datę od do jeśli zachodzą na siebie.
Po prostu później generuję plik z tych danych i nie chce w pliku mieć niepotrzebnych wpisów
typu:
2007-11-20 2015-07-09
2015-06-20 2016-11-30

tylko jednen wpis
2007-11-20 2016-11-30

itd.

inaczej chyba nie umiem tego przekazać :/

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0

na 99% jeśli po zamianie datetime na int przy użyciu CAST wyszukiwanie przyśpieszyło to baza NIE UŻYWA indeksów. Pierwsze co powinieneś zrobić to zobaczyć jak wygląda plan zapytania.
Teoretycznie indeks na kolumnach data_od, data_do powinien przyśpieszyć jeszcze bardziej


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
LE
  • Rejestracja:prawie 10 lat
  • Ostatnio:6 miesięcy
  • Postów:68
0

z tego co widzę na planie to używa, ale średnio umiem czytać plany, więc mogę się mylic :/

AB
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 8 lat
  • Postów:229
0
leonkuczma napisał(a):

wymagań czasowych Ci nie powiem tak samo jak pamięciowych, musi być jak najszybciej, ale bez kursora,
to trochę taki mój wymysł, chciałem coś zrobić w miarę optymalnego, więc nie mam określonych wymagań co do pamięci i szybkości.
Wiem po prostu że nie jest to najszybsze rozwiązanie bo kursor po tych samych danych chodzi w podobnym czasie, lub o jakąś sekundę czy dwie szybciej.

Danych wejściowych jest około 35k rows.
W takiej formie jak w excelu.

Dane wyjściowe muszą być połączone w jedną datę od do jeśli zachodzą na siebie.
Po prostu później generuję plik z tych danych i nie chce w pliku mieć niepotrzebnych wpisów
typu:
2007-11-20 2015-07-09
2015-06-20 2016-11-30

tylko jednen wpis
2007-11-20 2016-11-30

itd.

inaczej chyba nie umiem tego przekazać :/

Trzeba użyć podstaw matematyki: zbiory, pary, krotki, część wspólna, kwantyfikator ogólny, szczegółowy.
To zdanie naprawdę nie ma żadnego sensu:
"Dane wyjściowe muszą być połączone w jedną datę od do jeśli zachodzą na siebie"

Ja na razie domyślam się, że chodzi o coś takiego:
Mamy zbiór A (uporządkowanych) par (start,end). Pierwszy element pary jest datą początkową, drugi jest datą
końcową. Data początkowa jest zawsze mniejsza lub równa od daty końcowej. Te pary nazywam
zakresami. W zbiorze A trzeba polączyć wszystkie zakresy x i y z częścią wspólną w jeden zakres,
poprzez:
nowy.start = min( x.start , y.start )
nowy.end = max( x.end , y.end )
usuń x i y.

Przyznaję, trzeba chwilę się zastanowić nad tym zadaniem ( o ile dobrze zrozumialem treść ).

Pozdrawiam

P.S.
Jutro wrzucę przykład, ale niestety ani nie znam, ani nie mam mssqla, więc wrzucę w postgresie. Może na
coś Ci się przyda, może zrobimy porównanie wydajnosci obu baz na tym zadaniu.

edytowany 1x, ostatnio: artur_bredzki
AB
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 8 lat
  • Postów:229
0

Głowy bym nie dał, ale chyba działa poprawnie. Na pewno masz wskazówkę jak zbudować sqla.

U mnie zamula to niemiłosiernie, ale póki co nic nie optymalizowałem. Bez dłuższego zastanowienia,
uważam że ndeksy wiele nie pomogą, potrzebny jest jakiś sprytny algorytm, albo jakieś rozszerzenie
bazy danych.

Kopiuj
--trochę dupereli
rollback;

\pset pager off
\timing off
SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

\c postgres;
set role postgres;

--czyścimy stare śmieci
DROP DATABASE IF EXISTS test_rng;
DROP ROLE IF EXISTS test_rng;


--zakładamy nową bazę i usera
CREATE ROLE test_rng PASSWORD 'test_rng' NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;
CREATE DATABASE test_rng OWNER test_rng ENCODING 'UTF-8';
\c test_rng;
set role test_rng;


CREATE SEQUENCE gseq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1000;


CREATE TABLE ranges (
    id      bigint DEFAULT nextval('gseq') NOT NULL,
    fstart  int NOT NULL,
    fend    int NOT NULL
);
ALTER TABLE ONLY ranges ADD CONSTRAINT ranges_pkey PRIMARY KEY (id);

--CREATE INDEX idx_fstart ON ranges USING btree (fstart);
--CREATE INDEX idx_fend ON ranges USING btree (fend);
--CREATE INDEX idx_fboth ON ranges USING btree (fstart,fend);


create or replace function mk_data(length integer) returns void as 
$$
declare
  i       integer;
  vfstart integer;
  vfend   integer;
begin
  for i in 1..length loop
    vfstart = floor( random() * 20000 + 1 );
    vfend   = floor( random() *    30 + 1 ) + vfstart;
    execute 'insert into ranges (fstart,fend) values($1,$2)' using vfstart,vfend;
  end loop;
end;
$$ language plpgsql;



create or replace function mk_connect() returns void as 
$$
declare
  cnt1   integer;
  cnt2   integer;
begin
  loop
    execute 'with rows as (update ranges r1 set fstart = (select min(r2.fstart) from ranges as r2 where r2.fstart < r1.fstart and r2.fend >= r1.fstart) where exists (select * from ranges as r2 where r1.id <> r2.id and r2.fstart < r1.fstart and r2.fend >= r1.fstart ) returning 1) select count(*) from rows' into cnt1;
    execute 'with rows as (update ranges r1 set fend = (select max(r2.fend) from ranges as r2 where r2.fend > r1.fend and r2.fstart <= r1.fend) where exists (select * from ranges as r2 where r1.id <> r2.id and r2.fend > r1.fend and r2.fstart <= r1.fend ) returning 1) select count(*) from rows' into cnt2;
    if cnt1 + cnt2 < 1 then exit; end if;
  end loop;
-- execute 'with deleted as (delete from ranges r1 where exists (select * from ranges as r2 where r2.id <> r1.id and r2.fstart < r1.fstart and r2.fend > r1.fend) is true returning *) select count(*) from deleted' into cnt_del;  
end;
$$ language plpgsql;


\timing on
select mk_data(7000);
--select * from ranges;
select mk_connect();


select distinct fstart, fend from ranges order by fstart, fend;

WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
1
Juhas napisał(a):

DATETIME jest bardzo wolnym typem danych, jeśli chodzi o obliczenia i porównania. Zabawa z castem tego nie załatwia za bardzo. Najlepiej by było, gdybyś zamiast DATETIME używał inta - jako czas uniksowy (ilość sekund, która upłynęła od któregoś tam roku). Wtedy w tabeli będą inty, join będzie po intach, porównania będą po intach. To da największą optymalizację.

Dobra, starczy bo dalej to już dla mnie za duża herezja...

@Juhas Ty tak naprawdę czy dla zgrywy? A może w Comarchu pracujesz, oni też mają takie pomysły aby datę pchać do inta (ale to zaszłość, z tego co wiem).
Poza tym oczywiście wiesz, że mssql DateTime przechowuje jako dwa int32 (a więc jego długość to 8 bajtów - dokładnie tyle samo co int64)?
Natomiast sam typ Date to jest 3 bajty, a int32 - 4 bajty.
Mówisz o jakiejś optymalizacji?
Jakiej konkretnie, ciekawość.

I to ma być szybsze? Gdzie, jak?
Wszelki dostęp do takich danych wymaga konwersji, a to niepotrzebny narzut. A więc - po co?

MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 6 godzin
  • Postów:1082
0
wloochacz napisał(a):

A może w Comarchu pracujesz, oni też mają takie pomysły aby datę pchać do inta (ale to zaszłość, z tego co wiem).
Jakiś rok temu miałem okazję robić mały programik przegrywający dokumenty z jednego systemu do drugiego. System źródłowy właśnie trzymał datę dokumentu jako int w dodatku jako liczbę dni od jakiejś tam daty. Nie wiem kto mógł wpaść na taki bzdurny pomysł.

U siebie datę trzymam natomiast jako DATE, filtruję dane, robię zestawienia po tej dacie przy założonym indeksie i nie odczuwam żadnego spowolnienia. Wszystko działa ładnie. Ogólnie to nie wiem skąd takie przekonanie że DATE ma być wolniejsze niż int. Rozumiem porównanie VARCHAR(300) vs INT...

JU
A Ty myślisz, że jak wewnętrznie jest przechowywana data w komputerach?
MY
@Juhas DATE to są po prostu 3bajty i koniec. Nie ma tu żadnej konwersji podczas porównywania. Znaczy jest, ale tym już zajmuje się preprocesor zapytania jak mu zapodasz datę w formacie YYYY/MM/DD to on zamienia sobie na tymp DATE i w takiej formie następuje porównanie. Serwer nie porównuje dat zmieniając na stringi YYYY/MM/DD
JU
Napisałeś: "System (...) trzymał datę dokumentu jako int w dodatku jako liczbę dni od jakiejś tam daty. Nie wiem kto mógł wpaść na taki bzdurny pomysł." - właśnie w taki sposób jest wewnętrznie reprezentowana data na komputerach. Jeśli chodzi o unix, to jest liczba sekund od któregoś tam roku. Jeśli Windows, to jest już double, a nie int i przed przecinkiem masz zdaje się ilość dni od 1900 roku, a po przecinku ilość sekund w dniu. Więc to nie jest bzdurny pomysł, tylko tak działają komputery. Moje pytanie było sarkastyczne :)
MY
A to tak. Jednak niech konwersją na czytelną na człowieka postać zajmuje się serwer a nie aplikacja kliencka. Może jestem zbyt restrykcyjny, ale zawsze trzymam dane w takim typie jaki to jest najbardziej naturalny. Skoro mam typ DATE to nie chce trzymać ręcznie daty jako liczbę dni :)
JU
Wychodzi na to, że się nie zrozumieliśmy. DATE - ok. Ja mówię cały czas o DATETIME. Jeśli masz wybór DATE albo int, to wtedy oczywiście bardziej naturalne i lepsze jest DATE.
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0
wloochacz napisał(a):

Wszelki dostęp do takich danych wymaga konwersji, a to niepotrzebny narzut. A więc - po co?

Konwersji wymaga tylko taki select, który ma być wyświetlony użytkownikowi. Robiłem kilka lat temu dość spory system składający się z 3 aplikacji i dodatkowych pluginów. Optymalizacja była tutaj kluczowym aspektem, bo nawet skomplikowane obliczenia trwające 20 - 30 sekund były niesamowicie długie. Baza była optymalizowana (w ogóle cały system) przez kilka miesięcy. Zmiana DATETIME na int była jedną z metod optymalizacji, która dała radę i udało się wyciągnąć następne kilkanaście - kilkadziesiąt sekund. Jeśli komuś nie zależy, czy obliczenia wykonują się 2, czy 5 minut, to nie ma to żadnego znaczenia. Ale w systemach, gdzie szybkość jest bardzo kluczowa, to zmiana datetime na int jest jedną z tych rzeczy, które przyspieszają działanie.
Oczywiście największa odczuwalna zmiana pojawia się po prawidłowym nadaniu indeksów. Ale jeśli chcemy wyciągnąć, ile się da, to indeksy to za mało.

MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 6 godzin
  • Postów:1082
0

@Juhas dobrze, ale pokaż mi dlaczego zamiana DATE na INT ma przynieść tak szalone różnice w wydajności. Ja też mam system gdzie robię zestawienia po datach. Datę trzymam jako DATE i nic nie muli. Przy założonym indeksie na datę w zasadzie nie mam różnicy czy zapytanie wykonuję filtrując dane po indeksie głównym w postaci INT'a czy po datach.

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

@mr.YaHooo, nie mówię o zmianie DATE na int, ale o zmianie DATETIME na int, a to jest jednak różnica. Nie wiem, dlaczego to daje takie rezultaty. Baza była optymalizowana przez jakieś pół roku (jeśli nie dłużej), codziennie czytałem przynajmniej jakiś jeden artykuł odnośnie optymalizacji. To była walka o naprawdę każdą sekundę. Może to kwestia ilości danych, może powiązań między tabelami, może złożoności problemu. Nie wiem. Wiem, że nauczyło mnie to, że jeśli w grę wchodzi optymalizacja, to to daje rezultaty.

MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 6 godzin
  • Postów:1082
0

@Juhas rzeczywiście nie zrozumieliśmy się :) DATETIME ma większy rozmiar w bajtach niż int, więc siłą rzeczy samo porównanie powinno być wolniejsze niż dwóch intów. Jednak o ile nie mam pojęcia, aż tak nie wnikałem. W moich systemach nie potrzeba informacji o godzinie dokumentu. Liczy się tylko data i to po niej robię zestawienia. Co prawda jest kolumna DATETIME, ale tyczy się wprowadzenia oraz edycji rekodru. Tak aby było wiadomo kto kiedy wprowadził i zmienił rekord. Jednak to sa kolumny czysto informacyjne.

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0

@Juhas ale wiesz, że są indeksu funkcyjne i jak masz pole DateTime to nic nie stoi na przeszkodzie aby zrobić indeks na Trunc(pole_datetime) i potem porównywać tak WHERE trunc(pole_datetime) = sama_data


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

A jeśli potrzeba i daty, i czasu? :)

PA
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 5 godzin
  • Postów:3866
0

Można to zrobić bez użycia kursora i pętli, do zapytania potrzebujemy oznaczenia czy dany rekord ma w dacie od datę początkową, czy datę końcową, czy obie, później pozostajeproste zapytanie o te wartości.

Krokowo

  1. widok cteS pobiera dane z tabeli i pobiera poprzednie daty od/do (p_od,p_do) oraz następne daty od/do (n_do,n_od)
    n_od | n_do | p_od | p_do | Artykul | data_od | data_do | data_odInt | data_doInt
    ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ----------------
    2009-10-26 | 2009-11-21 | NULL | NULL | 1 | 2009-03-20 | 2009-04-05 | 39890 | 39906
    2009-11-16 | 2010-04-11 | 2009-03-20 | 2009-04-05 | 1 | 2009-10-26 | 2009-11-21 | 40110 | 40136
    2009-12-23 | 2010-01-11 | 2009-10-26 | 2009-11-21 | 1 | 2009-11-16 | 2010-04-11 | 40131 | 40277
    2010-01-11 | 2010-04-14 | 2009-11-16 | 2010-04-11 | 1 | 2009-12-23 | 2010-01-11 | 40168 | 40187
    2011-03-17 | 2011-07-14 | 2009-12-23 | 2010-01-11 | 1 | 2010-01-11 | 2010-04-14 | 40187 | 40280
    2011-06-12 | 2011-08-14 | 2010-01-11 | 2010-04-14 | 1 | 2011-03-17 | 2011-07-14 | 40617 | 40736
    2011-08-15 | 2011-08-21 | 2011-03-17 | 2011-07-14 | 1 | 2011-06-12 | 2011-08-14 | 40704 | 40767
    NULL | NULL | 2011-06-12 | 2011-08-14 | 1 | 2011-08-15 | 2011-08-21 | 40768 | 40774

  2. widok o korzysta z cteS i oznacza czy coś jest końcem okresu (kolumna k) jak i początkiem (p)
    Początek jest wtedy jeśli jest to pierwszy i data_od nie wchodzi w zakres poprzedniego okresu,
    koniec jest wtedy jesli jest to ostani rekord lub data_do jest mniejsza od następnej daty_od

n_od n_do p_od p_do Artykul data_od data_do data_odInt data_doInt p k
2009-10-26 2009-11-21 NULL NULL 1 2009-03-20 2009-04-05 39890 39906 1 1
2009-11-16 2010-04-11 2009-03-20 2009-04-05 1 2009-10-26 2009-11-21 40110 40136 1 0
2009-12-23 2010-01-11 2009-10-26 2009-11-21 1 2009-11-16 2010-04-11 40131 40277 0 0
2010-01-11 2010-04-14 2009-11-16 2010-04-11 1 2009-12-23 2010-01-11 40168 40187 0 0
2011-03-17 2011-07-14 2009-12-23 2010-01-11 1 2010-01-11 2010-04-14 40187 40280 0 1
2011-06-12 2011-08-14 2010-01-11 2010-04-14 1 2011-03-17 2011-07-14 40617 40736 1 0
2011-08-15 2011-08-21 2011-03-17 2011-07-14 1 2011-06-12 2011-08-14 40704 40767 0 0
NULL NULL 2011-06-12 2011-08-14 1 2011-08-15 2011-08-21 40768 40774 0 1
  1. wynik to pobranie rekordów z o gdzie jest znacznik początku i końca i podmiana dat.

Skrypt do testów:

Kopiuj
--DANE TESTOWE
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[source](
	[Artykul] [int] NULL,
	[data_od] [date] NULL,
	[data_do] [date] NULL,
	[data_odInt] [float] NULL,
	[data_doInt] [float] NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[source] ([Artykul], [data_od], [data_do], [data_odInt], [data_doInt])
VALUES (1, '20090320', '20090405', 39890, 39906)
,(1, '20091026', '20091121', 40110, 40136)
,(1, '20091116', '20100411', 40131, 40277)
,(1, '20091223', '20100111', 40168, 40187)
,(1, '20100111', '20100414', 40187, 40280)
,(1, '20110317', '20110714', 40617, 40736)
,(1, '20110612', '20110814', 40704, 40767)
,(1, '20110815', '20110821', 40768, 40774)
,(2, '20110609', '20110714', 40701, 40736)
,(2, '20110612', '20110814', 40704, 40767)
,(2, '20110815', '20110821', 40768, 40774)
,(2, '20130126', '20130207', 41298, 41310)
,(2, '20130802', '20130803', 41486, 41487)
,(2, '20140225', '20140316', 41693, 41712)
,(2, '20140317', '20140330', 41713, 41726)
,(2, '20141201', '20141204', 41972, 41975)
,(2, '20141226', '20141229', 41997, 42000)
,(2, '20141228', '20141230', 41999, 42001)
,(2, '20150109', '20150113', 42011, 42015)
,(2, '20150531', '20150531', 42153, 42153)
,(2, '20151104', '20151106', 42310, 42312)
,(2, '20170101', '99990909', 42734, 2958350)
,(3, '20110609', '20110714', 40701, 40736)
,(3, '20110612', '20110814', 40704, 40767)
,(3, '20110815', '20110821', 40768, 40774)
,(3, '20160620', '20160809', 42539, 42589)
,(3, '20160620', '20160624', 42539, 42543)
,(3, '20161109', '99990909', 42681, 2958350)

Zapytanie:

Kopiuj
with cteS as (
select 
	lead(data_od,1)  over (partition by artykul order by data_od) n_od,
	lead(data_do,1) over (partition by artykul order by data_od)  n_do,
	lag(data_od,1)  over (partition by artykul order by data_od) p_od,
	lag(data_do,1) over (partition by artykul order by data_od)  p_do,
	* 
from 
	dbo.source s)
, o as (
select
	*
	,case 
		when p_od is null then 1 
		else
			case when dateadd(d,-1,data_od) between p_od and p_do then 0
			else 1
			end
	end p
	,case 
		when n_do is null then 1 
		else
			case 
				when dateadd(d,1,data_do) < n_od then 1
				else 0
			end
	end k
from 
	ctes)

select distinct 
	artykul
	,case 
		when p=1 then data_od 
		else 
			lag(data_od,1) over (partition by artykul order by data_od)  
	end data_od
	,case 
		when k=1 then data_do 
		else 
			lead(data_do,1) over (partition by artykul order by data_od)  
	end data_do
from 
	o
where 
	p+k>=1

Wynik:

artykul data_od data_do
1 2009-03-20 2009-04-05
1 2009-10-26 2010-04-14
1 2011-03-17 2011-08-21
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)