Czy te dwa zapytania są logicznie równoważne?

10

Czy te dwa zapytania są logicznie równoważne?

DECLARE @DateTime DATETIME = GETDATE()

Zapytanie 1

SELECT *
FROM   MyTable
WHERE  Datediff(DAY, LogInsertTime, @DateTime) > 7   

Zapytanie 2

SELECT *
FROM   MyTable
WHERE  LogInsertTime < @DateTime - 7 

Jeśli nie są logicznie równoważne, czy możesz podać logiczny ekwiwalent pierwszego zapytania, aby klauzula WHERE mogła efektywnie korzystać z indeksu (tj. Wyeliminować zawijanie funkcji)?

Alf47
źródło
Jakiego typu LogInsertTimejest?
dezso
LogInsertTime jest DATETIME
Alf47

Odpowiedzi:

15

Nie ma znaczenia, czy dwa wysłane zapytania są logicznie równoważne; nie powinieneś używać żadnego z nich. Spróbuję oderwać cię od kilku rzeczy:

  1. O ile to możliwe, staraj się unikać stosowania funkcji do kolumn. Zawsze tak dobrze, a przede wszystkim lepiej, zachować te obliczenia względem stałych, a nie kolumn - może to zniszczyć SARGability i sprawić, że indeksy na tych kolumnach będą bezużyteczne. W takim przypadku zdecydowanie wolę zapytanie 2, szczególnie jeśli LogDateTimejest indeksowane (lub może być).
  2. Nie podoba mi się matematyka stenografii i odradzam. Jasne, że pisanie jest szybsze, ale spróbuj tego z DATEtypem danych, a otrzymasz brzydki błąd. Znacznie lepiej to przeliterować, np .:

    WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime);
Aaron Bertrand
źródło
Zgadzam się, moim celem była zmiana zapytania 1 na coś podobnego do zapytania 2, aby umożliwić efektywne wykorzystanie indeksów. Dziękuję za pomoc
Alf47,
8

Użyłbym następującego zapytania sargeable:

SELECT * FROM MyTable WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime)

Powód: uważam, że wynik @ DateTime-7 nie jest udokumentowany. Nawet jeśli akurat jest to odpowiednik DATEADD (DZIEŃ, -7, @DataTime), może się złamać w późniejszym wydaniu.

AK
źródło
Świetnie tego właśnie szukałem, dziękuję
Alf47,
2
Jest to w rzeczywistości, udokumentowane i dobrze zdefiniowany : - (Subtract): Subtracts two numbers (an arithmetic subtraction operator). Can also subtract a number, in days, from a date.. Mimo to zgadzam się, że użycie jawnych funkcji daty sprawia, że ​​wynikowe zapytanie jest bardziej czytelne i łatwiejsze niż „magia operatora arytmetycznego”.
Heinzi
6

Nie są równoważne. Rekordy sprzed 7 dni, ale przed bieżącą porą dnia - zostaną zwrócone tylko w zapytaniu nr 2:

Porównując dni za pomocą DATEADDfunkcji , nie bierze się pod uwagę części czasu . Funkcja zwróci 1 podczas porównywania niedzieli i poniedziałku, bez względu na porę.

Próbny:

DECLARE @MyTable TABLE(pk INT, LogInsertTime DATETIME);

INSERT @MyTable
VALUES (1, DATEADD(HOUR, 1, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE))AS DATETIME))),
(2, DATEADD(HOUR, 23, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE)) AS DATETIME)));

DECLARE @DateTime DATETIME = GETDATE();

SELECT *
FROM @MyTable
WHERE DATEDIFF(DAY, LogInsertTime, @DateTime) > 7;

-- 0 records.

SELECT *
FROM @MyTable
WHERE LogInsertTime < @DateTime - 7;
-- 1 record.

Logicznym odpowiednikiem pierwszego zapytania, które umożliwi potencjalne użycie indeksu, jest usunięcie części czasu @DateTimelub ustawienie czasu na 0:00:00:

SELECT *
FROM @MyTable
WHERE LogInsertTime < CAST(@DateTime - 7 AS DATE);

Powodem, dla którego pierwsze zapytanie nie może użyć indeksu, LogInsertTimejest to, że kolumna jest zakopana w funkcji. Zapytanie nr 2 porównuje kolumnę ze stałą wartością, która umożliwia optymalizatorowi wybór indeksu LogInsertTime.

Scrum Meister
źródło