8
votes

I understand that SQL uses three valued logic but I am having trouble understanding how to use this in practice, especially why TRUE || NULL = True and FALSE && NULL = False instead of evaluating to null.

Here are the three valued truth tables that apply to SQL Server:

Tri truth tables

I found a couple explanations of three valued logic online but I cannot find any real code examples of this in use. Can someone show me a code example using three valued logic to help me understand this a little better?

5
There's one in the article linked too showing true and unknown evaluating to unknown. (these cells i.stack.imgur.com/Yd7zq.png)Martin Smith
False and anything is false. There is no need to consider subsequent conditions. True or anything is true. (NB: The linked article does have an error in the truth tables for false as pointed out in the comments by Hugo Kornelis)Martin Smith
IF 1=2 AND NULL =1 PRINT 'TRUE' ELSE PRINT 'FALSE'Martin Smith
if 1 = 1 and @a + 1 > 5 evaluates to unknown not true which is why you go to the second branch. An if only has two branches. To really evaluate the value you can use SELECT CASE WHEN your_condition THEN 'true' WHEN NOT your_condition THEN 'false' ELSE 'unknown' END - so substituting in your latest example Declare @a int = NULL; SELECT CASE WHEN 1 = 1 and @a + 1 > 5 THEN 'true' WHEN NOT 1 = 1 and @a + 1 > 5 THEN 'false' ELSE 'unknown' END Martin Smith

5 Answers

4
votes

An example of TRUE || NULL = True would be

declare @x as int = null;
if 1=1 or @x/1=1
    print 'true'

An example of FALSE && NULL = False would be

declare @x as int = null;
if not(1=2 and @x/1=1)
    print 'false'
2
votes

True && NULL is neither True or False. It's just NULL.

Whether that will evaluate as True, False, or an Error in a boolean expression depends on what happens on your system when you evaluate NULL by itself as a boolean. Sql Server will do everything it can to avoid choosing, but when forced you'll pretty much never see a positive (True) result.

2
votes

Generally speaking from a user standpoint, you don't want a Boolean expression to evaluate to NULL.

Writing SQL typically involves writing queries to explicitly avoid NULL values in Boolean expressions. IMX, developers would consider using three valued logic intentionally would be considered an abuse of three valued logic. A properly written query should handle NULLs and understand them. You don't write them in such a way that they happen to work right when something is NULL. Usually this involves COALESCE() or IS NULL or IS NOT NULL somewhere.

It is, however, vital that you understand the logic, because NULLs exist and are unavoidable for most real-world data.

For example, let's say I'm working on a table of students. The table has First, Middle, and Last name fields. I want to know the list of students that don't have a middle name. Now, some applications will store an empty string, '', and some applications will store a NULL value, and some applications might do both (and some RDBMSs like Oracle treat empty strings as NULLs). If you were unsure, you could write it as:

SELECT *
FROM Student
WHERE MiddleName = ''
    OR MiddleName IS NULL;

The other common scenario is when you're OUTER JOINing to another table. Let's say you're comparing the paychecks for teachers. You have a table for Checks, and a table for CheckDetail. You want to know how much teachers pay for Benefits. Your report needs to list all teachers, even if they're contractors who don't pay for benefits because they don't get any:

SELECT Check.Employee_Id,
    SUM(CheckDetail.Amount) AS BenefitsDeductions
FROM Check
LEFT JOIN CheckDetail
    ON  Check.Id = CheckDetail.CheckId
    AND CheckDetail.LineItemType = 'Benefits'
GROUP BY Check.Employee_Id;

You run your report, and you notice that your contractor teachers show NULL for BenefitsDeductions. Oops. You need to make sure that shows up as a zero:

SELECT Check.Employee_Id,
    COALESCE(SUM(CheckDetail.Amount),0) AS BenefitsDeductions
FROM Check
LEFT JOIN CheckDetail
    ON  Check.Id = CheckDetail.CheckId
    AND CheckDetail.LineItemType = 'Benefits'
GROUP BY Check.Employee_Id;

So you try that, and it works. No NULL values! But... a few days later, your users report that teachers who used to be contractors are showing up with 0s even though they're paying for benefits now. You've got to COALESCE before the SUM to keep those amounts:

SELECT Check.Employee_Id,
    SUM(COALESCE(CheckDetail.Amount,0)) AS BenefitsDeductions
FROM Check
LEFT JOIN CheckDetail
    ON  Check.Id = CheckDetail.CheckId
    AND CheckDetail.LineItemType = 'Benefits'
GROUP BY Check.Employee_Id;

Finding these kinds of corner cases and exceptions is what writing SQL is all about.

2
votes

The code example by user4955163 is a great visualization of this, however I just wanted to step back in to address the first segment of the question:

...especially why TRUE || NULL = True and FALSE && NULL = False instead of evaluating to null...

TRUE || NULL = True

This is because the or operator will short-circuit if one operand is already known to be true. No matter what the second operand is (even if unknown, ie. "NULL"), it wouldn't make the expression false since we already know the other operand is true. or only needs one operand to be true, to evaluate to true.

FALSE && NULL = False

This is because the and operator will short-circuit if one operand is already known to be false. No matter what the second operand is (even if unknown, ie. "NULL"), it wouldn't make the expression true since we already know the other operand is false. and needs both operands to be true to evaluate to true.

0
votes

To use a nullable variable you just need to check NULL conditions (using IS NULL) before checking the value.

e.g. IF @a IS NOT NULL AND @a = 1