CONCATENATE a CASE in Oracle SQL - sql

I need to run a CASE expression on a number of columns, the columns are Boolean, so if it's 0 I need to populate the column with the column name and if it's 1, I ignore the column/value. I then need to concatenate all these columns into one. Is it possible to do this in Oracle SQL?
I've tried this:
Select
||CASE
WHEN COL_A = 0 THEN 'COL_A'
ELSE ''
END||','
||CASE
WHEN COL_B = 0 THEN 'COL_B'
ELSE ''
END||
from ABC.123
Can this even been done? If not this way are there any other ways?

Yes, it will work (if you clean up the syntax). Here's a simple example:
with q as (
select 0 col_a, 1 col_b, 'Rec 1' id from dual
union all
select 1, 0, 'Rec 2' from dual
union all
select 0, 0, 'Rec 3' from dual
)
Select id,
CASE
WHEN COL_A = 0 THEN 'COL_A'
ELSE ''
END||','
||CASE
WHEN COL_B = 0 THEN 'COL_B'
ELSE ''
END "TheString"
from q
Result:
ID TheString
------- -------------------
Rec 1 COL_A,
Rec 2 ,COL_B
Rec 3 COL_A,COL_B

Related

Display only the columns of two different select statements containing one row?

I have two select statements which both have one row but an fixed number of columns (15).
So far I have tried using intersect but it doesn't seem to do what I want
select 'ABC' as test, 'DEF' as test2, '123' as test3
select 'ABC2' as test, 'DEF' as test2, '124' as test3
Comparing both will display the a string will the differences
The result will the the columns with the different values as a string. In this case:
"'test': 'ABC', 'test3': '123'"
Use a bunch of case expressions to compare each column and output a value if different e.g.
select
case when a.test1 <> b.test1 then 'test1:' + a.test1 else '' end
+ case when a.test2 <> b.test2 then ', test2:' + a.test2 else '' end
+ case when a.test3 <> b.test3 then ', test3:' + a.test3 else '' end
from (
select 'ABC' as test1, 'DEF' as test2, '123' as test3
) a
cross join (
select 'ABC2' as test1, 'DEF' as test2, '124' as test3
) b;
Returns:
Result
test1:ABC, test3:123

CONCAT with IF condition in SQL Server

I have a table with four columns presenting {YES, NO, N/A} values. What I'd like to obtain is a column with concatenated names of those columns which present a 'YES' value separate by a double underscore.
\, A, B, C, D
1, YES, NO, YES, N/A
2, NO, YES, N/A, N/A
3, YES, NO, NO, YES
Expected result:
A__C
B
A__D
Something like:
select CONCAT(
IF(A = 'YES', 'A'),
IF(B = 'YES', 'B'),
IF(C = 'YES', 'C'),
IF(D = 'YES', 'D'))
from my_table
Hope I understand you right, that you want a double underscore separator.
This solution works without any subquery or cte processing.
select substring(
iif(a='YES','__A','') + iif(b='YES','__B','') +
iif(c='YES','__C','') + iif(d='YES','__D','')
,3,100)
from table1
One should know that this: substring('', 3, 100) will work using SqlServer.
Assuming T1 is your table:
SELECT CASE WHEN LEN(X)>0 THEN LEFT(X, LEN(X)-2) ELSE '' END AS Y
FROM (
SELECT
CASE WHEN A='YES' THEN 'A__' ELSE '' END + CASE WHEN B='YES' THEN 'B__' ELSE '' END + CASE WHEN C='YES' THEN 'C__' ELSE '' END + CASE WHEN D='YES' THEN 'D__' ELSE '' END AS X
FROM T1
) A
WITH ABC
as
(
Select
(
CASE WHEN A = 'YES' THEN 'A_' ELSE '' END as A +
CASE WHEN B = 'YES' THEN 'B_' ELSE '' END as B +
CASE WHEN C = 'YES' THEN 'C_' ELSE '' END as C +
CASE WHEN D = 'YES' THEN 'D_' ELSE '' END as D
) as output
)
Select case when len(output) = 2 then left (output,1)
else output
end as output
From ABC
Select case A then 'YES' then 'A' else '_'end + case B then 'YES' then 'B' else '_'end +case C then 'YES' then 'C' else '_'end +case D then 'YES' then 'D' else '_'end as result from my_table

SQL - contains only combinations of specific numbers

There is a Table (let T) with column (let C) containing only numbers. I wish to retrieve the numbers using select that are only a combination of e.g 1,2,3 like 123123, 111111, 112113, 231213 etc. Please help.
Something that only uses standard SQL and which will work in all RDBMSs:
select c from t
where replace(replace(replace(c, '1', ''), '2', ''), '3', '') = ''
and length(c) > 0
For those that support regex, like Postgres:
select c from t
where c rlike '^[123]+$'
Oracle:
select c from t
where regexp_like(c,'^[123]+$');
Just to point out that a standard SQL solution may not work properly in Oracle.
With a table like this:
create table t(c) as (
select '1233' from dual union all
select '1XX3' from dual union all
select 'XX' from dual union all
select '' from dual
)
The standard approach gives:
SQL> select c from t
2 where replace(replace(replace(c, '1', ''), '2', ''), '3', '') = ''
3 and c <> '';
no rows selected
the reason is in the way Oracle handles empty strings :
SQL> select c,
2 case when replace(replace(replace(c, '1', ''), '2', ''), '3', '') = '' and c <> '' then 'MATCH'
3 else 'NO MATCH'
4 end as checkMatch
5 from t;
C CHECKMAT
---- --------
1233 NO MATCH
1XX3 NO MATCH
XX NO MATCH
NO MATCH
In a simple example:
SQL> select case when '' = '' then 'true' else 'false' end
2 from dual;
CASEW
-----
false
In Oracle, the check should be against NULL:
SQL> select c,
2 case when replace(replace(replace(c, '1', ''), '2', ''), '3', '') is null and c is not null then 'MATCH'
3 else 'NO MATCH'
4 end as checkMatch
5 from t;
C CHECKMAT
---- --------
1233 MATCH
1XX3 NO MATCH
XX NO MATCH
NO MATCH
So an Oracle solution without regexp could be:
SQL> select c from t
2 where replace(replace(replace(c, '1', ''), '2', ''), '3', '') is null
3 and c is not null;
C
----
1233

Print result by merging records in a table

I have a table with name "PrintWord" and column name as col_letter and data in it is as follows:
"col_letter"
S
A
C
H
I
N
I would like to print the o/p from this table as:
SACHIN
Thanks!
DECLARE #t table
(
Name varchar(10)
)
INSERT INTO #t
SELECT 's' UNION ALL
SELECT 'a' UNION ALL
SELECT 'c' UNION ALL
SELECT 'h' UNION ALL
SELECT 'i' UNION ALL
SELECT 'n'
SELECT DISTINCT
stuff(
(
SELECT ' '+ [Name] FROM #t FOR XML PATH('')
),1,1,'')
FROM (SELECT DISTINCT Name FROM #t ) t
There is a hard-coded version :
SELECT col_letter
FROM PrintWord
ORDER BY
CASE col_letter
WHEN 'S' THEN 1
WHEN 'A' THEN 2
WHEN 'C' THEN 3
WHEN 'H' THEN 4
WHEN 'I' THEN 5
WHEN 'N' THEN 6
END
FOR XML PATH('')
You need an ORDER BY clause to guarantee the order of the letters.

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