Beispiel
Ich habe einen Tisch
ID myField
------------
1 someValue
2 NULL
3 someOtherValue
und ein boolescher T-SQL-Ausdruck, der als TRUE, FALSE oder (aufgrund der ternären Logik von SQL) UNBEKANNT ausgewertet werden kann:
SELECT * FROM myTable WHERE myField = 'someValue'
-- yields record 1
Wenn ich alle anderen Datensätze erhalten möchte , kann ich den Ausdruck nicht einfach negieren
SELECT * FROM myTable WHERE NOT (myField = 'someValue')
-- yields only record 3
Ich weiß, warum dies geschieht (ternäre Logik), und ich weiß, wie ich dieses spezielle Problem lösen kann.
Ich weiß, dass ich nur verwenden kann myField = 'someValue' AND NOT myField IS NULL
und ich bekomme einen "invertierbaren" Ausdruck, der niemals UNBEKANNT ergibt:
SELECT * FROM myTable WHERE NOT (myField = 'someValue' AND myField IS NOT NULL)
-- yields records 2 and 3, hooray!
Allgemeiner Fall
Lassen Sie uns nun über den allgemeinen Fall sprechen. Lassen Sie uns sagen , anstatt myField = 'someValue'
ich etwas komplexer Ausdruck haben viele Felder und Bedingungen , die, vielleicht Subqueries:
SELECT * FROM myTable WHERE ...some complex Boolean expression...
Gibt es eine generische Möglichkeit, diese Expedition "umzukehren"? Bonuspunkte, wenn es für Unterausdrücke funktioniert:
SELECT * FROM myTable
WHERE ...some expression which stays...
AND ...some expression which I might want to invert...
Ich muss SQL Server 2008-2014 unterstützen, aber wenn es eine elegante Lösung gibt, die eine neuere Version als 2008 erfordert, bin ich auch daran interessiert, davon zu hören.
quelle
Der erste Gedanke, der mir einfällt:
Kehrt zurück:
Kehrt zurück:
Dies hängt davon ab, wie
EXISTS
immer wahr oder falsch zurückgegeben wird , niemals unbekannt . Die Notwendigkeit für dieSELECT 1 WHERE
ist leider notwendig, aber es könnte für Ihre Anforderung praktikabel sein, zum Beispiel:Siehe EXISTS (Transact-SQL)
Ein etwas komplexeres Beispiel zeigt, wie entweder
EXISTS
oderCASE/IIF
Methoden angewendet werden können, um einzelne Prädikate zu invertieren:Code:
quelle
Wenn es Ihnen nichts ausmacht, die Unterausdrücke im Voraus neu zu schreiben, können Sie Folgendes verwenden
COALESCE
:Sie müssen sicherstellen, dass
'notSomeValue'
sich das von unterscheidet'someValue'
; vorzugsweise wäre es ein völlig illegaler Wert für die Spalte. (Das kannNULL
natürlich auch nicht sein .) Dies ist leicht zu negieren, selbst wenn Sie eine lange Liste haben:Sauberer, einfacher und offensichtlicher als
CASE
oderIIF
, meiner Meinung nach. Der Hauptnachteil ist, dass ein zweiter Wert, von dem Sie wissen, dass er nicht gleich ist, aber dies ist nur dann wirklich ein Problem, wenn Sie den tatsächlichen Wert im Voraus nicht kennen. In diesem Fall können Sie tun, was Hanno Binder vorschlägt und verwendetCOALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'
(wo'someValue'
tatsächlich parametrisiert werden würde).COALESCE
Es ist dokumentiert, dass es ab SQL Server 2005 verfügbar ist.Beachten Sie, dass das Durcheinander mit Ihrer Abfrage (unter Verwendung einer der hier empfohlenen Methoden) es für die Datenbank schwieriger machen kann, Ihre Abfrage zu optimieren. Bei großen Datenmengen ist die
IS NULL
Version wahrscheinlich einfacher zu optimieren.quelle
COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'
sollte für jeden "someValue" und alle Daten in der Tabelle funktionieren.Es gibt den integrierten EXCEPT- Set-Operator, der die Ergebnisse einer zweiten Abfrage effektiv aus den Ergebnissen der ersten Abfrage entfernt.
quelle
Ist COALESCE verfügbar?
quelle
sql-server
, nichtmysql
oderpostgresql
.BOOLEAN
Typ hat und MySQL einen (gefälschten)BOOLEAN
Typ, der Parameter derCOALESCE()
Funktion sein kann. Wenn die Frage mitsql-agnostic
oder markiertsql-standard
wäre, wäre die Antwort in Ordnung.