Why does this SQL statement work the way it does? - sql

I've been looking to find a good way to do a SQL query where I can have a where statement which, if left blank, will act like it wasn't there at all. I've found this, which seems to work quite well:
WHERE (Column = #value OR #value is null)
If I specify a value for #value, the search is filtered like I want. But if I pass in null, it's like saying #value can be anything. I like this. This is good. What I don't understand though is, why does this work like this?

If #value is null, your WHERE clause:
WHERE (Column = #value OR #value is null)
reduces to
WHERE (Column = #value OR 1=1)
(This is similar to if (Column == value || true) in other common languages)
An OR conjunction is true if either of its operands (sides) are true: SQL uses three valued logic
+---------+------+---------+---------+
| A OR B | TRUE | Unknown | FALSE |
+---------+------+---------+---------+
| TRUE | TRUE | TRUE | TRUE |
| Unknown | TRUE | Unknown | Unknown |
| FALSE | TRUE | Unknown | FALSE |
+---------+------+---------+---------+
And so:
If #value is null, the right side of your WHERE clause is true, so the entire conditional is true, and your WHERE clause will always be satisfied.
If #value isn't null, then the right side of your WHERE clause is false, then whether the WHERE clause is satisfied depends on whether Column = #value.

Well, there are two cases:
1) #value is "something".
In this case, the second clause is always false, because "something" is never null. So all that effectively remains is WHERE Column = #value.
2) #value is null
In this case, the second clause is always false, because null never equals anything. So all that effectively remains is WHERE #value is null and #value is known to be null, so this is like WHERE 1 = 1 and the whole WHERE is ignored. The database should be clever enough to figure this out before touching any data, so this should perform just like if there was no condition specified at all.
So what you have here, is a single SQL statement that can act like two, with an "optional WHERE". The advantage over two separate SQL statements for the two cases is that you don't need conditional logic in your application when building the SQL statement (which can get really hairy if there are more than one of these "toggles").

Related

How to bring back both true , false or both

Is there a way in sql to, by default bring back values regardless of if they are true or false?
for example I have a column, 'Mandatory' which datatype is a bit.
Is there a way to bring back records where the column 'Mandatory' is either true or false or null?
something like
Select * From Table Where Mandatory = .... etc
select * from Table
where mandatory is null or mandatory in (0,1)
If the mandatory is a bit, this code is definitely true. There is no other possibility.
Let me assume you are using SQL Server -- because it supports bit but not boolean.
You can use:
where mandatory = 1
or
where mandatory = 'true'
resolved by doing this
ISNULL(Mandatory,0) =
CASE
WHEN #Mandatory = 0 THEN 0
WHEN #Mandatory = 1 THEN 1
WHEN #Mandatory = 2 THEN ISNULL(Mandatory,0)
essentially saying that when 2 is inputted, it will bring back all values for true, false and null. works very well
The question is how to run a binary logic into trinary logic.
Binary = There are distinct, actual values: in regards to this question the values [True] or [False]
Trinary = There are distinct, actual values plus unknown values: in regards to this question the values [ True | False | NULL ]
It is not possible to include unknown values in a single WHERE condition. Instead it has to be checked separately:
WHERE mandatory IN (0, 1) -- check whether TRUE or FALSE against bit
OR mandatory IS NULL -- also include UNKNOWN
This approach is better than using a function as a function in a WHERE condition can (and will) lead more often than not to performance losses.

TSQL filter that I do not understand, 2 versions that I thought would return the same data do not

So I have a SQL statement that was not returning data like it should (MSSQL Server), in part of the query I had this
and t.Invc_Sts_Cd <> 2
turns out if I changed that to
and (t.Invc_Sts_Cd <> 2 OR t.Invc_Sts_Cd IS NULL)
then it returned the data I expected, I would have thought those 2 things would be equivalent but obviously not in TSQL.
Here is the full Statement
SELECT t.tm_sht_id,0
FROM tm_sht t
INNER JOIN cand c ON c.cand_id = t.cand_id
LEFT JOIN SOW_Resource sr on c.Req_Id = sr.Req_Id and (sr.Invoice_Timesheets=1 OR sr.Invoice_Timesheets is NULL)
WHERE t.tm_sht_sts_cd = 3
AND t.apvd_dt >= #Last_Run_Time
and c.clnt_org_id = #Org_Id
and (t.Suspend_Fg <> 1)
and (t.Invc_Sts_Cd <> 2 OR t.Invc_Sts_Cd IS NULL)
My question is why do those return different data sets?
t.Invc_Sts_Cd <> 2 and (t.Invc_Sts_Cd <> 2 OR t.Invc_Sts_Cd IS NULL) are not equivalent, but they are similar.
The key thing you need to understand here is how NULL values work. Nothing is equal to or not equal to NULL, including NULL itself; NULL = NULL isn't TRUE (though is isn't FALSE either, but I come to that shortly). The only way to evaluate against a NULL is using IS NULL and IS NOT NULL.
When doing a comparison against NULL, if you don't use IS (NOT) NULL then the result will be UNKNOWN. For a expression like WHERE Column = 1 then it isn't a problem when Column has the value NULL, as it isn't 1. For other expressions, it does.
For WHERE Column <> 2, when Column has the value NULL it gives result the UNKNOWN, which importantly is not TRUE. Thus the row does not meet to criteria.
The same is true for an expression like WHERE NOT(Column = 1). If Column has the value NULL, then Column = 1 = UNKNOWN and NOT(UNKNOWN) is still UNKNOWN.
Therefore, if you have NULL values, and are performiong comparisons which rely on them, you must include IS NULL and/or IS NOT NULL logic.
Remember, NULL means you don't know what the value is. This is subtly different than not having a value at all. When you don't know what the value is, it's still possible the value might be 2, and therefore NULL <> 2 could still possibly be false... but, again, the result is you don't know.
Therefore, if t.Invc_Sts_Cd is NULL, then t.Invc_Sts_Cd <> 2 must evaluate as... NULL, because we don't know what the result is. And NULL is falsy when you force it into a true/false boolean situation.
This is not about T-SQL, but SQL in general. NULL is the unknown value. If you compare it with some value, the result is hence unknown (i.e. it could or could not match the value).
So both
and t.Invc_Sts_Cd <> 2
and
and t.Invc_Sts_Cd = 2
exclude NULLs from the result, because for a NULL in Invc_Sts_Cd neither expression results in TRUE.
The crux here is that the logical comparison:
t.Invc_Sts_Cd <> 2
is actually false when the Invc_Sts_Cd value is NULL. The reason for this is that comparing NULL to any other value yields the value NULL, which will evaluate to false when it appears inside a WHERE clause.
The second version of your logic is correct:
and (t.Invc_Sts_Cd <> 2 OR t.Invc_Sts_Cd IS NULL)
This will be true when either Invc_Sts_Cd is not equal to 2, or this value happens to be NULL.

Express a ternary SQL data type

Context
I just met a single table in a PostgreSQL database which is actually only defining a triplet of coded values that are used across the whole database as a ternary data type. I am a bit astonished at first glance, I feel it's weird; there should be some ternary data type?
I've searched the web, especially the PostgreSQL documentation without any apparent success (I'm probably wrong with my search keywords?!), but maybe there is no other solution.
Question
I would like to know if it exists a ternary (as comparison with binary or boolean) data type in PostgreSQL or more generally in SQL which permits to express a "ternary state" (or "ternary boolean" which is clearly is an abuse of language), which I would represent as a general idea as:
+-------+----------+--------------------+
| id | type | also expressed as |
+-------+----------+--------------------+
| 0 | false | 0 |
| 1 | true | 1 |
| 2 | unknown | 2 |
+-------+----------+--------------------+
where unknown can be whatever third state you are actually dealing with.
I would like to know if it exists a ternary (as comparison with binary or boolean) data type
Actually, the boolean data type is ternary because it can have the values true, false and null.
Consider this table:
create table data (some_number int, some_flag boolean);
And the following data:
insert into data (some_number, some_flag)
values (1, true), (2, false), (3, null);
Then the following:
select *
from data
where some_flag = false;
will only return one row (with some_number = 2)
there is not a specific ternary operator but you could use case
select case when operator =0 then 'false'
when operatore =1 then 'true'
when operator = 2 then 'unknow'
else 'not managed'
end
from your_table
I second a_horse_with_no_name's solution for your specific example, but the more general approach is to use an enum data type:
CREATE TYPE ternary AS ENUM (
'never',
'sometimes',
'always'
);
Constants of such a data type are written as string constantls, e.g. 'never', but the internal storage uses 4 bytes per value, regardless of the length of the label.

IS NULL doesn't work

I have a table like the one above with the two left columns (Both of them are integer) and I added to this table two more fields:
Table1:
Asset_Value Contract_Value
-------------------------------
0 NULL
NULL 200
0 NULL
And the query:
Select
Asset_Value, Contract_Value,
Case
when Asset_Value is null
then 1
else 0
end As Ind_ForNullA,
Case
when Contract_Value is null
then 1
else 0
end As IndForNullC
from
table1
However, I get strange results:
Asset_Value Contract_Value Ind_ForNullA IndForNullC
----------------------------------------------------
0 NULL 1 1
NULL 200 0 0
0 NULL 0 1
Update : Never Mind. Damm comma has been forgetten.
Try with ' ' empty string instead of null.
Try to use the function ASCII
ASCII ( character_expression )
Using this function you can understand what is the really character in the column Asset_Value: for example NULL value must have ASCII code: '00' Table with character ASCII code
Why are you bothering to define something as NULL--which is appropriate--but, in this case or that one, changing it to one or zero (an attempt at Boolean, I imagine)--which is inappropriate?
What will you do next: decide that, in a third report, you want to have a REAL boolean that displays TRUE or FALSE?
Go with NULL consistently, throughout the entire application, everywhere, or don't. However, DO NOT MIX AND MATCH PARADIGMS and expect consistent results. Also, try not to rely upon CASE: there's very, very legitimate reason to depend upon that--especially for something as simplistic as what you're doing.
FYI, the only reason I can conceive why you would want this "indicator" field is so that you can test whether INDICATOR_A = 1. However, since you can test ASSET IS NULL, why even bother? NEVER introduce extraneous mechanisms when there is no overpowering reason to do so.

IS NULL versus <> 1 SQL bit

I have a bit column on a table in a SQL Server 2012 database.
I am trying to retrieve all the rows where this bit column is either NULL or NOT TRUE.
This query does not bring back what it should: (returns 0 rows)
Select *
from table
where bit_column_value <> 1
This query brings back the correct rows:
Select *
from table
where bit_column_value IS NULL
Now, I'd be happy to use the second query, but my issue is that, in a similar query for another table, the reverse of the above is true, where the first way works, but the second way does not!
Could someone assist in explaining what the difference is in the above? I have specifically updated the relevant bit columns to be NULL and this does not change the results. (Thought maybe there was a difference between "Empty" and Null values.
Thanks in advance for any explanations.
The reason <> doesn't work is that SQL treats NULL as unknown - it doesn't know what NULL is supposed to mean, so it evaluates both = and <> on a NULL value as UNKNOWN (which is treated as false in a where clause or join condition). For more info, read this: Why does NULL = NULL evaluate to false in SQL server.
If there's an index on it, using the ISNULL function will mean the index can't be used, so to
ensure the query can use the index just use OR:
SELECT *
FROM TableName
WHERE
bit_column_value IS NULL OR bit_column_value = 0
your best bet would be to write the query as such:
SELECT
*
FROM
table
WHERE
ISNULL(bit_column_value, 0) = 0
This should return all the NULL and FALSE records.
Without seeing your table structure and data, I cannot really comment on why you are getting different results from your 2 queries.
MSDN says that the BIT type can store values 0, 1 or NULL. (The fact that a BIT value is NULL must be stored separately from the bit value itself, since the bit values can be compressed so that 8 BIT values are stored in a byte.)
Remember that a condition in a WHERE clause selects a row when the condition is TRUE. For most binary predicates (conditions), if you compare NULL with some value, the result is NULL or UNKNOWN (not TRUE). So, for example, if the value in a column is NULL, then column = 0 evaluates to NULL or UNKNOWN, and so does column <> 0.
Looking at your queries:
SELECT * FROM table WHERE bit_column_value <> 1
Where the value in the bit_column_value column is 1, the condition is FALSE so the row is not returned; where the value is 0, the condition is TRUE so the row is returned; and where the value is NULL, the condition is also NULL or UNKNOWN so the row is not returned.
SELECT * FROM table WHERE bit_column_value IS NULL
According to the SQL standard, the IS [NOT] NULL predicate, and the related IS [NOT] {TRUE|FALSE|UNKNOWN} predicates, are slightly different. The IS NULL test returns TRUE if the tested value is NULL; otherwise, they return FALSE (and never return UNKNOWN). The IS [NOT] {TRUE|FALSE|UNKNOWN} tests are similar; they return TRUE if the value is of the specified type and FALSE otherwise (not UNKNOWN). For example:
Column IS TRUE IS FALSE IS UNKNOWN IS NOT TRUE IS NOT FALSE IS NOT UNKNOWN
FALSE FALSE TRUE FALSE TRUE FALSE TRUE
TRUE TRUE FALSE FALSE FALSE TRUE TRUE
NULL FALSE FALSE TRUE TRUE TRUE FALSE
So, in your second query, only the rows where the bit_column_value value is NULL (which is separate from both 0 and 1) will be selected — not the TRUE, nor the FALSE.
I am trying to retrieve all the rows where this bit column is either NULL or NOT TRUE.
Try writing the query directly from your specification:
SELECT * FROM table WHERE bit_column_value IS NULL OR bit_column_value IS NOT TRUE
SELECT * FROM table WHERE bit_column_value IS NULL OR bit_column_value = FALSE
SELECT * FROM table WHERE bit_column_value IS NULL OR bit_column_value <> TRUE
SELECT * FROM table WHERE bit_column_value IS NOT TRUE
Given the truth table above, query 4 would yield the result you want — with the major caveat that I'm not certain that MS SQL Server supports the IS [NOT] {TRUE|FALSE|UNKNOWN}. Judging from MSDN on Predicates, the IS [NOT] {TRUE|FALSE|UNKNOWN} predicates are not supported (but I might have missed the correct part of the manual). If that's correct, you need to use one of query 2 or 3.
(There are some extra complications with these predicates when the value is not a simple column but is a row value. However, that's not relevant to your question or problem; doubly not since MS SQL Server does not seem to support them.)
Please check your table data, if it's contain value = 0 ?
SQL Bit data type can only have value either 0, 1 or NULL, if you insert other value, it's considered to 1 (Exception : If you insert 'False' it will became 0, 'True' will became 1).
For example :
insert into t1 values (1),(2),(1),(3),(-1),(0),(NULL),('false'),('true')
The result :
1, 1, 1, 1, 1, 0, NULL, 0, 1
I think this is because that all the data have NULL values in this column. So:
Select *
from table
where bit_column_value <> 1;
Won't give you the result. Since NULL is unknown. And this:
Select *
from table
where bit_column_value IS NULL;
Will give you the result you are looking for.
But you have a misconception of representing true and false using the bit data type.
You are representing false as NULL, 0 is empty and true is any other value. The bit data types works as #IswantoSan explained in his answer; It should be 0 or 1 or NULL:
0 is false,
1 is true,
NULL is empty.
Therefore to get:
true values use the where bit_column_value = 1.
false values use the where bit_column_value = 0.
NULL or empty where bit_column_value IS NULL.
NULL or not true:where bit_column_value IS NULL or bit_column_value = 0`.
The other thing to note is that NULL and empty are two different things, they are not the same. In case of the BIT data type empty is NULL not 0, because 0 is supposed to be false. But consider a string data type like VARCHAR for example then the empty string '' is totally different from the NULL value.