Update binary column based on integer column - Postgres - sql

I have some legacy data with a binary column, fish_otolith, that is not accurate. It should indicate based on separate column named fish_age. When an age is present in the fish_age column the binary column, fish_otolith, should indicate and when a null value is present in the fish_age column the binary column, fish_otolith, should not indicate.
fish_otolith
fish_age
1
10
1
2
0
Null
1
Null
1
Null
I am struggling with writing the correct update sql statement of "if fish_age is not Null then fish_otolith is equal to 1, else if fish is null then fish_otolith is equal to 0

If your fish_otolith is a boolean, then try this:
update ttable
set fish_otolith = fish_age is not null;

The expression fish_age is not null returns a boolean value which can be cast as an integer with the result false -> 0 and true -> 1, which is the results you are looking for. So:
update fishes
set fish_otolith = (fish_age is not null)::integer;
This (or any other update) however contains a potential fatal flaw: fish_otolith can be updated independent of fish_age and can contain any valid integer not just 0 or 1. This con be overcome with a slight design change. Define fish_otolith as a generated always ... derived from fish_age. (see demo here - for both implementations).
Note: Either setting by #MikeOrganek would also work.

Related

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.

How to set a NULL when entering NULL keeps the old value

I have a stored procedure where it is able to update the 4 columns below as there are 4 parameters (1 parameter for each column with ClawbackID the only one that doesn't change).
Now there is no fixed amount of columns that can be updated, I may choose to update 1 column, or 3 columns or all 4 columns. So to reduce human error, I have a coalesce so that if the user enters in 'NULL' for a particular parameter whilst executing a procedure, the original amount stays.
Example for row 3 for 'ClawbackAmount', if I enter in NULL and execute, it will still display the 'ClawbackAmount' 900.54. Now the problem I have is that actually want to set this amount to 'NULL', but I don't want to lose the functionality that if I type in 'NULL' when executing my procedure that it keeps the old value.
My question is that is there a way or an idea you can think of where I type in NULL then the default values stays but if I type in something like '' then it will default to 'NULL'?
Or alternatively type in the word 'SAME' to keep the default value and then type in 'NULL' for a null value? Just ideas really to get around it?
Below is the code I have with the parameters included as they begin with #:
update clw
set clw.PaymentID = Coalesce(#PaymentID, clw.PaymentId)
,clw.ClawbackDate = Coalesce(#ClawbackDate, clw.ClawbackDate)
, clw.ClawbackPercent = Coalesce(#ClawbackPercent, clw.ClawbackPercent)
, clw.ClawbackAmount = Coalesce(#ClawbackAmount,clw.ClawbackAmount)
OUTPUT '[Fees].EBD.Clawback' 'TableName','ClawbackId', inserted.ClawbackId,
Core.updXMLFragment('PaymentId', inserted.PaymentId, deleted.PaymentId) +
Core.updXMLFragment('ClawbackDate', Convert(varchar(50),inserted.ClawbackDate, 112), Convert(varchar(50),deleted.ClawbackDate, 112)) +
Core.updXMLFragment('ClawbackPercent', inserted.ClawbackPercent, deleted.ClawbackPercent) +
Core.updXMLFragment('ClawbackAmount', inserted.ClawbackAmount, deleted.ClawbackAmount)
INTO #OutputList
from [Fees].EBD.Clawback clw
Where
ClawbackId = #ClawbackID
Below is the code for the execution as an example for row 3 if I want to make the desired change:
First Param is ClawbackID (this doesn't change but need it to know which row to manipulate.
Second Param is PaymentID which is NULL as want to keep the same
Third Param is Clawbackdate which is NULL as want to keep the same
Fourth Param is ClawbackPercent which needs to be 0.25
Last Param is ClawbackAmount which I need to set to NULL. This is an int field btw but leaving it NULL will keep the orginal amount displayed.
exec SupportAudit.BI.UpdateHotelClawback 28817, NULL, NULL, 0.25, NULL
I generally use some specific invalid value as an indicator the column should be set to NULL. In the case of ClawbackPercent and ClawbackAmount, -1 seems like a good candidate. The change would look like this:
...
, clw.ClawbackPercent = NullIf(Coalesce(#ClawbackPercent, clw.ClawbackPercent), -1)
, clw.ClawbackAmount = NullIf(Coalesce(#ClawbackAmount,clw.ClawbackAmount), -1)
...
For string parameters, '' (the empty string) might be an appropriate choice.

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 not working in WHERE clause of SQL

I have written a Stored procedure in which in given table column named xx can have 0,1 or null. When I give the below condition. SP is ignoring null and returning data only for 0 value.
WHERE (CAR_INSPECTION_NEW_TEST.NODAMAGEFLAG is null OR
CAR_INSPECTION_NEW_TEST.NODAMAGEFLAG = 0) AND
CAR_INSPECTION_NEW_TEST.ISSUBJECT_TODELIVER = 0
Can any one tell what is the problem?
By simple logic it will not return rows where NODAMAGEFLAG is null as long as ISSUBJECT_TODELIVER = 0 is not also valid (because of the AND).
So, check your data please.

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.