2014-09-16 12 views
5

Próbuję napisać oświadczenie przypadku w klauzuli where dla kwerendy, nad którym pracuję. Importuję kod w raportach Crystal i zasadniczo próbuję stwierdzić, czy zmienna "type" jest ustawiona na "create" dla tego zakresu dat w klauzuli where, gdzie uruchamiany jest inny zakres dat. Ciągle daje mi błąd. Nie mogę zidentyfikować, co jest nie tak z moją składnią tutaj. Pomóc komuś?CASE oświadczenie gdzie klauzula w tsql zapytanie

DECLARE @Date1 DATETIME 
DECLARE @Date2 DATETIME 
DECLARE @type VARCHAR(20) 
SET @Date1 = '2010-1-1' 
SET @Date2 = '2010-2-1' 
SET @type = '{?DateType}' 

select * 
from filled 
WHERE 
    (CASE WHEN @type = 'create' THEN 
    filled.CREATEDON >= @Date1 
    AND filled.CREATEDON < DATEADD(d, +1, @Date2) 
    WHEN @type <> 'create' THEN 
    filled.datefilled >= @Date1 
    AND filled.datefilled < DATEADD(d, +1, @Date2) 
    END) 
+0

Jakiego błędu dostałeś? I dlaczego masz 2 kontrole dla '@ type = 'create''? – Andrew

+0

Ale dlaczego jest zaznaczone w raporcie Crystal, gdy jest to wszystko SQL – aMazing

+0

Ponieważ importuję zapytanie do kryształu. – Barzul

Odpowiedz

12

Nie potrzebujemy case oświadczenie

WHERE ((@type = 'create' and filled.CREATEDON >= @Date1 AND filled.CREATEDON < DATEADD(d, +1, @Date2)) or 
     (@type <> 'create' and filled.datefilled >= @Date1 AND filled.datefilled < DATEADD(d, +1, @Date2)) 
    ) 

Pozostawia to non-sensical logikę masz w rachunku case. Oba warunki mają tę samą wartość dla @type. Zakładam, że to literówka.

+0

Tak, to był błąd literowy <>. Dzięki. – Barzul

7

Korzystanie wyrażenie przypadku w którym klauzula jest możliwe, ale ogólnie można uniknąć, a zapisane w AND/OR, w twoim przypadku to będzie:

WHERE( @Type = 'create' 
    AND filled.CREATEDON >= @Date1 
    AND filled.CREATEDON < DATEADD(d, +1, @Date2) 
    ) 
OR ( @Type != 'create' 
    AND filled.datefilled >= @Date1 
    AND filled.datefilled < DATEADD(d, +1, @Date2) 
    ) 

jednak zapytania jak to zwykle produkują suboptimal plany. Należy użyć IF/ELSE logiki, jeśli to możliwe:

IF @Type = 'create' 
BEGIN 
    SELECT * 
    FROM Filled 
    WHERE Filled.CreatedOn >= @Date1 
    AND  Filled.CreatedOn < DATEADD(DAY, 1, @Date2) 
END 
ELSE 
BEGIN 
    SELECT * 
    FROM Filled 
    WHERE Filled.DateFilled >= @Date1 
    AND  Filled.DateFilled < DATEADD(DAY, 1, @Date2) 
END 

Powodem tego jest wartością @type nie jest znany w czasie kompilacji, więc optymalizator nie wie, czy będzie trzeba szukać na DateFilled lub CreatedOn, dlatego nie można zaplanować użycia indeksu w żadnej z kolumn (jeśli taka istnieje), więc wykona skanowanie tabeli niezależnie od dostępnych indeksów. Natomiast jeśli oddzielisz logikę od IF/ELSE, nie ma znaczenia, jaka jest wartość @type, plan jest tworzony dla każdego oddziału IF, aw każdym oddziale optymalizator wie, która kolumna będzie wyszukiwany i może planować użycie odpowiedni indeks.

Można również użyć UNION ALL:

SELECT * 
FROM Filled 
WHERE Filled.CreatedOn >= @Date1 
AND  Filled.CreatedOn < DATEADD(DAY, 1, @Date2) 
AND  @Type = 'create' 
UNION ALL 
SELECT * 
FROM Filled 
WHERE Filled.DateFilled >= @Date1 
AND  Filled.DateFilled < DATEADD(DAY, 1, @Date2) 
AND  @Type <> 'create'; 

Ponownie, jeśli indeksy istnieje na DateFilled lub CreatedOn ten jest o wiele bardziej prawdopodobne, aby stworzyć plan, który wykorzystuje je niż przy użyciu OR.