{"id":579,"date":"2009-05-07T00:00:00","date_gmt":"2009-05-07T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/sql-and-the-snare-of-three-valued-logic\/"},"modified":"2021-08-16T15:02:19","modified_gmt":"2021-08-16T15:02:19","slug":"sql-and-the-snare-of-three-valued-logic","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/sql-and-the-snare-of-three-valued-logic\/","title":{"rendered":"SQL and the Snare of Three-Valued Logic"},"content":{"rendered":"<p>At the end of my article on Relational Division, one of the comments, by a reader named Dean, asked if the sentence &#8220;There ain&#8217;t no planes in this hangar that I can&#8217;t fly!&#8221; is a double negative or a triple? And he asked for an article on Boolean logic next.<\/p>\n<div id=\"pretty\">\n<p>Here it is!\u00a0But the problem is that SQL has a three valued logic (3VL) and not the two values of Mr. George Boole.\u00a0In fact, SQL logic is complicated.<\/p>\n<h2><b>NULLs<\/b><\/h2>\n<p>Like many problems, this starts with NULLs.\u00a0 Dr. Codd put the concept into the first version of the Relational Model.\u00a0 It is not a value; it is a &#8220;place holder&#8221; for a value.\u00a0 The history of the NULL is interesting.\u00a0 When Dr. Codd and Chris Date were business partners, Date was opposed to NULLs and this was a great debate in those days.\u00a0<\/p>\n<p>Later Dr. Codd did a second version of the Relational Model which has two kinds of NULLs.\u00a0 One kind of NULL marks values which are missing because the <i>value<\/i> is unknown, and the other kind marks values that are missing because the <i>attribute<\/i> is missing.\u00a0 For example, my hat size exists (I have a head) but might not be known so it is the first kind of NULL; my hair color does not exist since I am bald as a cue ball, so it is the second kind of NULL.\u00a0 FirstSQL is the only product which implemented Dr. Codd&#8217;s second model.\u00a0<\/p>\n<p>SQL followed Dr. Codd&#8217;s first model and has a single NULL. But much like FORTRAN follows algebra; SQL had to make adjustments from the mathematical model.\u00a0 In the Relational Model, a NULL has no data type.\u00a0 In SQL, the compiler has to know about storage requirements so you can write &#8220;CAST (NULL AS &lt;data type&gt;)&#8221; to pass that information.\u00a0<\/p>\n<p>NULLs have certain basic properties:<\/p>\n<ol>\n<li>\n<p>All SQL data types are NULL-able.\u00a0 This is one reason why IDENTITY is a <i>table<\/i> property and not a data type.\u00a0 The other reason is that a table can have only one IDENTITY column in it.\u00a0 The count of PHYSICAL insertion attempts (NOT\u00a0 successes) is not an attribute; it is audit meta-data and has no place in RDBMS.\u00a0 <\/p>\n<p> This is why the first implementations of BIT which were assembly language bits were not a data type.\u00a0 When BIT became a numeric data type, then things were Kosher.\u00a0 <br \/>\n \u00a0<\/p>\n<\/li>\n<li>\n<p>NULLs propagate.\u00a0 If you use a NULL in an expression, then result is a NULL. \u00a0In numeric expressions, we had questions about priorities, but propagation is vital.\u00a0 In particular:<br \/>\n &#8220;NULL \/ 0&#8221; = NULL or &#8220;division by zero&#8221; error?<\/p>\n<p> &#8220;0 \/ NULL&#8221; = NULL or 0, since this can only resolve to zero for any value.\u00a0 No, I am not going to tell you; test it yourself in Query Analyzer or SSMS!\u00a0 But can you figure it out from principles?\u00a0<\/p>\n<\/li>\n<\/ol>\n<h2><b>TRUE, FALSE and UNKNOWN <\/b><\/h2>\n<p>A comparison between known values gives you a result of TRUE or FALSE.\u00a0 This is Boolean logic. \u00a0If you get a chance look at some of the Syd Harris cartoons about George Boole at <a href=\"http:\/\/www.sciencecartoonsplus.com\/gallery\/computers\/index.php#\">S. Harris Computer Cartoons<\/a>.\u00a0<\/p>\n<p>But that is classic two-valued logic (2VL); all the values are known.\u00a0 When you do a comparison with a NULL, you cannot get a Boolean (i.e. TRUE or FALSE) result.\u00a0<\/p>\n<p>This is where we invented the logical value UNKNOWN.\u00a0 Well, re-discovered it.\u00a0 There were already a lot of multi-valued logics in the mathematical literature.\u00a0 Some of them are based on discrete values and some on continuous values (i.e. fuzzy logic).\u00a0 SQL looks like the system developed by Polish logician Jan \u00c5ukasiewicz (the L-bar is a &#8220;W&#8221; sound in English; you can get the rest of it at <a href=\"http:\/\/en.wikipedia.org\/wiki\/Jan_%C5%81ukasiewicz\">Wikipedia: Jan \u00c5ukasiewicz<\/a>).\u00a0 Programmers know him as the guy who invented Polish Notation and inspired Reverse Polish Notation for HP calculators and stack architecture (aka zero address machines) computers like the old Burroughs machines.\u00a0 SQL has three logical operations, which are found in all programming languages.\u00a0 These are extended to three values: \u00a0<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>OR<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>AND<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>NOT<\/b><\/p>\n<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the \u00c5ukasiewicz multi-valued logic systems, the AND, OR and NOT are almost the same as in SQL&#8217;s three valued logic.\u00a0 The general case is based on the following Polish notation formulas in which 1 is TRUE, 0 is FALSE and fractions are the other values.<\/p>\n<ul class=\"list\">\n<li><code>Cpq = 1\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for p &lt;= q <\/code><\/li>\n<li><code>Cpq = 1 - p + q\u00a0 for (p &gt; q)<\/code><\/li>\n<li><code>Np = 1 - p<\/code><\/li>\n<\/ul>\n<p>N is the negation operator, C is the implication operator.<\/p>\n<p>From this pair, we define all other operators<\/p>\n<ul class=\"list\">\n<li><code>Opq = CCpqq\u00a0\u00a0\u00a0\u00a0 Or<\/code><\/li>\n<li><code>Apq = NONpNq\u00a0\u00a0\u00a0 And<\/code><\/li>\n<li><code>Epq = ACpqCqp\u00a0\u00a0 Equivalence<\/code><\/li>\n<li><code>Mp = CNpp\u00a0\u00a0\u00a0 \u00a0\u00a0 maybe (not FALSE)<\/code><\/li>\n<\/ul>\n<p>Then Tarski and Lukasiewicz had a lot of rules of inference to prove theorems.\u00a0 David McGoveran pointed out that SQL is not really a logic system because it lacks rules for deductions and proofs.\u00a0 The idea is that the UNKNOWN might be resolved to {TRUE, FALSE}, so can we sometimes determine the result regardless of how one value resolves.\u00a0 If we cannot determine a result, then return UNKNOWN.\u00a0<\/p>\n<p>Let me sum up.\u00a0 SQL is <i>not <\/i>a formal logical system.\u00a0 We have no inference rules and what we informally call &#8220;predicates&#8221; are actually &#8220;search conditions&#8221; in the ANSI\/ISO Standards.\u00a0 What we have is a collection of look-up tables to compute a value of {TRUE, FALSE, UNKNOWN}.\u00a0<\/p>\n<p>This is a subtle difference for anyone who is not a math major, but an inference system is like those axioms, postulates and theorems you had in High School geometry.\u00a0 You need an implication operation.\u00a0 In regular 2VL, this is shown as a double tailed right arrow &#8221; =&gt;&#8221; and it is defined by the look-up table in 2VL, where the column is the first operand:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>IMPLIES<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Or informally, a TRUE premise cannot imply a FALSE conclusion.\u00a0 A FALSE premise can imply anything.\u00a0 This 2VL model can also be written as:<\/p>\n<ul class=\"list\">\n<li>\n<p><code>(A IMPLIES B) = NOT (A AND NOT B)<\/code><\/p>\n<\/li>\n<\/ul>\n<p>But if you apply De Morgan&#8217;s Law, it can also be written as<\/p>\n<ul class=\"list\">\n<li>\n<p><code>(A IMPLIES B) = (NOT(A) OR B)<\/code><\/p>\n<\/li>\n<\/ul>\n<p>Now expand these 2VL rules into 3VL rules.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>IMPLIES-1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>UNKNOWN<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>IMPLIES-2 <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>FALSE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TRUE<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>TRUE<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<td valign=\"top\">\n<p>UNKNOWN<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Oops!\u00a0 They are not the same.\u00a0 The premise &#8220;X IMPLIES {FALSE, UNKNOWN}&#8221; could argue that a FALSE premise X can imply anything, TRUE, FALSE or UNKNOWN.\u00a0 Or that propagation of unknown values applies, so a FALSE premise can lead to a TRUE conclusion. Make a decision and design a logical system for yourself.\u00a0<\/p>\n<p>We avoided it in the SQL Standards. We did not do implication and the other rules.\u00a0 This is a problem for those of us doing an optimizer for SQL.\u00a0<\/p>\n<h1><b>BIT Flags versus Predicates <\/b><\/h1>\n<p><b><\/b>One of the major problems for those people learning SQL is that they have not un-learned a mindset based originally on punch cards and magnetic tapes instead of RDBMS.\u00a0 Even if they never saw a punch card or a magnetic tape, they think of data processing as a sequence of procedural steps that depend on sequentially ordered data.\u00a0 At each step the data is moved to another file, scratch tape or temporary table.<\/p>\n<p>Did you ever think about the people who use a singular table name instead of a collective or plural name?\u00a0 In a file system, you process one record at time.\u00a0 Calling the current record &#8220;Employee&#8221; makes sense.\u00a0 But in RDBMS, we work with the <i>entire table<\/i> (set) all at once.\u00a0 Therefore we call that table &#8220;Personnel&#8221; instead.\u00a0<\/p>\n<p>We used bit flags in the Dark Ages because we lacked storage and computing power.\u00a0 We would discover a fact that was TRUE at one point in time and then set a flag to mark that state within the system.\u00a0 A classic example is a deletion flag at the start of a tape file record.\u00a0 When the system read it, it would skip forward on the tape to the next record.\u00a0 Eventually, the deleted records would be removed when the data was copied over to a new tape.\u00a0<\/p>\n<p>An embarrassing story of my own was writing a small restaurant scheduling program in BASIC on an early Personal Computer.\u00a0 I used a flag to show if the staff member was at least 18 years old which was the legal age to consume and serve alcoholic beverages at the time.\u00a0 The legal age then changed to 21 and my program cheerfully scheduled an entire shift of now under-aged servers on the night that the state Alcoholic Beverage Control agency decides to check on the new law.\u00a0 My client was not happy.\u00a0<\/p>\n<p>If I had SQL back then, I would have used the employee birthdates to set up the shifts.\u00a0 A simple two keystrokes in a VIEW declaration would have done it.\u00a0<\/p>\n<p>SQL is a &#8220;predicate language&#8221; by its nature in the same way that FORTRAN is an &#8220;algebra language&#8221; by its nature.\u00a0 Because the data is shared, and in constant motion, you cannot ever trust that the flags will actually show the current state of the data.\u00a0 You have to test it when you do something. Silly bit-flags work in file systems because nobody else can physically read the tape as it goes thru its sequential process.\u00a0<\/p>\n<h1><b>DDL versus DML <\/b><\/h1>\n<p>As if all of this was not weird enough, SQL&#8217;s 3VL behaves differently in the DDL (Data Declaration Language) and the DML (Data Manipulation Language).\u00a0<\/p>\n<p>When you declare a column in a table in the DDL, you have the options of making in NOT NULL, giving it a DEFAULT value, using it in a CHECK() constraint and\/or adding DRI actions.\u00a0 Those options are one of the <i>many <\/i>reasons that a column in RDBMS is different from a field in a file system.\u00a0<\/p>\n<p>When I use a CHECK() constraint in the DDL and some or all of the columns are NULL-able, the search condition can return UNKNOWN.\u00a0 This is fine because DDL accepts {TRUE, UNKNOWN} as the same and will permit the data to persist.\u00a0<\/p>\n<p>We did that because adding the extra search conditions to test for NULLs would be a screaming nightmare.\u00a0 Try it.\u00a0<\/p>\n<p>In DML, we are not so forgiving.\u00a0 DML sees {FALSE, UNKNOWN} as the same and will reject the data, keeping only that which tests TRUE.\u00a0<\/p>\n<p>This can be weird.\u00a0 You put something into a table and you need to work extra hard to get it back out because of NULLs and 3VL.\u00a0 That is why we SQL pros keep telling you to not make a column NULL-able unless it makes sense in the data model.\u00a0<\/p>\n<h1><b>Shorthand<\/b><\/h1>\n<p>SQL is a higher level language so it has shorthands in it.\u00a0 But they can all be expanded out into combinations of {AND, OR, NOT}.\u00a0 Beginners in SQL often do not know how to use them and so tend to stick with the familiar {AND, OR, NOT} constructs they know from earlier languages like FORTRAN or BASIC. This is a bad way to write code.\u00a0 It hides the higher level logic and in better SQL engines, the shorthands are optimized.\u00a0\u00a0<\/p>\n<h3><b>BETWEEN<\/b><\/h3>\n<div class=\"indent\">\n<p>This search condition is equivalent to two comparisons:<\/p>\n<p><code>(x BETWEEN a AND b) = ((a &lt; = x) AND (x &lt; = b))<\/code><\/p>\n<p>The early optimizers simply expanded it out this way.\u00a0 More sophisticated optimizers look at it as a 3-ary logical operator and optimize for the &#8220;between-ness&#8221; that seems to be expected<\/p>\n<\/div>\n<h3>IN()<\/h3>\n<div class=\"indent\">\n<p><b><\/b>The IN()n search condition has two forms.\u00a0 \u00a0\u00a0<\/p>\n<p><code>&lt;expression&gt; [NOT] IN (&lt;expression list&gt;)<\/code><code><br \/>\n&lt;expression&gt; [NOT] IN (&lt;single column select stmt&gt;)<\/code><\/p>\n<p>This is a bit tricky.\u00a0 With either version we get a single column of expressions and compare the left hand expression to each of those values with an OR.\u00a0 In effect,<\/p>\n<p><code>x IN (1, 2, 3) <\/code><\/p>\n<p>&#8230;is shorthand for &#8230;<\/p>\n<p><code>((x = 1) OR (x = 2) OR (x = 3))<\/code><\/p>\n<p>Likewise,<\/p>\n<p><code>x NOT IN (1, 2, 3) <\/code><\/p>\n<p>&#8230; is shorthand for &#8230;<\/p>\n<p><code>NOT ((x = 1) OR (x = 2) OR (x = 3))<\/code><\/p>\n<p>Which can be re-written with DeMorgan&#8217;s law to:<\/p>\n<p><code>((x &lt;&gt; 1) AND (x &lt;&gt; 2) AND (x &lt;&gt; 3))<\/code><\/p>\n<p>Now try to follow the same rule with<\/p>\n<p><code>x IN (1, 2, NULL) <\/code><\/p>\n<p>&#8230;is shorthand for&#8230;<\/p>\n<p><code>((x = 1) OR (x = 2) OR (x = NULL)) <br \/>\n((x = 1) OR (x = 2) OR UNKNOWN)\u00a0 -- propagate NULLs<\/code><\/p>\n<p>Let&#8217;s assume that one of the ORs is TRUE. So the other is FALSE<\/p>\n<p><code>(TRUE OR FALSE OR UNKNOWN)\u00a0 <br \/>\n(TRUE OR UNKNOWN)\u00a0<br \/>\n(TRUE)\u00a0 <\/code><\/p>\n<p>The only way this can return FALSE would be for x to be something other than {1, 2}.\u00a0 The only way for it to return UNKNOWN would be for x to be NULL.\u00a0<\/p>\n<p>Repeat the exercise with<\/p>\n<p><code>x NOT IN (1, 2, NULL) <\/code><\/p>\n<p>&#8230;is shorthand for&#8230;<\/p>\n<p><code>NOT ((x = 1) OR (x = 2) OR (x = NULL)) <br \/>\nNOT ((x = 1) OR (x = 2) OR UNKNOWN) -- propagate NULLs<br \/>\n((x &lt;&gt; 1) AND (x &lt;&gt; 2) AND NOT UNKNOWN) -- DeMorgan<br \/>\n(UNKNOWN) -- definition of AND<\/code><\/p>\n<p>So if this was used in DDL, rows would be allowed in the table, but will always be rejected by the DML.<\/p>\n<\/div>\n<h3><b>SOME|ANY()<\/b><\/h3>\n<div class=\"indent\">\n<p>There is an underused generalization of the IN() of the form:<\/p>\n<p><code>&lt;expression&gt; &lt;theta op&gt; [SOME | ANY] (&lt;single column select stmt&gt;)<\/code><\/p>\n<p>This is shorthand for a chain of ORs:<\/p>\n<p><code>(&lt;expression&gt; &lt;theta op&gt; &lt;expression_1&gt; <br \/>\n\u00a0 OR &lt;expression&gt; &lt;theta op&gt; &lt;expression_2&gt;<br \/>\n\u00a0 OR...<br \/>\n&lt;expression&gt; &lt;theta op&gt; &lt;expression_n&gt;)<\/code><\/p>\n<p>There is no difference between SOME and ANY, but sometimes the search condition reads a little better to a human.\u00a0<\/p>\n<p>The IN() search condition is the case where the &lt;theta op&gt; is equality.\u00a0 All of the 3VL problems you had with IN() are also here.<\/p>\n<\/div>\n<h3>ALL<\/h3>\n<div class=\"indent\">\n<p>There is really, really underused (almost unknown) search condition that generalized a chain of ANDs:<\/p>\n<p><code>&lt;expression&gt; &lt;theta op&gt; ALL (&lt;single column select stmt&gt;)<\/code><\/p>\n<p>This is shorthand for a chain of ANDs:<\/p>\n<p><code>(&lt;expression&gt; &lt;theta op&gt; &lt;expression_1&gt; <br \/>\nAND &lt;expression&gt; &lt;theta op&gt; &lt;expression_2&gt;<br \/>\nAND...<br \/>\n&lt;expression&gt; &lt;theta op&gt; &lt;expression_n&gt;)<\/code><\/p>\n<p>The ALL search condition does not play well with the extrema functions (e.g. MAX, MIN).\u00a0<\/p>\n<p>It is counter-intuitive at first that these two search condition are not the same in SQL:<\/p>\n<p><code>x &gt;= (SELECT MAX(y) FROM Foobar)<br \/>\nx &gt;= ALL (SELECT y FROM Foobar)<\/code><\/p>\n<p>But you have to remember the rules for the extrema functions &#8212; they drop out all the NULLs before returning the greater, greatest,\u00a0or least values.\u00a0 The (&lt;single column select stmt&gt;) does not drop NULLs, so you can get them in the results.\u00a0 This can leave a NULL to give us an UNKNOWN.\u00a0<\/p>\n<\/div>\n<h3>EXISTS()<\/h3>\n<div class=\"indent\">\n<p>The EXISTS() search condition is on of the very few Boolean operator we have in SQL.\u00a0 It returns {TRUE, FALSE} and never UNKNOWN. This is because it works at the table level and not at the column level.\u00a0 Compare this to COUNT(*) which returns the <i>cardinality<\/i> of the whole table as opposed to COUNT(<i>&lt;expression&gt;<\/i>).\u00a0 They look alike but are totally different.\u00a0<\/p>\n<\/div>\n<h1><b>Summary <\/b><\/h1>\n<p><b><\/b>I tell people that they need to learn to think in sets to get good with SQL.\u00a0 Give up\u00a0your sequential, procedural record-at-a-time mindset and trade up to declarative set processing!\u00a0 See the light!\u00a0 Leave the darkness!\u00a0<\/p>\n<p>I probably ought to spend more time on 3VL since it is also something they never saw before.\u00a0 And it is not easy.\u00a0<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The whole subject of the Three-Valued (also known as ternary, trivalent or 3VL) Logic of SQL tends to trip people up. This is hardly surprising in view of the fact that it involves an esoteric Polish mathematician and because it behaves differently in the DDL (Data Declaration Language) and the DML (Data Manipulation Language). In response to requests, Joe Celko comes to the rescue and makes it all seem simple.&hellip;<\/p>\n","protected":false},"author":96214,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143525],"tags":[4149,4150,4941,4942],"coauthors":[6781],"class_list":["post-579","post","type-post","status-publish","format-standard","hentry","category-learn","tag-learn-sql-server","tag-sql","tag-sql-and-exists","tag-three-value-logic"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/96214"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=579"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/579\/revisions"}],"predecessor-version":[{"id":72991,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/579\/revisions\/72991"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=579"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}