2016-05-25 6 views
2

Ich habe immer dynamische WHERE Anweisungen mit COALESCE() gemacht, um entweder den übergebenen Wert zurückgeben oder alle Datensätze zurückgeben.Wie mache ich eine dynamische WHERE mit COALESCE, die eine OR-Klausel hat

Allerdings kann ich es nicht tun, wenn ich gegen zwei Spalten überprüfen muss, und wenn ein Parameter nicht übergeben wurde dann alle Datensätze zurückgeben. Hier ist Schnipsel meines Code:

DECLARE @MaxAge INT = NULL --- Example value: 5 

SELECT 
    * 
FROM 
    dbo.Vehicle 
WHERE 
DATEPART(YEAR, RegistrationDate) <= 
CASE 
    WHEN @MaxAge IS NOT NULL 
    THEN (DATEPART(YEAR,GETDATE()) - @MaxAge) 
    ELSE DATEPART(YEAR, RegistrationDate) 
END 
OR 
DATEPART(YEAR, ProductionDate) <= 
CASE 
    WHEN @MaxAge IS NOT NULL 
    THEN (DATEPART(YEAR,GETDATE()) - @MaxAge) 
    ELSE DATEPART(YEAR, ProductionDate) 
END 

Was ich versuche (in einfachem Englisch) zu tun ist:

Gib mir alle Zeilen in dem Fahrzeug Tisch, an dem dem Jahr Teil des RegistrationDate ist kleiner als der @MaxAge oder der YEAR-Teil des ProductionDate ist kleiner als der @MaxAge. Wenn @MaxAge NULL ist, dann gib mir die ganze Menge, egal wann es gemacht oder registriert wurde.

Manchmal ist der Wert in der Tabelle NULL, wenn ein Fahrzeug noch nicht registriert wurde. Manchmal kann die NULL sein, wenn der Verkäufer einfach nicht weiß, wann es gemacht wurde.

Was wäre der beste Weg, um dies anzugehen? Ich habe versucht, in SQL-Büchern und online zu suchen, aber kann wirklich nichts finden, um dieser Situation zu entsprechen.

+0

Welche Version von SQL Server wird verwendet? Es gibt derzeit zwei. – Steven

+0

So ist es möglich, dass ein Datensatz NULL registrationDate und NULL ProductionDate hat? Arbeitet Ihr Beispielcode überhaupt? – Aron

+0

@Steven SQL Server 2014 Enterprise –

Antwort

5

du etwas vereinfachen kann zurückzukehren:

SELECT * 
FROM dbo.Vehicle 
WHERE @MaxAge IS NULL -- return everything if null 
OR  DATEDIFF(YEAR, RegistrationDate, GETDATE()) <= @MaxAge 
OR  DATEDIFF(YEAR, ProductionDate, GETDATE()) <= @MaxAge 

Beachten Sie, dass, wenn @MaxAge ist null, die beide der OR ... <= @MaxAge Anweisungen werden false zurückgeben. Sie werden immer noch mit allem von WHERE @MaxAge IS NULL enden.

Ich gehe hier davon aus, dass @MaxAge die älteste Registrierung darstellt, die Ihnen wichtig ist. Mit einem MaxAge von 5 würden Sie alle Ergebnisse an oder nach '01/01/2011' zurückgeben.

Edit:

Per Erike Kommentar beachten, dass DATEDIFF() etwas schrulliges Verhalten hat, die Sie vielleicht nicht erwarten.

Bedenken Sie, dass diese beiden Aussagen äquivalent sind:

SELECT DATEDIFF(YEAR, '01/01/2011', GETDATE()) -- = 5 
SELECT DATEDIFF(YEAR, '12/31/2011', GETDATE()) -- = 5 

Um dies zu umgehen, können Sie versuchen:

SELECT * 
FROM dbo.Vehicle 
WHERE @MaxAge IS NULL -- return everything if null 
OR  DATEADD(YEAR, [email protected], GETDATE()) <= RegistrationDate 
OR  DATEADD(YEAR, [email protected], GETDATE()) <= ProductionDate 

Diese zurück@MaxAge Jahre von heute gehen wird (einschließlich der aktuellen Zeit) und alles zurückgeben, was danach passiert ist. In diesem Fall würde alles, was vor 5 Jahren und 10 Sekunden passiert ist, weggelassen. Um dies zu beheben, können Sie verwenden:

DECLARE @TodayNoTime datetime = DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0) -- 05/25/2016 00:00:00.000 

SELECT * 
FROM dbo.Vehicle 
WHERE @MaxAge IS NULL -- return everything if null 
OR  DATEADD(YEAR, [email protected], @TodayNoTime) <= RegistrationDate -- note the negative sign 
OR  DATEADD(YEAR, [email protected], @TodayNoTime) <= ProductionDate 

Dies wird alles zurück, die heute vor @MaxAge Jahren passiert ist bis zu, unabhängig von der Zeit.

Schließlich funktioniert die Verwendung von COALESCE() in diesem Fall nicht, da Sie nur die eine oder die andere Spalte vergleichen, je nachdem, welche zuerst nicht null ist. Also, wenn RegistrationDate außerhalb Ihrer Parameter fällt, aber ProductionDate fällt in, werden Sie diesen Datensatz verpassen. Als solche habe ich diese Aussage aus der Antwort entfernt.

+0

ich die erste Version bevorzugen - es erlaubt mir auch gegen solche zu vergleichen, wie "DeliveryDate" mehr Spalten hinzufügen beispielsweise. Vielen Dank levelonehuman für diese ausgezeichnete Antwort. –

+0

@volumeone Mein Rat ist, für die Lesbarkeit zu gehen, es sei denn, Sie haben eine definitive Notwendigkeit, es schneller gehen zu lassen. Selbst mit einer Million Zeilen sollte diese spezielle Abfrage wirklich schnell sein - du machst keine 'JOIN's oder ähnliches, die dies verlangsamen könnten. Viel besser (meiner Meinung nach), um es lesbar für Sie oder den nächsten Typ zu machen, wenn die Datei seit Monaten unberührt ist. – levelonehuman

+0

@ErikE Antwort wurde aktualisiert, einschließlich des Bits über 'COALESCE()'. Danke für den Fang! – levelonehuman

2

in einem OR zum WHERE Klausel Hinzufügen schien die richtigen Werte

DECLARE @MaxAge INT = NULL 

SELECT * 
FROM Vehicle 
WHERE @MaxAge IS NULL OR 
    (DATEPART(YEAR, RegistrationDate) <= 
      CASE WHEN @MaxAge IS NOT NULL 
      THEN (DATEPART(YEAR,GETDATE()) - @MaxAge) 
      ELSE DATEPART(YEAR, RegistrationDate) END 
OR 
    DATEPART(YEAR, ProductionDate) <= 
       CASE WHEN @MaxAge IS NOT NULL 
        THEN (DATEPART(YEAR,GETDATE()) - @MaxAge) 
        ELSE DATEPART(YEAR, ProductionDate) END 
    ) 
Verwandte Themen