How to use DATEADD in a case statement in snowflake? - sql

I have a table with a column PERIOD_DAYS that is in varchar but contains a lot of numbers. I have another column called FIRST_DATETIME, which is in datetime data type. I want to add the numbers in the PERIOD_DAYS to the FIRST_DATETIME to get the FINAL datetime. So if datetime = '2020-01-01' + 2 DAYS, the final_datetime should be = '2020-01-03'
I have a query that contains a case statement, that says if the first_datetime IS NULL, then default the final_datetime to 0. However, I keep getting an error.
This works:
select
ID,
PERIOD_DAYS,
FIRST_DATETIME,
DATEADD('days',try_to_number(PERIOD_DAYS), FIRST_DATETIME) as FINAL_DATETIME
from TBL_A a
group by ID,PERIOD_DAYS,FINAL_DATETIME
Does not work:
select
ID,
PERIOD_DAYS,
FIRST_DATETIME,
case when try_to_number(PERIOD_DAYS) IS NULL then 0 else DATEADD('days',try_to_number(PERIOD_DAYS), FIRST_DATETIME) END as FINAL_DATETIME
from TBL_A a
group by ID,PERIOD_DAYS,FINAL_DATETIME
I need the second query to work to take into account the non-numeric values in period_days columns.

The problem as you have it is because you CASE is returning 0 which is a NUMBER on one branch and TIMESTAMP_NTZ on the other.
These are not compatible types. I would recommend using NULL instead of zero.
thus:
CASE WHEN try_to_number(PERIOD_DAYS) IS NULL THEN null
ELSE DATEADD('days',try_to_number(PERIOD_DAYS), FIRST_DATETIME)
END as FINAL_DATETIME
Given this only has 2 branches a IFF can be used instead:
IFF(try_to_number(PERIOD_DAYS) IS NULL, null, DATEADD('days',try_to_number(PERIOD_DAYS), FIRST_DATETIME) ) as FINAL_DATETIME

Related

NULL values not filtered out with WHERE statement

SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE
When I select these columns, I see that the results have some NULL values. They don't seem to be strings. However, when I try to filter the NULL values out with a where statement:
SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE WHERE VOLUME = NULL
I don't see any results. What might be the possible causes? I also tried filtering with 'NULL' but that would throw an error since the column type is double.
use this for only want null recode
SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE WHERE VOLUME IS NULL
or
SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE WHERE ISNULL(VOLUME,'') = ''
if you get not null value then use
SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE WHERE ISNULL(VOLUME,'') <> ''
or
SELECT ID, VOLUME, TYPEOF(VOLUME) FROM DBT.BASE WHERE VOLUME IS NOT NULL
a more full answer is NULL is not directly comparable, much like NaN is not comparable in floating point numbers. Both represent the "lack of a value" if you "have not value here" how can you compare it to something.
"There is nobody next to you, what is their name?" it just doesn't make sense.
So to test you ask column IS NULL or column IS NOT NULL or you can use a compact logic expression see Conditional Expressions but some common ones in Snowflake are:
short form
ANSI long
snowflake long
NVL(column,'')
CASE WHEN column IS NOT NULL THEN column ELSE '' END
IFF(column IS NOT NULL, column, '')
NVL2(column,'a','b')
CASE WHEN column IS NOT NULL THEN 'a' ELSE 'b' END
IFF(column IS NOT NULL, 'a', 'b')
ZEROIFNULL(column)
CASE WHEN column IS NOT NULL THEN column ELSE 0 END
IFF(column IS NOT NULL, column, 0)
COALESCE/NVL/IFNULL are all interchangable so I will only show one (expect COALESCE can handle N items which are checked in order)
You can use the where is function or is not function to filter all the null values.

How to use Decode Function in the case where column has string values

I have a view xxabc_v (shown below), I need to update the "Code" column to Null wherever it is N/A when "Value" column sum (900+(-900)=0) becomes zero for the "field_name" values (Demand A+Demand B) for the "Date" 01-Apr-21.
How can I put the decode logic to code column in the above case?
Table structure and expected output:
You don't want decode() because a much simpler method works:
select nullif(code, 'N/A')
This returns NULL when code takes on the specified value.
If you actually want to change the data, then you want update:
update t
set code = NULL
where code = 'N/A';
EDIT:
I see, you have an extra condition. So, use case:
(case when code = 'N/A' and
sum(value) over (partition by id, date) = 0
then NULL
else code
end)
I assumed that you need date wise id wise sum when to sum(). Please check this out:
select date,id,(case when sum(value)over(partition by date,id)=0 and code='N/A' then NULL
else Code end)code, field_name,value
from tablename

SQL SUM to ignore NULL value

I have a table TEST_TABLE as follows:
Name x_col y_col
=======================
Jay NULL 2
This is a simplistic representation of a much larger issue but will suffice.
When I do the following query I get NULL returned
SELECT SUM(x_col + y_col) FROM TEST_TABLE WHERE Name='Jay'
I want it to be 2. I thought the SUM() method ignores NULL values. How can I ignore values that are null in this query? Or actually in general, as this is a problem for a lot of my algorithms.
You get NULL because NULL + 2 returns NULL. The SUM() has only one row, and if the + expression is NULL, then the SUM() returns NULL.
If you want NULL to be treated as 0, the use COALESCE():
SELECT SUM(COALESCE(x_col, 0) + COALESCE(y_col, 0))
FROM TEST_TABLE
WHERE Name = 'Jay';
One final note. If you start with your data and filtered out all rows, then the result will still be NULL. To get 0, you need an additional COALESCE():
SELECT COALESCE(SUM(COALESCE(x_col, 0) + COALESCE(y_col, 0)), 0)
FROM TEST_TABLE
WHERE Name = 'Jayden';
Use COALESCE to replace NULL with 0.
SELECT sum(coalesce(x_col, 0) + coalesce(y_col, 0)) FROM TEST_TABLE WHERE Name='Jay'

Why does PostgreSQL complain about type when I use CASE to SET a field to NULL?

I have the following query:
UPDATE managed_avs
SET own_license_expires_at = CASE id WHEN 50 THEN NULL END
WHERE id in (50)
I get the following error:
ERROR: column "own_license_expires_at" is of type timestamp without time zone but expression is of type text
LINE 1: update managed_avs set own_license_expires_at = CASE id WHEN...
^
HINT: You will need to rewrite or cast the expression.
Why does it say that CASE id WHEN 50 THEN NULL END is of type text? Isn't it just NULL?
This happens because the case expression returns a null value for every possible outcome. As a null value has no type, Postgres defaults to text.
You can verify that using pg_typeof():
select pg_typeof(case id when 50 then null end)
from (values (50) ) as x (id);
returns
pg_typeof
---------
text
In order for this to work either the result of the when needs to be cast to a timestamp or the whole expression:
case id when 50 then null::timestamp end
or
(case id when 50 then null end)::timestamp

Get MAX value if column has a certain format

SQL Server 2008 R2
I have a table similar to this:
Example table:
ID Column
---------------
xxx1234
xxx12345
xxx123456
20150001
I am trying to get a conditional MAX value depending on the value of the column based on whether it meets as certain format. Using the above example, the fourth record, 20150001, represents a "good record" because it contains the current year, and the start of an increment. So, in my table, records that are considered "good" (those subject to the query I am trying to write) have the format "year + increment". The first three, that do not follow this format, should not be conditioned to the query since they don't match this format and should not be subject when computing the max value. Those are bad records. In the above example, the expected result would be "20150002".
The MAX query is simple enough to write, however I am wondering about an approach where I can sanitize the query to only include those records whom meet the particular format, and increment the last four digits (0001 to 0002).
TIA!
You can use the isdate function to filter out ID Columns that do not start with a valid year, and isnumeric to make sure the last 4 characters of the ID Column are valid increments. You also want the len to be 8, given this criteria. You can accomplish all this in the where clause:
-- load test data
declare #Example_Table table(ID_Column varchar(10))
insert into #Example_Table values
('xxx1234'),
('xxx12345'),
('xxx123456'),
('20150001')
-- return max valid ID_Column
select max(ID_Column) as max_ID_Column
from #Example_Table
where isdate(left(ID_Column,4)) = 1
and isnumeric(right(ID_Column,4)) = 1
and len(ID_Column) = 8
-- increment max valid ID_Column
update #Example_Table
set ID_Column = cast(ID_Column as int) + 1
where isdate(left(ID_Column,4)) = 1
and isnumeric(right(ID_Column,4)) = 1
and len(ID_Column) = 8
select * from #Example_Table
ID_Column
----------
xxx1234
xxx12345
xxx123456
20150002
You could use a regular expression to verify a correct year. The second half of the regular expression I taylored to your examples of 0001 and 0002, this could be opened up by adding '[0-9]' for each digit you're expecting.
DECLARE #Sample VARCHAR(30) = '20150001';
SELECT CASE WHEN (#Sample LIKE '[12][09][0-9][0-9]000[12]') THEN 'Yes' ELSE 'No' END;
SELECT
SUBSTRING(#Sample, 1, 4),
SUBSTRING(#Sample, 5, 4),
CASE WHEN (SUBSTRING(#Sample, 1, 4) LIKE '[12][09][0-9]') THEN 'Yes' ELSE 'No' END,
CASE WHEN (SUBSTRING(#Sample, 5, 4) LIKE '[0-9][0-9][0-9][0-9]') THEN 'Yes' ELSE 'No' END;