Compatible SQL to test for not null and not empty strings - sql

I want to have compatible SQL for both Oracle database and Microsoft SQL server.
I want a compatible SQL expression that will return true for not null and not empty strings.
If I use:
column <> ''
it will work on Microsoft SQL server but not on Oracle database (as '' is null for Oracle)
If I use:
len(column) > 0
it will work on Microsoft SQL server but not on Oracle database (since it uses length() )

NULLIF is available on both Oracle (doc) and SQL Server (doc). This expression should work:
NULLIF(column, '') IS NOT NULL
In both servers, if column is NULL, then the output of NULLIF will just pass the NULL value through. On SQL Server, '' = '', so the output of NULLIF will be NULL. On Oracle, '' is already NULL, so it gets passed through.
This is my test on SQL Server 2008 R2 Express:
WITH SampleData AS
(SELECT 1 AS col1, NULL AS col2
UNION ALL
SELECT 2, ''
UNION ALL
SELECT 3, 'hello')
SELECT *
FROM SampleData
WHERE NULLIF(col2, '') IS NOT NULL;
And this is my test case on Oracle 10g XE:
WITH SampleData AS
(SELECT 1 AS col1, NULL AS col2 FROM DUAL
UNION ALL
SELECT 2, '' FROM DUAL
UNION ALL
SELECT 3, 'hello' FROM DUAL)
SELECT *
FROM SampleData
WHERE NULLIF(col2, '') IS NOT NULL;
Both return 3 as expected.

How about
CASE WHEN column = '' THEN NULL ELSE column END IS NOT NULL

I think the key here is to differentiate between the case when the empty string is equivalent to NULL and when it isn't:
WHERE CASE WHEN '' = '' THEN -- e.g., SQL Server this is true
CASE WHEN col <> '' AND col IS NOT NULL THEN 'Y'
ELSE 'N'
END
WHEN COALESCE(col,NULL) IS NOT NULL THEN 'Y' -- Not SS, e.g., Oracle
ELSE 'N'
END = 'Y';
If the first case is true then empty string is not the same as null, and we have to test for both string being not null and string not being the empty string. Otherwise, our task is easier because empty string and null evaluate the same.

A try to shorten #DCookie's answer. I like his ( '' = '' ) test.
CASE WHEN ( '' = '' ) THEN ( column <> '' )
ELSE ( column = column )
END
Sadly, the above will not work. The next works in SQL-Server. I can't test in Oracle now:
CASE WHEN '' = '' THEN CASE WHEN column <> '' THEN 1 ELSE NULL END
ELSE CASE WHEN column = column THEN 1 ELSE NULL END
END
which can be written also as:
( '' = '' AND column <> '' )
OR ( '' IS NULL AND column = column )

Related

SQL CONCAT statement with multiple conditions

I have a table with output as of following:
I am trying to concat the outputs into a single output, given the conditions that:
When column fields (Type 1 to Type 5) IS NOT NULL OR '', take the value and combine it with another field with the same condition met
Expected output(based on screenshot above): RAM/TOTAL/N.A.
When column field (Type 6) IS NOT NULL OR '', display the result as OUT OF SERVICE, ignoring other values
Expected output: OUT OF SERVICE
Sample query:
SELECT CONCAT(Type1, '/' , Type2, '/' , Type3, '/' , Type4, '/' , Type5, '/' , Type6) AS OUTPUT FROM #myTable
You example screen shots do not agree with your expected output.
You say if Type6 is Null or blank, then it should say 'OUT OF SERVICE'
However, in your screen shot, your expected output concats the previous columns though Type6 is blank! Should it not say OUT OF SERVICE ??
Assuming that this is simply a typo.. what you could do is the following:
Set the values of the column to null if blank and then concat them in the subsequent select. Just one one many ways to do it..
;with mycte as
(select
'' as Type1
, 'RAM' as Type2
,'' as Type3
,'TOTAL' as Type4
,'N/A' as Type5
,'' as Type6
)
,set_null as (
select
Type1 = case when isnull(Type1,'') = '' then NULL else Type1+'/' end
,Type2 = case when isnull(Type2,'') = '' then NULL else Type2+'/' end
,Type3 = case when isnull(Type3,'') = '' then NULL else Type3 +'/'end
,Type4 = case when isnull(Type4,'') = '' then NULL else Type4+'/' end
,Type5 = case when isnull(Type5,'') = '' then NULL else Type5+'/' end
,Type6 = case when isnull(Type6,'') = '' then NULL else Type6 end
from mycte
)
select
case
when isnull(Type6,'') = '' then 'OUT OF SERVICE'
else concat(Type1+Type2,Type3,Type4,Type5,Type6)
end as concat_column
from set_null
You may try this. This is an approach use to remove unnecessary character added due to no null blank value from a string.
First create your string and remove the duplicate unnecessary / from the string.
At the end only need to check whether / is added in suffix or prefix which need to be removed, by using simple case and substring you can achieve the same.
;with cte as
(select 'sd' as Type1, 'RAM' as Type2,'' as Type3,'TOTAL' as Type4,'N.A' as Type5,null as Type6)
,ct as (
SELECT case when Type6 is null ---- you may chnage your condition of checking Type6 is over there
then 'OUT OF SERVICE'
else REPLACE( REPLACE( CONCAT('][', Type1, '][' , Type2, '][' , Type3, '][' , Type4, '][' , Type5, '][' , Type6), '[]', ''), '][', '/')
End AS OUTP FROM cte
)
select
case when OUTP like '/%'
then case when OUTP like '%/'
then SUBSTRING( OUTP, 2, len(OUTP)-2 )
else SUBSTRING( OUTP, 2, len(OUTP)-1 ) end
else case when OUTP like '%/'
then SUBSTRING( OUTP, 1, len(OUTP)-1 )
else OUTP end
End as OUTPU
from ct

Concat String columns in hive

I need to concat 3 columns from my table say a,b,c. If the length of the columns is greater than 0 then I have to concat all 3 columns and store it as another column d in the below format.
1:a2:b3:c
I have tried the following query but I am not sure how to proceed as I am getting null as the result.
select a,b,c,
case when length(a) >0 then '1:'+a else '' end + case when length(b) > 0 then '2:'+b else '' end + case when length(c) > 0 then '3:'+c else '' end AS d
from xyz;
Appreciate the help :)
Use concat() function:
select a,b,c,
concat(
case when length(a)>0 then concat('1:',a) else '' end,
case when length(b)>0 then concat('2:',b) else '' end,
case when length(c)>0 then concat('3:',c) else '' end
) as d
from (--test dataset
select stack(4, 'a','b','c', --all
'','b','c', --one empty
null,'b','c', --null
'','','' --all empty
) as (a,b,c)
)your_data;
Result:
OK
a b c 1:a2:b3:c
b c 2:b3:c
NULL b c 2:b3:c
Time taken: 0.284 seconds, Fetched: 4 row(s) - last one row is empty
As of Hive 2.2.0. you can use || operator instead of concat:
select a,b,c,
case when length(a)>0 then '1:'||a else '' end||
case when length(b)>0 then '2:'||b else '' end||
case when length(c)>0 then '3:'||c else '' end as d

Fill Variable with Case Statement Results

I have a questionnaire that my users have filled out (several thousand a day)
The result is each questionnaire record contains 70 something fields (that correspond to each question)
I've been asked to identify all the affirmatives for each of the 70 questions and concatentate them into one field (a summary of all the issues identified for that record).
In other languages (VBA in particular) I would accomlish this by initializing a variable to '', looping through my recordset and setting the variable to what it was previously + the field name of the issue. I'm not sure how to accomplish this in sql.
I've tried...
DECLARE #strFYI AS NVARCHAR
SET #strFYI = ''
SELECT
a.record_num
,CASE
WHEN a.Date_Missing = 'Yes' THEN #strFYI = #strFYI + 'Date_Missing, '
WHEN a.Unclear_Images = 'Yes' THEN #strFYI = #strFYI + 'Unclear_Images, '
WHEN a.Damage = 'Yes' THEN #strFYI = #strFYI + 'Damage, '
ELSE #strFYI
END AS FYI_Reasons
FROM
questionaretable a
But obviously that doesn't work. I'll also need to trim the last comma and space off the list once it's generated, but that shouldn't be a problem... I'm just not sure how to iterate through my records and build this concatenation in tsql :) I'm not even sure (because the syntax is wrong) if the variable would be reset to '' before each record was evaluated!
Can anyone help me out here?
This will be very ugly for 70 columns.
SELECT record_num, LEFT(strFYI, LEN(strFYI) - 2)
FROM (
SELECT
a.record_num,
(CASE WHEN a.Date_Missing = 'Yes' THEN 'Date_Missing, ' ELSE '' END) +
(CASE WHEN a.Unclear_Images = 'Yes' THEN 'Unclear_Images, ' ELSE '' END) +
(CASE WHEN a.Damage = 'Yes' THEN 'Damage, ' ELSE '' END) as strFYI
FROM
questionaretable a
) T
Maybe is cleaner using IIF
IIF ( boolean_expression, true_value, false_value )
SELECT record_num, LEFT(strFYI, LEN(strFYI) - 2)
FROM (
SELECT
a.record_num,
IIF(a.Date_Missing = 'Yes', 'Date_Missing, ' , '' ) +
IIF(a.Unclear_Images = 'Yes', 'Unclear_Images, ', '') +
IIF(a.Damage = 'Yes', 'Damage, ', '') as strFYI
FROM
questionaretable a
) T
As CElliot mention not IIF in 2008 so another solution may be
isnull((select 'Date_Missing, ' where a.Date_Missing = 'Yes'),'')

Count the Null columns in a row in SQL

I was wondering about the possibility to count the null columns of row in SQL, I have a table Customer that has nullable values, simply I want a query that return an int of the number of null columns for certain row(certain customer).
This method assigns a 1 or 0 for null columns, and adds them all together. Hopefully you don't have too many nullable columns to add up here...
SELECT
((CASE WHEN col1 IS NULL THEN 1 ELSE 0 END)
+ (CASE WHEN col2 IS NULL THEN 1 ELSE 0 END)
+ (CASE WHEN col3 IS NULL THEN 1 ELSE 0 END)
...
...
+ (CASE WHEN col10 IS NULL THEN 1 ELSE 0 END)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
Note, you can also do this perhaps a little more syntactically cleanly with IF() if your RDBMS supports it.
SELECT
(IF(col1 IS NULL, 1, 0)
+ IF(col2 IS NULL, 1, 0)
+ IF(col3 IS NULL, 1, 0)
...
...
+ IF(col10 IS NULL, 1, 0)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
I tested this pattern against a table and it appears to work properly.
My answer builds on Michael Berkowski's answer, but to avoid having to type out hundreds of column names, what I did was this:
Step 1: Get a list of all of the columns in your table
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable';
Step 2: Paste the list in Notepad++ (any editor that supports regular expression replacement will work). Then use this replacement pattern
Search:
^(.*)$
Replace:
\(CASE WHEN \1 IS NULL THEN 1 ELSE 0 END\) +
Step 3: Prepend SELECT identityColumnName, and change the very last + to AS NullCount FROM myTable and optionally add an ORDER BY...
SELECT
identityColumnName,
(CASE WHEN column001 IS NULL THEN 1 ELSE 0 END) +
-- ...
(CASE WHEN column200 IS NULL THEN 1 ELSE 0 END) AS NullCount
FROM
myTable
ORDER BY
NullCount DESC
For ORACLE-DBMS only.
You can use the NVL2 function:
NVL2( string1, value_if_not_null, value_if_null )
Here is a select with a similiar approach as Michael Berkowski suggested:
SELECT (NVL2(col1, 0, 1)
+ NVL2(col2, 0, 1)
+ NVL2(col3, 0, 1)
...
...
+ NVL2(col10, 0, 1)
) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
A more generic approach would be to write a PL/SQL-block and use dynamic SQL. You have to build a SELECT string with the NVL2 method from above for every column in the all_tab_columns of a specific table.
Unfortunately, in a standard SQL statement you will have to enter each column you want to test, to test all programatically you could use T-SQL. A word of warning though, ensure you are working with genuine NULLS, you can have blank stored values that the database will not recognise as a true NULL (I know this sounds strange).
You can avoid this by capturing the blank values and the NULLS in a statement like this:
CASE WHEN col1 & '' = '' THEN 1 ELSE 0 END
Or in some databases such as Oracle (not sure if there are any others) you would use:
CASE WHEN col1 || '' = '' THEN 1 ELSE 0 END
You don't state RDBMS. For SQL Server 2008...
SELECT CustomerId,
(SELECT COUNT(*) - COUNT(C)
FROM (VALUES(CAST(Col1 AS SQL_VARIANT)),
(Col2),
/*....*/
(Col9),
(Col10)) T(C)) AS NumberOfNulls
FROM Customer
Depending on what you want to do, and if you ignore mavens, and if you use SQL Server 2012, you could to it another way. .
The total number of candidate columns ("slots") must be known.
1. Select all the known "slots" column by column (they're known).
2. Unpivot that result to get a
table with one row per original column. This works because the null columns don't
unpivot, and you know all the column names.
3. Count(*) the result to get the number of non-nulls;
subtract from that to get your answer.
Like this, for 4 "seats" in a car
select 'empty seats' = 4 - count(*)
from
(
select carId, seat1,seat2,seat3,seat4 from cars where carId = #carId
) carSpec
unpivot (FieldValue FOR seat in ([seat1],[seat2],[seat3],[seat4])) AS results
This is useful if you may need to do more later than just count the number of non-null columns, as it gives you a way to manipulate the columns as a set too.
This will give you the number of columns which are not null. you can apply this appropriately
SELECT ISNULL(COUNT(col1),'') + ISNULL(COUNT(col2),'') +ISNULL(COUNT(col3),'')
FROM TABLENAME
WHERE ID=1
The below script gives you the NULL value count within a row i.e. how many columns do not have values.
{SELECT
*,
(SELECT COUNT(*)
FROM (VALUES (Tab.Col1)
,(Tab.Col2)
,(Tab.Col3)
,(Tab.Col4)) InnerTab(Col)
WHERE Col IS NULL) NullColumnCount
FROM (VALUES(1,2,3,4)
,(NULL,2,NULL,4)
,(1,NULL,NULL,NULL)) Tab(Col1,Col2,Col3,Col4) }
Just to demonstrate I am using an inline table in my example.
Try to cast or convert all column values to a common type it will help you to compare the column of different type.
I haven't tested it yet, but I'd try to do it using a PL\SQL function
CREATE OR REPLACE TYPE ANYARRAY AS TABLE OF ANYDATA
;
CREATE OR REPLACE Function COUNT_NULL
( ARR IN ANYARRAY )
RETURN number
IS
cnumber number ;
BEGIN
for i in 1 .. ARR.count loop
if ARR(i).column_value is null then
cnumber := cnumber + 1;
end if;
end loop;
RETURN cnumber;
EXCEPTION
WHEN OTHERS THEN
raise_application_error
(-20001,'An error was encountered - '
||SQLCODE||' -ERROR- '||SQLERRM);
END
;
Then use it in a select query like this
CREATE TABLE TEST (A NUMBER, B NUMBER, C NUMBER);
INSERT INTO TEST (NULL,NULL,NULL);
INSERT INTO TEST (1 ,NULL,NULL);
INSERT INTO TEST (1 ,2 ,NULL);
INSERT INTO TEST (1 ,2 ,3 );
SELECT ROWNUM,COUNT_NULL(A,B,C) AS NULL_COUNT FROM TEST;
Expected output
ROWNUM | NULL_COUNT
-------+-----------
1 | 3
2 | 2
3 | 1
4 | 0
This is how i tried
CREATE TABLE #temptablelocal (id int NOT NULL, column1 varchar(10) NULL, column2 varchar(10) NULL, column3 varchar(10) NULL, column4 varchar(10) NULL, column5 varchar(10) NULL, column6 varchar(10) NULL);
INSERT INTO #temptablelocal
VALUES (1,
NULL,
'a',
NULL,
'b',
NULL,
'c')
SELECT *
FROM #temptablelocal
WHERE id =1
SELECT count(1) countnull
FROM
(SELECT a.ID,
b.column_title,
column_val = CASE b.column_title
WHEN 'column1' THEN a.column1
WHEN 'column2' THEN a.column2
WHEN 'column3' THEN a.column3
WHEN 'column4' THEN a.column4
WHEN 'column5' THEN a.column5
WHEN 'column6' THEN a.column6
END
FROM
( SELECT id,
column1,
column2,
column3,
column4,
column5,
column6
FROM #temptablelocal
WHERE id =1 ) a
CROSS JOIN
( SELECT 'column1'
UNION ALL SELECT 'column2'
UNION ALL SELECT 'column3'
UNION ALL SELECT 'column4'
UNION ALL SELECT 'column5'
UNION ALL SELECT 'column6' ) b (column_title) ) AS pop WHERE column_val IS NULL
DROP TABLE #temptablelocal
Similary, but dynamically:
drop table if exists myschema.table_with_nulls;
create table myschema.table_with_nulls as
select
n1::integer,
n2::integer,
n3::integer,
n4::integer,
c1::character varying,
c2::character varying,
c3::character varying,
c4::character varying
from
(
values
(1,2,3,4,'a','b','c','d'),
(1,2,3,null,'a','b','c',null),
(1,2,null,null,'a','b',null,null),
(1,null,null,null,'a',null,null,null)
) as test_records(n1, n2, n3, n4, c1, c2, c3, c4);
drop function if exists myschema.count_nulls(varchar,varchar);
create function myschema.count_nulls(schemaname varchar, tablename varchar) returns void as
$BODY$
declare
calc varchar;
sqlstring varchar;
begin
select
array_to_string(array_agg('(' || trim(column_name) || ' is null)::integer'),' + ')
into
calc
from
information_schema.columns
where
table_schema in ('myschema')
and table_name in ('table_with_nulls');
sqlstring = 'create temp view count_nulls as select *, ' || calc || '::integer as count_nulls from myschema.table_with_nulls';
execute sqlstring;
return;
end;
$BODY$ LANGUAGE plpgsql STRICT;
select * from myschema.count_nulls('myschema'::varchar,'table_with_nulls'::varchar);
select
*
from
count_nulls;
Though I see that I didn't finish parametising the function.
My answer builds on Drew Chapin's answer, but with changes to get the result using a single script:
use <add_database_here>;
Declare #val Varchar(MAX);
Select #val = COALESCE(#val + str, str) From
(SELECT
'(CASE WHEN '+COLUMN_NAME+' IS NULL THEN 1 ELSE 0 END) +' str
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<add table name here>'
) t1 -- getting column names and adding the case when to replace NULLs for zeros or ones
Select #val = SUBSTRING(#val,1,LEN(#val) - 1) -- removing trailling add sign
Select #val = 'SELECT <add_identity_column_here>, ' + #val + ' AS NullCount FROM <add table name here>' -- adding the 'select' for the column identity, the 'alias' for the null count column, and the 'from'
EXEC (#val) --executing the resulting sql
With ORACLE:
Number_of_columns - json_value( json_array( comma separated list of columns ), '$.size()' ) from your_table
json_array will build an array with only the non null columns and the json_query expression will give you the size of the array
There isn't a straightforward way of doing so like there would be with counting rows. Basically, you have to enumerate all the columns that might be null in one expression.
So for a table with possibly null columns a, b, c, you could do this:
SELECT key_column, COALESCE(a,0) + COALESCE(b,0) + COALESCE(c,0) null_col_count
FROM my_table

SELECT * FROM col WHERE name != 'NULL' && name (is an int) - SQL PROBLEM

I have an sql statement which I now need to change so it only returns values back that are integers.
SELECT * FROM col WHERE name != 'NULL' <-- this works
But now I need to extend that to something like:
SELECT * FROM col WHERE name != 'NULL' && name (is an int)
But I cannot figure out the sql for this. Does anyone have any ideas?
SQL:
SELECT * FROM col WHERE NOT name IS NOT NULL AND name NOT LIKE '%[^0-9]%'
or
SELECT * FROM col WHERE NOT name IS NOT NULL AND ISNUMERIC(name) = 1
Warning, ISNUMERIC can return 1 for some non numeric characters such as +, -, or $
Here is some more information on ISNUMERIC
Also, when comparing against NULL in SQL you must use IS NULL or IS NOT NULL
For SQL server this is the Syntax:
SELECT * FROM col WHERE NOT name IS NULL AND ISNUMERIC(name) = 1 AND NOT name LIKE '%.%'
Assuming "." is used for decimals
If you use MSSQL,
SELECT * FROM col WHERE name <> 'NULL' AND IsNumeric(name) = 1