PostgreSQL query rows with least null value on columns - sql

How can I query rows where the output would be the rows with least null value on the columns?
My data is:
ID | col1 | col2 | col3 | col4
-----------+----------+-----------+-----------+-----------
1 | Null |Null | with value| with value
2 |with value|Null | with value| with value
3 |with value|Null | Null | Null
where the result would be:
ID | col1 | col2 | col3 | col4
-----------+----------+-----------+-----------+-----------
2 |with value|Null | with value| with value
Because id 2 is the record with fewest null values.
Any help will be greatly appreciated. Thanks

You can:
Order rows by number of nulls (ascending)
Limit rows to 1 ( LIMIT 1 )
Your code:
SELECT *
FROM your_table
ORDER BY
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 col4 IS NULL THEN 1 ELSE 0 END
LIMIT 1

If you want only one row, then you can do:
select t.*
from t
order by ( (col1 is null)::int + (col2 is null)::int +
(col3 is null)::int + (col4 is null)::int
) asc
fetch first 1 row only;
If you want all such rows, I think I would do:
select t.*
from (select t.*,
dense_rank() over
(order by (col1 is null)::int + (col2 is null)::int +
(col3 is null)::int + (col4 is null)::int
) as null_ranking
from t
) t
where null_ranking = 1;

Related

How to select only those rows where one column can have null or not null values for the same id

Can someone please help me on how to select only the rows which has not null value of a column where that column can have null or not null values for the same id
I have a table like this
I need an output like this
Your description is not very clear. If I do understand correctly your requirement, you want only 1 row per col1. Choose the row with most not null value ?
select *
from
(
select *, rn = row_number() over (partition by col1
order by case when col2 is not null then 1 else 0 end
+ case when col3 is not null then 1 else 0 end
+ case when col4 is not null then 1 else 0 end
desc)
from a_table
) d
where rn = 1

Create a calculated column based on evaluating whether some of several columns are true?

I want to write a query creating a calculated column based on evaluating whether several existing columns are true.
For example, if I wanted the output of the column to be 'No' if any 3 or more of 7 columns happen to be null, 'Maybe' if any 2 of them happens to be null, and "Yes" if any one of them happens to be null. A case statement seems like it would require each possible combination of columns to be enumerated.
I looked into creating a local variable, and then setting/redefining it within a select statement, but I can't find anything about what syntax can do this (assuming it's possible). What is the way to do this?
It's not pretty, but here's an option:
ALTER TABLE dbo.Table ADD CalculatedColumn AS (
CASE (CASE WHEN col_a IS NULL THEN 1 ELSE 0 END + CASE WHEN col_b IS NULL THEN 1 ELSE 0 END + ...)
WHEN 1 THEN 'Yes'
WHEN 2 THEN 'Maybe'
ELSE 'No' --3 or more
END
);
You could also add a case for 0, however you wanted to handle that.
From a query perspective, you can try something like the following:
-- Create a mock-up table.
DECLARE #Data table (
col1 varchar(10),
col2 varchar(10),
col3 varchar(10),
col4 varchar(10),
col5 varchar(10),
col6 varchar(10),
col7 varchar(10),
id int IDENTITY ( 1, 1 )
);
-- Insert some mock data.
INSERT INTO #Data VALUES
( 'val', 'val', 'val', 'val', NULL, NULL, NULL ),
( 'val', 'val', 'val', 'val', 'val', NULL, NULL ),
( 'val', 'val', 'val', 'val', 'val', 'val', NULL );
-- Query data and return Yes, No, Maybe based on the number of null columns.
;WITH col_data AS (
SELECT
id,
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 col4 IS NULL THEN 1 ELSE 0 END
+ CASE WHEN col5 IS NULL THEN 1 ELSE 0 END
+ CASE WHEN col6 IS NULL THEN 1 ELSE 0 END
+ CASE WHEN col7 IS NULL THEN 1 ELSE 0 END
AS null_sum
FROM #Data
)
SELECT
id,
null_sum,
CASE
WHEN null_sum <= 1 THEN 'Yes'
WHEN null_sum <= 2 THEN 'Maybe'
ELSE 'No'
END AS msg
FROM col_data;
RETURNS
+----+----------+-------+
| id | null_sum | msg |
+----+----------+-------+
| 1 | 3 | No |
| 2 | 2 | Maybe |
| 3 | 1 | Yes |
+----+----------+-------+

How to get number of false in every column of a table?

I have a table say T_myTable it has 5 columns and all have some values either true or false.
--------------------------------
col1 | col2 | col3 | col4 | col5
--------------------------------
true | false|false|true |false
false| true |false|false|false
true | false|false|true |false
false| false|false|true |false
I want to get result as:-
col1 | col2 | col3 | col4 | col5
--------------------------------
2 | 3 |4 |1 |4
Where these numbers here are numbers of false.
Also true and false are varchar.
This:
SELECT SUM(CASE WHEN Col1 = 'false' THEN 1 ELSE 0 END) AS Col1
, SUM(CASE WHEN Col2 = 'false' THEN 1 ELSE 0 END) AS Col2
, SUM(CASE WHEN Col3 = 'false' THEN 1 ELSE 0 END) AS Col3
, SUM(CASE WHEN Col4 = 'false' THEN 1 ELSE 0 END) AS Col4
, SUM(CASE WHEN Col5 = 'false' THEN 1 ELSE 0 END) AS Col5
FROM T_myTable
Try this:
SELECT SUM(CASE col1 WHEN 'false' THEN 1 ELSE 0 END),
SUM(CASE col2 WHEN 'false' THEN 1 ELSE 0 END),
SUM(CASE col3 WHEN 'false' THEN 1 ELSE 0 END),
SUM(CASE col4 WHEN 'false' THEN 1 ELSE 0 END),
SUM(CASE col5 WHEN 'false' THEN 1 ELSE 0 END)
FROM T_myTable
One of the most interesting way is to:
unpivot data to be able to filter data
pivot data again to get count of 'false'
Check this:
SELECT [col1],[col2],[col3],[col4],[col5]
FROM (
SELECT MyVal, ColName
FROM (
SELECT *
FROM T_myTable
) AS pvt
UNPIVOT(MyVal FOR ColName IN ([col1],[col2],[col3],[col4],[col5])) AS unpvt
WHERE MyVAl = 'false'
) As DT
PIVOT (COUNT(MyVal) FOR ColName IN ([col1],[col2],[col3],[col4],[col5])) AS PT
Result:
col1 col2 col3 col4 col5
2 3 4 1 4

Concatenate SQL table rows with conditions in single text

I have a SQL table like this
col1 col2 col3
1 0 1
1 1 1
0 1 1
1 0 0
0 0 0
I am expecting output as like this
col1 col2 col3 NewCol
1 0 1 SL,PL
1 1 1 SL,EL,PL
0 1 1 EL,PL
1 0 0 SL
0 0 0 NULL
The condition for this is if col1>0 then SL else ' ', if col2>0 EL else ' ', if col3>0 PL else ' '
I tried to use Concatenate many rows into a single text string? but didn't able to achieve the desired result properly
I have tried It is working fine with a message
Invalid length parameter passed to the LEFT or SUBSTRING function.
WITH CTE AS (
SELECT col1, col2, col3,
CASE WHEN col1 > 0 THEN 'SL,' ELSE '' END +
CASE WHEN col2 > 0 THEN 'EL,' ELSE '' END +
CASE WHEN col3 > 0 THEN 'PL,' ELSE '' END AS NewCol
FROM Employee
)
SELECT col1, col2, col3,
substring(NewCol, 1, len(NewCol) - 1) AS NewCol
FROM CTE
But again my last condition is not matching if all columns is 0 then I have to show NULL as per desired output.
Find the attach fiddle http://sqlfiddle.com/#!6/2bd6a/1
The issue with your code example is that when all columns are 0 then the length is 0 and the substring function will throw an error.
Use nullif to fix it: substring(NewCol, 1, len(nullif(NewCol,'')) - 1) AS NewCol
You could also change to appending the delimiter on the front and use STUFF.
STUFF('',1,1,'') will return NULL rather than an error.
WITH
Employee(col1, col2, col3) AS (
SELECT 1,1,1 UNION ALL
SELECT 0,0,0
),
CTE AS (
SELECT col1, col2, col3,
CASE WHEN col1 > 0 THEN ',SL' ELSE '' END +
CASE WHEN col2 > 0 THEN ',EL' ELSE '' END +
CASE WHEN col3 > 0 THEN ',PL' ELSE '' END AS NewCol
FROM Employee
)
SELECT col1,
col2,
col3,
STUFF(NewCol, 1, 1, '')
FROM CTE
Returns
+------+------+------+------------------+
| col1 | col2 | col3 | (No column name) |
+------+------+------+------------------+
| 1 | 1 | 1 | SL,EL,PL |
| 0 | 0 | 0 | NULL |
+------+------+------+------------------+
You have to check with NULLIF to do this trick
Two ways
SELECT col1, col2, col3,
nullif(CASE WHEN col1 = 1 THEN 'SL,' ELSE '' END +
CASE WHEN col2 = 1 THEN 'EL,' ELSE '' END +
CASE WHEN col3 = 1 THEN 'PL,' ELSE '' END,'') AS NewCol
FROM Employee
OR
SELECT
col1,
col2,
col3,
substring(nullif(NewCol,''), 1, len(NewCol) - 1) AS NewCol
FROM
CTE

SQL query about self join

I have a sql in db2 as below:
SELECT distinct KEY_COL, MY_TYPE1,
CASE WHEN MY_TYPE2 = 'ONE' THEN COL1 ELSE '' END AS NEWCOL1,
CASE WHEN MY_TYPE2 = 'ONE' THEN COL2 ELSE '' END AS NEWCOL2,
CASE WHEN MY_TYPE2 = 'TWO' THEN COL1 ELSE '' END AS NEWCOL3,
CASE WHEN MY_TYPE2 = 'TWO' THEN COL2 ELSE '' END AS NEWCOL4
FROM MYSCHEMA.MYTABLE
As clear from above query, based on MY_TYPE2 colums in MYSCHEMA.MYTABLE, the new column values are decided.
If the MY_TYPE2 is one, col1 and col2 goes to newcol1 and newcol2 respectively for given key and MY_TYPE1 column. But if the MY_TYPE2 is two for same key and MY_TYPE1, then col1 and col2 goes to newcol3 and newcol4.
The output table looks something like below:
KEY_COL | MY_TYPE1 | NEWCOL1 | NEWCOL2 | NEWCOL3 | NEWCOL4
=========================================================
1 T1 1 2
1 T1 4 3
1 T2 2 3
1 T2 4 1
========================================================
However, I want a single row based on MY_TYPE1 column as below:
KEY_COL | MY_TYPE1 | NEWCOL1 | NEWCOL2 | NEWCOL3 | NEWCOL4
=========================================================
1 T1 1 2 4 3
1 T2 2 3 4 1
========================================================
How to modify the query to achieve above result ?
Thanks for reading!
select key_col, my_type1, max(newcol1), max(newcol2), max(newcol3), max(newcol4)
from
(
SELECT distinct KEY_COL, MY_TYPE1,
CASE WHEN MY_TYPE2 = 'ONE' THEN COL1 ELSE '' END AS NEWCOL1,
CASE WHEN MY_TYPE2 = 'ONE' THEN COL2 ELSE '' END AS NEWCOL2,
CASE WHEN MY_TYPE2 = 'TWO' THEN COL1 ELSE '' END AS NEWCOL3,
CASE WHEN MY_TYPE2 = 'TWO' THEN COL2 ELSE '' END AS NEWCOL4
FROM MYSCHEMA.MYTABLE
) definedTable
group by key_col, my_type1
select KEY_COL ,max(MY_TYPE1),max(NEWCOL1),max(NEWCOL2),max(NEWCOL3),max(NEWCOL4)
from table
group by KEY_COL