web-dev-qa-db-de.com

Bedingte WHERE-Klausel in SQL Server

Ich erstelle eine SQL-Abfrage, in der ich eine bedingte where-Klausel benötige. 

Es sollte ungefähr so ​​sein:

SELECT 
    DateAppr,
    TimeAppr,
    TAT,
    LaserLTR,
    Permit,
    LtrPrinter,
    JobName,
    JobNumber,
    JobDesc,
    ActQty,
    (ActQty-LtrPrinted) AS L,
    (ActQty-QtyInserted) AS M,
    ((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM 
    [test].[dbo].[MM]
WHERE 
    DateDropped = 0
            --This is where i need the conditional clause 
    AND CASE
            WHEN @JobsOnHold = 1 THEN DateAppr >=  0
            ELSE  DateAppr != 0
        END

Die obige Abfrage funktioniert nicht. Ist das nicht die richtige Syntax oder gibt es eine andere Möglichkeit, die ich nicht kenne? 

Ich möchte kein dynamisches SQL verwenden. Gibt es einen anderen Weg oder muss ich eine Problemumgehung verwenden, z. B. if else und dieselbe Abfrage mit verschiedenen where-Klauseln verwenden?

49
user2721874

Versuche dies 

SELECT 
    DateAppr,
    TimeAppr,
    TAT,
    LaserLTR,
    Permit,
    LtrPrinter,
    JobName,
    JobNumber,
    JobDesc,
    ActQty,
    (ActQty-LtrPrinted) AS L,
    (ActQty-QtyInserted) AS M,
    ((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM 
    [test].[dbo].[MM]
WHERE 
    DateDropped = 0
    AND (
    (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
    OR 
    (ISNULL(@JobsOnHold, 0) != 1 AND DateAppr != 0)
    )

Sie können hier mehr über bedingte WO lesen.

65
Coder of Code

Probier diese -

WHERE DateDropped = 0
    AND (
        (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
        OR 
        (ISNULL(@JobsOnHold, 0) != 1 AND DateAppr != 0)
    )
23
Devart

So beantworten Sie die zugrunde liegende Frage, wie Sie einen CASE-Ausdruck in der WHERE-Klausel verwenden: 

Denken Sie zunächst daran, dass der Wert eines CASE-Ausdrucks einen normalen Datentypwert haben muss, nicht einen booleschen Wert. Es muss ein Varchar oder ein Int oder etwas sein. Aus demselben Grund können Sie SELECT Name, 76 = Age FROM [...] nicht sagen und erwarten, 'Frank', FALSE in der Ergebnismenge zu erhalten.

Darüber hinaus müssen alle Ausdrücke in einer WHERE-Klausel einen booleschen Wert haben. Sie können nicht haben einen Wert von Varchar oder Int. Sie können nicht WHERE Name; oder WHERE 'Frank'; sagen. Sie müssen einen Vergleichsoperator verwenden, um einen booleschen Ausdruck zu erstellen. WHERE Name = 'Frank';

Das bedeutet, dass sich der CASE-Ausdruck auf einer Seite eines booleschen Ausdrucks befinden muss. Sie müssen den CASE-Ausdruck mit etwas vergleichen. Es kann nicht alleine stehen!

Hier:

WHERE 
    DateDropped = 0
    AND CASE
            WHEN @JobsOnHold  = 1 AND DateAppr >= 0 THEN 'True'
            WHEN DateAppr != 0 THEN 'True'
            ELSE 'False'
        END = 'True'

Beachten Sie, wie am Ende der CASE-Ausdruck auf der linken Seite den booleschen Ausdruck in 'True' = 'True' oder 'False' = 'True' umwandelt.

Beachten Sie, dass 'False' und 'True' nichts Besonderes sind. Sie können auch 0 und 1 verwenden, wenn Sie dies auch möchten.

Normalerweise können Sie den CASE-Ausdruck in boolesche Ausdrücke umschreiben, mit denen wir uns besser auskennen. Manchmal ist es jedoch einfacher oder wartbarer, einen vorhandenen Ausdruck zu verwenden, als die Logik zu konvertieren.

9
Bacon Bits

Das Problem Ihrer Abfrage ist, dass in CASE-Ausdrücken die THEN- und ELSE-Teile einen Ausdruck haben müssen, der eine Zahl oder einen Varchar oder einen anderen Datentyp auswertet, jedoch nicht einen booleschen Wert.

Sie müssen nur die boolesche Logik (oder besser die von SQL verwendete ternäre Logik) verwenden und diese neu schreiben:

WHERE 
    DateDropped = 0
AND ( @JobsOnHold = 1 AND DateAppr >= 0 
   OR (@JobsOnHold <> 1 OR @JobsOnHold IS NULL) AND DateAppr <> 0
    )
7
ypercubeᵀᴹ

Wenn Sie bedingte WHERE-Klauseln verwenden, enden Sie häufig mit einer äußerst ineffizienten Abfrage, die bei großen Datensätzen, in denen Indizes verwendet werden, bemerkbar macht. Optimieren der Abfrage für verschiedene Werte Ihres Parameters ist es, für jeden Wert des Parameters einen anderen Ausführungsplan zu erstellen. Dies erreichen Sie mit OPTION (RECOMPILE).

In diesem Beispiel würde es wahrscheinlich keinen großen Unterschied machen, aber sagen, die Bedingung sollte nur in einem von zwei Fällen verwendet werden, dann könnten Sie einen großen Einfluss feststellen.

In diesem Beispiel:

WHERE 
    DateDropped = 0
    AND (
    (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
    OR 
    (ISNULL(@JobsOnHold, 0) <> 1 AND DateAppr <> 0)
    )
OPTION (RECOMPILE)

Source Parameter Sniffing, Embedding und die RECOMPILE-Optionen

0
wezzix