2013-03-11 45 views
21

Mam pole SQL datetime w bardzo dużej tabeli. Jest indeksowany i musi zostać zapytany.Jaki jest optymalny sposób porównywania dat w serwerze Microsoft SQL?

Problem polega na tym, że SQL zawsze przechowuje komponent czasu (nawet jeśli jest zawsze o północy), ale wyszukiwania dotyczą raczej dnia niż czasu.

declare @dateVar datetime = '2013-03-11; 

select t.[DateColumn] 
from MyTable t 
where t.[DateColumn] = dateVar; 

Nic nie zwróci, ponieważ t.[DateColumn] zawsze zawiera komponent czasu.

Moje pytanie brzmi: jak najlepiej to obejść?

Wydaje się, że dwie główne grupy opcji:

  1. utworzyć drugą zmienną korzystając dateadd i używają between ... and lub >= ... and ... <=.

  2. Konwersja t.[DateColumn] na komponent tylko daty - Myślę, że spowoduje to zignorowanie indeksów.

Oba wydają się bardzo niechlujne - nie chcę porównywać zakresów ani skanować stołu.

Czy istnieje lepszy sposób?

Jeśli jedna z tych opcji jest konsekwentnie optymalna, to w jaki sposób i dlaczego?

Odpowiedz

24

Konwersja na DATE lub użycie otwartego zakresu dat w każdym przypadku przyniesie najlepszą wydajność. FYI, konwersja do daty za pomocą indeksu są najlepsze wyniki. Więcej testuje różne techniki w artykule: What is the most efficient way to trim time from datetime? Wysłany przez Aaron Bertrand

Z tego artykułu:

DECLARE @dateVar datetime = '19700204'; 

-- Quickest when there is an index on t.[DateColumn], 
-- because CONVERT can still use the index. 
SELECT t.[DateColumn] 
FROM MyTable t 
WHERE = CONVERT(DATE, t.[DateColumn]) = CONVERT(DATE, @dateVar); 

-- Quicker when there is no index on t.[DateColumn] 
DECLARE @dateEnd datetime = DATEADD(DAY, 1, @dateVar); 
SELECT t.[DateColumn] 
FROM MyTable t 
WHERE t.[DateColumn] >= @dateVar AND 
     t.[DateColumn] < @dateEnd; 

Również z tego artykułu: using BETWEEN, DATEDIFF lub CONVERT(CHAR(8)... są wolniejsze.

+2

@Keith Dzięki za podanie przykładów .. i dobre pytanie;) –

10

Oto przykład:

mam tabelę zamówienia z polem DateTime zwanego DataZamówienia. Chcę odzyskać wszystkie zamówienia, których data zamówienia jest równa 01.01.2006. Kolejne istnieją sposoby, aby to zrobić:

1) WHERE DateDiff(dd, OrderDate, '01/01/2006') = 0 
2) WHERE Convert(varchar(20), OrderDate, 101) = '01/01/2006' 
3) WHERE Year(OrderDate) = 2006 AND Month(OrderDate) = 1 and Day(OrderDate)=1 
4) WHERE OrderDate LIKE '01/01/2006%' 
5) WHERE OrderDate >= '01/01/2006' AND OrderDate < '01/02/2006' 

znajduje here

+0

wszystkich tych, # 1 jest uważany za najbardziej wydajny: http://www.sqlservercentral.com/Forums/Topic937765-338-1.aspx – RandomUs1r

+1

@ Opcja RandomUs1r # 1 musi uruchomić obliczenie 'datediff' dla każdego wiersza w tabeli, to na pewno wygrało być najszybszym lub najbardziej wydajnym. To samo dotyczy opcji # 2 z konwersją 'varchar (20)'. Opcje # 4 i # 5 są bardziej obiecujące, a ja dodaję jeszcze dwa: 6) 'gdzie OrderDate między @dateVar a dateadd (dzień, 1, @dateVar)' i 7) 'GDZIE przekonwertuj (data, OrderDate) = @ dateVar' – Keith

+3

Z tych 5 sugerowanych metod 1-4 wszystkie są naprawdę powolne. 5 jest szybkie dla sterty stołów, a najszybsza metoda ('convert (date, OrderDate)' see selected answer) nie znajduje się nawet na liście. – Keith

0

można dodać obliczoną kolumnę, która zawiera tylko datę bez czasu. Pomiędzy tymi dwiema opcjami, poleciłbym operatorowi BETWEEN, ponieważ jest dla mnie "czystszy" i powinien lepiej wykorzystywać indeksy. Porównanie planów wykonania wydaje się wskazywać, że numer BETWEEN byłby szybszy; jednak w rzeczywistych testach wykonywali to samo.

+1

Okazuje się, że ["między" jest tutaj złym pomysłem] (http://sqlblog.com/blogs/aaron_bertrand/archive/2011/10/19/what-do-between-and-the-devil-have- in-common.aspx). – Keith

+1

@Keith +1 Dzięki za link. Zdawałem sobie sprawę z większości problemów (wiedziałem, że są one włączone i zwykle dodaję wartość czasu do daty końcowej, kiedy jest taka potrzeba), ale ostatnie były dla mnie nowe. W pracy wszystkie wartości czasu, które wybieramy są obcięte do minuty lub sekundy (nie, to nie była moja decyzja, tak, brak precyzji jest denerwujący.) Więc problemy nano-drugie jeszcze się nie pojawiły , ale są one potencjalnie wystarczająco poważne, prawdopodobnie przyniosę zmianę użycia 'BETWEEN' na' => ... <'do dyskusji z innymi programistami. –

0

Zdobądź produkty, gdy data jest pomiędzy datami od daty i daty.

gdzie konwersji (data, fromdate, 103) < = '26.07.2016' i konwersji (data, todate, 103)> = '26.07.2016'

+2

Witaj, witaj w Stack Overflow. Twoja odpowiedź tak naprawdę niczego tu nie dodaje - jeśli przeczytasz zaakceptowaną odpowiedź, zobaczysz, że już omówiono "konwersję" i wyjaśniono, czy zmiana typu lub użycia jest lepsza w oparciu o indeksy, o które tak naprawdę pytałem dla. Aby zdobyć up-votes, naprawdę musisz dodać coś, czego inni nie mają, lub odpowiedzieć w pierwszej kolejności. – Keith

Powiązane problemy