sql >> Databasteknik >  >> RDS >> Oracle

INTE I urval med NULL-värden

Först lite teori:Null (SQL)

De viktigaste delarna för oss från länken ovan:

Jämförelser med NULL och logiken med tre värden (3VL)

Eftersom Null inte är medlem i någon datadomän anses det inte vara ett "värde", utan snarare en markör (eller platshållare) som indikerar frånvaron av värde. På grund av detta kan jämförelser med Null aldrig leda till varken Sant eller Falskt, utan alltid i ett tredje logiskt resultat, Okänt.[8] Det logiska resultatet av uttrycket nedan, som jämför värdet 10 med Null, är okänt:

SELECT 10 = NULL       -- Results in Unknown

så att båda jämförelserna:x = NULL och x <> NULL utvärderas till NULL(okänd).

SQL implementerar tre logiska resultat, så SQL-implementationer måste tillhandahålla en specialiserad logik med tre värden (3VL). Reglerna för SQL-logik med tre värden visas i tabellerna nedan (p ochq representerar logiska tillstånd)"[9] Sanningstabellerna SQL använder för OCH, ELLER och INTE motsvarar ett vanligt fragment av Kleene- och Łukasiewicz-logiken med tre värden ( som skiljer sig i sin definition av implikation, men SQL definierar ingen sådan operation).

+---------+-------------+-------------+-------------+-----------+--------+
|    p    |        q    |     p OR q  |     p AND q |    p = q  |p != q  |
+---------+-------------+-------------+-------------+-----------+--------+
| True    |     True    |     True    |     True    |   True    | False  |
| True    |     False   |     True    |     False   |   False   | True   |
| True    |     Unknown |     True    |     Unknown |   Unknown | Unknown|
| False   |     True    |     True    |     False   |   False   | True   |
| False   |     False   |     False   |     False   |   True    | False  |
| False   |     Unknown |     Unknown |     False   |   Unknown | Unknown|
| Unknown |     True    |     True    |     Unknown |   Unknown | Unknown|
| Unknown |     False   |     Unknown |     False   |   Unknown | Unknown|
| Unknown |     Unknown |     Unknown |     Unknown |   Unknown | Unknown|
+---------+-------------+-------------+-------------+-----------+--------+

Effekt av okänt i WHERE-satser

SQL-logik med tre värden påträffas i Data Manipulation Language (DML) i jämförelse av predikat av DML-satser och frågor. WHERE-satsen gör att DML-satsen endast agerar på de rader för vilka predikatet utvärderas till True.

Så kort sagt:WHERE-satsen behandlar NULL som FALSE

Tänk nu på ett enklare fall:

SELECT * FROM T1;

|      X |
|--------|
|      1 |
| (null) |

och en fråga:

SELECT * FROM t1 WHERE x IN (1, NULL);

Ovanstående fråga är ett kortland till denna:

SELECT * FROM t1 
WHERE x = 1
  OR  x = NULL

För den andra raden från tabellen t ( x =NULL) det här villkoret ser ut så här:

WHERE NULL = 1
   OR NULL = NULL

så detta villkor för raden x=NULL utvärderas till NULL eftersom NULL=1 är NULL, NULL=NULL är NULL och NULL OR NULL är också NULL (se tabell 3VL ovan).

Tänk nu på mer märkliga fall:

SELECT * FROM t1 WHERE x NOT IN (1, NULL);

Denna sats x NOT IN (1, NULL) motsvarar NOT ( x IN (1, NULL) )
så det motsvarar också:

NOT (
  x = 1
  OR
  x = NULL
)

och enligt De Morgans lagar motsvarar det:

NOT ( x = 1 ) AND NOT ( x = NULL )

och (om vi ersätter NOT x = y med x <> y ) det motsvarar också:

 x <> 1 AND x <> NULL

Titta noga på det sista villkoret:

WHERE 
x <> 1 AND x <> NULL

Vi vet än x <> NULL utvärderas alltid till NULL. Vi vet också från 3VL-tabellen ovan att både true AND NULL är NULL och false AND NULL evalueras till FALSE, så hela villkoret utvärderas alltid till FALSE eller NULL, men det evalueras aldrig till TRUE.

Därför en fråga med detta villkor:

SELECT .....
WHERE x NOT IN ( NULL, whatever)

returerar alltid tomma resultat

Och nu din fråga, som också är nyfiken:

SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);

som kan skrivas om (med konstanta värden) till:

SELECT * FROM t1
WHERE (id, val) NOT IN (
       (1, null),
       (2, 2 )
)

Den här frågan använder så kallat radvärdeuttryck

I grund och botten ett villkor som använder radvärdet uttryck i så här

(a, b) = (x, y)

motsvarar denna:

a = x AND b = y

så ovanstående fråga kan skrivas om till denna:

SELECT * FROM t1
WHERE NOT (
   id = 1 AND val = NULL
   OR
   id = 2 AND val = 2
)

Enligt De Morgans lagar är detta identiskt med:

SELECT * FROM t1
WHERE 
   NOT ( id = 1 AND val = NULL )
   AND
   NOT ( id = 2 AND val = 2 )

och vidare till:

SELECT * FROM t1
WHERE 
   ( id <> 1 OR val <> NULL )
   AND
   ( id <> 2 OR val <> 2 )

Sedan den första delen ( id <> 1 OR val <> NULL ) av villkoret utvärderas till sant endast i ett fall där id <> 1 (se 3VL-tabellen ovan), detta villkor kan förenklas till:

SELECT * FROM t1
WHERE 
   ( id <> 1 )
   AND
   ( id <> 2 OR val <> 2 )

och vidare (enligt De Morgans lagar) till:

SELECT * FROM t1
WHERE 
   id <> 1 AND id <> 2
   OR
   id <> 1 AND  val <> 2

så varken (1,1) inte heller (2,2) från källkoden data1 följa dessa villkor.



  1. Hur anropar jag en lagrad procedur med argument med sqlcmd.exe?

  2. Postgresql Drop View

  3. Mest effektiva T-SQL sättet att vaddera en varchar till vänster till en viss längd?

  4. SQL Server:Filterutgång för sp_who2