Count NULL Values from multiple columns with SQL - sql

I have 3 columns let say A, B, and C. I need to count the NULL values in each column.
For example:
A | B | C
-------------
1 |NULL| 1
1 | 1 | NULL
NULL| 1 | 1
NULL|NULL| 1
Should output:
A | B | C
---------------
2 | 2 | 1
I've tried count, sum, sub-queries but nothing has worked for me yet. Any input would be appreciated!

SELECT COUNT(*)-COUNT(A) As A, COUNT(*)-COUNT(B) As B, COUNT(*)-COUNT(C) As C
FROM YourTable;

For SQL SERVER you can use the following:
SET NOCOUNT ON
DECLARE #Schema NVARCHAR(100) = '<Your Schema>'
DECLARE #Table NVARCHAR(100) = '<Your Table>'
DECLARE #sql NVARCHAR(MAX) =''
IF OBJECT_ID ('tempdb..#Nulls') IS NOT NULL DROP TABLE #Nulls
CREATE TABLE #Nulls (TableName sysname, ColumnName sysname , ColumnPosition int ,NullCount int , NonNullCount int)
SELECT #sql += 'SELECT '''+TABLE_NAME+''' AS TableName , '''+COLUMN_NAME+''' AS ColumnName, '''+CONVERT(VARCHAR(5),ORDINAL_POSITION)+''' AS ColumnPosition, SUM(CASE WHEN '+COLUMN_NAME+' IS NULL THEN 1 ELSE 0 END) CountNulls , COUNT(' +COLUMN_NAME+') CountnonNulls FROM '+QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME(TABLE_NAME)+';'+ CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #Schema
AND TABLE_NAME = #Table
INSERT INTO #Nulls
EXEC sp_executesql #sql
SELECT *
FROM #Nulls
DROP TABLE #Nulls
you will receive a result set with the count of Null values and non null values in each column of you table

You can use an aggregate function with a CASE expression:
select
sum(case when a is null then 1 else 0 end) A,
sum(case when b is null then 1 else 0 end) B,
sum(case when c is null then 1 else 0 end) C
from yt
See Demo

SELECT
(SELECT count(*) FROM my_table WHERE A is NULL) as A,
(SELECT count(*) FROM my_table WHERE B is NULL) as B,
(SELECT count(*) FROM my_table WHERE C is NULL) as C

select
sum(case when a is null then 1 else 0 end) as a_null_count,
sum(case when b is null then 1 else 0 end) as b_null_count,
sum(case when c is null then 1 else 0 end) as c_null_count
from table

I am very late to this, but if you don't want to manually list out all the column names and still want to get a table, you can do this in SQL Server (Just replace testTable with your actual table):
--Creates the table the poster wanted
CREATE TABLE testTable (A int, B int, C int)
INSERT INTO testTable (A, C) VALUES (1,1)
INSERT INTO testTable (A, B) VALUES (1,1)
INSERT INTO testTable (B, C) VALUES (1,1)
INSERT INTO testTable (C) VALUES (1)
--Creates the output table which will consist of each column name and the amount of nulls
CREATE TABLE ColumnNames (
ID int IDENTITY(1,1) PRIMARY KEY,
[name] varchar(max),
nullAmount int
)
INSERT INTO ColumnNames ([name])
SELECT [name] FROM sys.columns WHERE object_id = OBJECT_ID('dbo.testTable')
DECLARE #columnIndex INT = 1
WHILE #columnIndex <= ( SELECT COUNT(*) FROM dbo.ColumnNames )
BEGIN
DECLARE #colName nvarchar(max) = (SELECT [name] FROM ColumnNames WHERE ID = #columnIndex)
EXEC('SELECT ' + #colName + ' INTO colTable FROM testTable')
DECLARE #SQL nvarchar(max) = N'UPDATE ColumnNames SET nullAmount = (SELECT COUNT(1) - COUNT(' + quotename(#colName) + ') FROM colTable) WHERE ID = #columnIndex'
EXEC SP_EXECUTESQL #SQL, N'#columnIndex int', #columnIndex
DROP TABLE colTable
SET #columnIndex = #columnIndex + 1
END
--Select the output table and get null info
SELECT * FROM ColumnNames

Related

Rolling up records by referencing values from one column to another

I have a table where I have two columns- col1 & col2. The table need to be rolled up such that if a value is in col1 then the adjacent value to col2 gets inserted next to original col1 value. To illustrate, if I have a table like this:
col1 col2
24 670
25 980
26 24
28 1570
28 26
27 5745
27 4654
Then the output should look like this (in no particular order placement):
col1 col_1 col_2 col_3 col_4
24 670 26 28 1570
25 980 NULL NULL NULL
27 4654 5745 NULL NULL
Here 24 from col2 exists in col1 already, so 26 (and other associations 28 and 1570) gets inserted next to 24. Unfortunately, with my current knowledge in sql I was able to get this:
--refrences
-- https://stackoverflow.com/questions/38233002/how-to-create-add-columns-using-a-variable-in-a-loop
-- https://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query
declare #max_columns int
declare #counter int
declare #col_header nvarchar(max)= ''
declare #query nvarchar(max)= ''
drop table if exists #tmp_ini
drop table if exists #tmp
drop table if exists #tmp_allcols
create table #tmp_ini (col1 int, col2 int )
create table #tmp_allcols (col1 int, col nvarchar(max), [val] int, [row_id] int)
insert into #tmp_ini values (24, 670),
(25, 980),
(26, 24),
(28, 1570),
(28, 26),
(27, 5745),
(27, 4654)
select * into #tmp from #tmp_ini
insert into #tmp_allcols
select col1,
'col_' + cast(t.row_id as nvarchar(max)) as col ,
col2 as val,
t.row_id
from (SELECT * , ROW_NUMBER() OVER ( PARTITION by col1 Order by col2 ) AS row_id FROM #tmp ) t
select #max_columns= max(m.tot) from(
select COUNT(*) as tot from #tmp_allcols group by col1
) m
set #counter=1
while #counter <= #max_columns
begin
set #col_header += 'col_' + cast(#counter as nvarchar(50)) + ', '
set #counter = #counter + 1
end
set #col_header = SUBSTRING(#col_header,1,LEN(#col_header)-1)
set #query += ' select * from ('
set #query += ' select col1, col, val from #tmp_allcols '
set #query += ' ) tmp'
set #query += ' PIVOT ( max(val) for Col in ('
set #query += #col_header
set #query += ' )) AS pvt'
print #query
exec sp_executesql #query
Current output:
col1 col_1 col_2
24 670 NULL
25 980 NULL
26 24 NULL
27 4654 5745
28 26 1570
Any tips or help is appreciated. I have looked at these posts, but couldn't get far SQL Pivot IF EXISTS ; `PIVOT` with `EXISTS` ; `PIVOT` with `EXISTS`
I don't think there is an easy way to do what you need, that's why I skipped the easy way, and did some crazy SQL logic:
--#####################
--#### SCHEMA #########
--#####################
CREATE TABLE Entities(
entity1 int NULL,
entity2 int NULL
)
INSERT INTO Entities VALUES
(24, 670),
(25, 980),
(26, 24),
(28, 1570),
(28, 26),
(27, 5745),
(27, 4654)
--###############################
--########### QUERY #############
--###############################
IF OBJECT_ID('tempdb..#TempRelations') IS NOT NULL
DROP TABLE #TempRelations
IF OBJECT_ID('tempdb..#TempEntities') IS NOT NULL
DROP TABLE #TempEntities
IF OBJECT_ID('tempdb..#FinalRelations') IS NOT NULL
DROP TABLE #FinalRelations
IF OBJECT_ID('tempdb..#FinalTable') IS NOT NULL
DROP TABLE #FinalTable
CREATE TABLE #TempRelations (entity INT NULL)
CREATE TABLE #FinalRelations (id INT, entity INT NULL)
SELECT ROW_NUMBER() OVER (ORDER BY entity1) Id, *
INTO #TempEntities
FROM Entities
DECLARE #entity1 INT, #entity2 INT
DECLARE #count INT = (SELECT COUNT(*) FROM #TempEntities)
DECLARE #relationsCount INT = 1;
DECLARE #i INT = 1
--WHILE THERE ARE STILL ENTITIES
WHILE EXISTS(SELECT 1
FROM #TempEntities
WHERE entity1 IS NOT NULL
OR entity2 IS NOT NULL)
BEGIN
SELECT #entity1 = entity1, #entity2 = entity2
FROM #TempEntities
WHERE Id = #i
DELETE #TempRelations
IF #entity1 IS NOT NULL
BEGIN
INSERT INTO #TempRelations VALUES (#entity1)
INSERT INTO #TempRelations VALUES (#entity2)
END
UPDATE #TempEntities
SET entity1 = NULL,
entity2 = NULL
WHERE Id = #i
--WHILE THERE ARE STILL RELATIONS TO BE TAKEN FROM ENTITIES
WHILE 1=1
BEGIN
SET #i = 1
WHILE #i <= #count
BEGIN
SELECT #entity1 = entity1, #entity2 = entity2
FROM #TempEntities
WHERE Id = #i
--IF entity1 OR entity2 HAS RELATION TO THE CURRENT TEMP RELATIONS, INSERT THE RELATION THEN REMOVE THE ENTITIES
IF #entity1 IS NOT NULL AND ( EXISTS (SELECT 1
FROM #TempRelations
WHERE entity = #entity1
OR entity = #entity2) OR (SELECT COUNT(*) FROM #TempRelations) = 0 )
BEGIN
INSERT INTO #TempRelations VALUES (#entity1)
INSERT INTO #TempRelations VALUES (#entity2)
UPDATE #TempEntities
SET entity1 = NULL,
entity2 = NULL
WHERE Id = #i
END
SET #i = #i + 1
END
IF NOT EXISTS (SELECT 1
FROM #TempEntities
WHERE EXISTS (SELECT 1
FROM #TempRelations
WHERE entity = entity1
OR entity = entity2))
BREAK
END
INSERT INTO #FinalRelations
SELECT DISTINCT #relationsCount,
entity
FROM #TempRelations
SET #relationsCount = #relationsCount + 1
DELETE #TempRelations
END
SELECT 'col' + (CAST(ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS VARCHAR)) Col,
id,
entity
INTO #FinalTable
FROM #FinalRelations
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT DISTINCT ',' + col
FROM #FinalTable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' FROM
(
SELECT Col, id, entity
FROM #FinalTable
) x
PIVOT
(
MAX(entity)
FOR Col IN (' + #cols + N')
) p '
PRINT (#query)
exec sp_executesql #query;

How to loop through a list of table names and see if a specific value in a column exists?

I have produced a table in SQL with a list of tables. This list of tables is stored under the column 'table_name'. I want to loop through each entry under 'table_name' and return a 1 if that table has a value in a specific column or 0 if that table does not have a value in a specific column.
How would I do that?
Edited With sample data
table_name
tabel1
table2
table3
table4
Pseudo Code
For i in table_name
if count(table_name["col_name"] = "value") > 0
return 1
else
return 0
Try this:
drop table if exists #t
create table #t (A int)
insert into #t
select 1
drop table if exists #t2
create table #t2 (A int)
insert into #t2
select 0
drop table if exists #tables
create table #tables (tab varchar(100))
declare
#loop table (rn int, tab varchar(100))
declare
#res table (cnt int)
declare
#i int=1
,#tab varchar (100)=''
,#query nvarchar (max)
insert into #tables
select '#t'
union all
select '#t2'
insert into #loop
select ROW_NUMBER () over (partition by (select 1) order by tab),tab from #tables
while #i<=(select max(rn) from #loop)
begin
select #tab=tab from #loop where rn=#i
set #query='select count(*) from '+#tab+' where a=1'
insert into #res
exec(#query)
if (select cnt from #res)>0 select 'Exists' else select 'Not Exists'
delete #res
set #i=#i+1
end
Assuming you have a singular column/value in question, you can try the following in SSMS:
DECLARE #Tables table ( table_name varchar(50) );
INSERT INTO #Tables VALUES
( 'Child' ), ( 'COS' ), ( 'CustomData' ), ( 'Misc' ), ( 'Misc2' );
DECLARE
#col varchar(50) = 'id', -- column to be queried
#val varchar(50) = '1', -- value to be queried
#sql varchar(MAX) = '' -- important! set to empty string;
SELECT
#sql = #sql + CASE WHEN LEN( #sql ) > 0 THEN ' UNION ' ELSE '' END
+ 'SELECT ' + QUOTENAME( table_name, '''' ) + ' AS [table_name], COUNT(*) AS [value_count] FROM [' + table_name + '] WHERE [' + #col + ']=' + QUOTENAME( #val, '''' )
FROM #Tables t WHERE EXISTS (
SELECT * FROM sys.columns c WHERE c.object_id = OBJECT_ID( t.table_name ) AND c.[name] = #col
);
EXEC( #sql );
In my environment this returns:
+------------+-------------+
| table_name | value_count |
+------------+-------------+
| Child | 1 |
| Misc | 1 |
| Misc2 | 0 |
+------------+-------------+
This is the (beautified) dynamic SQL created:
SELECT 'Child' AS [table_name], COUNT(*) AS [value_count] FROM [Child] WHERE [id]='1'
UNION
SELECT 'Misc' AS [table_name], COUNT(*) AS [value_count] FROM [Misc] WHERE [id]='1'
UNION
SELECT 'Misc2' AS [table_name], COUNT(*) AS [value_count] FROM [Misc2] WHERE [id]='1'
The EXISTS in the WHERE clause eliminates any tables that do not have the column in question, and thereby any errors related to it.

Select from a table only the columns that are not empty?

I have a table with hundreds of columns:
------------------------------------------------
ID ColA ColB ColC Col D ... ColZZZ
------------------------------------------------
1 bla
2 foo
3 bar
4 baz
------------------------------------------------
I need to know which columns have no values in them (that is: which are empty '' not NULL)
I could create a query for every column:
select count(1) from [table] where [ColA] <> ''; -- returns 2, so not, not empty
select count(1) from [table] where [ColB] <> ''; -- returns 1, so no
select count(1) from [table] where [ColC] <> ''; -- returns 0, so yay! found and empty one
...
etc
But there has to be an easier way for this?
Is there a way to return [table] without the empty columns, in other words:
----------------------------
ID ColA ColB ColZZZ
----------------------------
1 bla
2 foo
3 bar
4 baz
----------------------------
Here is solution to it. I used this query before too search for empty columns across all tables. Slightly modified now to search for non-empty, it might have few extra parts not needed in you example.
You create a temp table to store column names that are not empty, and use cursor to create dynamic sql to search for them.
In the end, just generate another dynamic sql to select columns based on temp table results.
IF (OBJECT_ID('tempdb..#tmpRez') IS NOT NULL) DROP TABLE #tmpRez;
CREATE TABLE #tmpRez (TableName sysname, ColName sysname);
DECLARE crs CURSOR LOCAL FAST_FORWARD FOR
SELECT t.name, c.name FROM sys.tables t
INNER JOIN sys.columns c ON c.object_id=t.object_id
WHERE 1=1
AND t.name = 'Table1' -- OR your own condition
OPEN crs;
DECLARE #tbl sysname;
DECLARE #col sysname;
DECLARE #sql NVARCHAR(MAX);
FETCH NEXT FROM crs INTO #tbl,#col;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #sql = 'IF EXISTS (SELECT * FROM ['+ #tbl+'] WHERE [' + #col + '] <> '''') INSERT INTO #tmpRez SELECT ''' + #tbl +''','''+ #col + '''';
EXEC(#sql);
FETCH NEXT FROM crs INTO #tbl,#col;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT #sql = 'SELECT ' + STUFF((SELECT ',' + ColName FROM #tmpRez x
where x.TableName = y.TableName
FOR XML PATH ('')), 1, 1, '') + ' FROM ' + TableName
FROM #tmpRez y GROUP BY TableName
EXEC (#sql)
How about this to return the table with no empty columns:
SELECT * from table
WHERE column IS NOT NULL AND TRIM(column) <> ''
This to return the table with empty columns:
SELECT * from table
WHERE column IS NULL AND TRIM(column) = ''

Multiple column values in a single row

I have table like this
ID Status
1 5
1 6
1 7
2 5
2 6
2 7
I need the result like below
ID col1 col2 col3
1 5 6 7
2 5 6 7
Please help me
SELECT ID,
MAX(CASE WHEN status = 5 THEN Status ELSE NULL END) col1,
MAX(CASE WHEN status = 6 THEN Status ELSE NULL END) col2,
MAX(CASE WHEN status = 7 THEN Status ELSE NULL END) col3
FROM tableNAME
GROUP BY ID
SQLFiddle Demo
using PIVOT
SELECT *
FROM (
SELECT ID, Status, CASE Status
WHEN 5 THEN 'Col1'
WHEN 6 THEN 'Col2'
WHEN 7 THEN 'Col3'
END Stat
FROM tableName
) src
PIVOT
(
MAX(Status)
FOR Stat IN ([Col1],[Col2],[Col3])
) pivotTbl
SQLFiddle Demo
Attempt for unknown count of destination columns
--Create table TESTx (id int,status int)
--insert into TESTx Values (1,5),(1,6),(1,7),(1,17),(2,5),(2,7),(2,8),(3,1),(3,5);
-
Declare #Tab Varchar(50)='##tmp' + Replace(Cast(newID() as Varchar(36)),'-','')
Declare #SQL Varchar(max)
Declare #Cols Varchar(max)
Declare #Renames Varchar(max)
Select #Cols ='Create Table ' + #Tab + '(ID int'
Select #Cols=#Cols + ',[Col'+ Cast(Status as varchar(10))+'] int'
from
(Select Distinct top 1000 Status
from
TESTx order by Status
) a
Select #Cols=#Cols +')'
Select #SQL= #Cols
+' Insert into '+#Tab +' (ID) Select Distinct ID from TESTx'
exec(#SQL)
Declare #Status int
DECLARE P_cursor CURSOR FOR
SELECT Distinct Status from TESTx
OPEN P_cursor
FETCH NEXT FROM P_cursor
INTO #Status
WHILE ##FETCH_STATUS = 0
BEGIN
Select #SQL='Update ' + #Tab + ' Set Col' + CAST(#Status as Varchar(10)) +'=' +Cast(#Status as Varchar(10))
+' from TESTx Where TESTx.ID=' + #Tab +'.ID and Testx.Status=' +Cast(#Status as Varchar(10))
Exec(#SQL)
FETCH NEXT FROM P_cursor
INTO #Status
END
CLOSE P_cursor
DEALLOCATE P_cursor
Select #SQL=' Select * from '+#Tab +' Drop Table ' + #Tab
Exec(#SQL)

Find columns that contain only zeros

I'm working with SQL Server 2008. I have a list of column names on a table and I'd like to know how to use SQL to return the names of those columns which contain nothing but zero or NULL values.
declare #T table
(
Col1 int,
Col2 int,
Col3 int,
Col4 int
)
insert into #T values
(1, 0 , null, null),
(0, null, 0 , 1)
select U.ColName
from
(
select count(nullif(Col1, 0)) as Col1,
count(nullif(Col2, 0)) as Col2,
count(nullif(Col3, 0)) as Col3,
count(nullif(Col4, 0)) as Col4
from #T
) as T
unpivot
(C for ColName in (Col1, Col2, Col3, Col4)) as U
where U.C = 0
Result:
ColName
----------
Col2
Col3
The idea behind this is to count the non null values and only keep those with a count of 0.
COUNT will only count non null values.
NULLIF(ColX, 0) will make all 0 into null.
The inner query returns one row with four columns. UNPIVOT will turn it around so you have two columns and four rows.
Finally where U.C = 0 makes sure that you only get the columns that has no values other than null or 0.
Here is a brute force way, since you know all the column names.
CREATE TABLE dbo.splunge
(
a INT,
b INT,
c INT,
d INT
);
INSERT dbo.splunge VALUES (0,0,1,-1), (0,NULL,0,0), (0,0,0,NULL);
SELECT
cols = STUFF(
CASE WHEN MIN(COALESCE(a,0)) = MAX(COALESCE(a,0)) THEN ',a' ELSE '' END
+ CASE WHEN MIN(COALESCE(b,0)) = MAX(COALESCE(b,0)) THEN ',b' ELSE '' END
+ CASE WHEN MIN(COALESCE(c,0)) = MAX(COALESCE(c,0)) THEN ',c' ELSE '' END
+ CASE WHEN MIN(COALESCE(d,0)) = MAX(COALESCE(d,0)) THEN ',d' ELSE '' END,
1, 1, '')
FROM dbo.splunge;
-- result:
-- a,b
GO
DROP TABLE dbo.splunge;
You could probably generate much of this script instead of doing it manually, assuming you know the naming scheme or data type of the columns you want (or just by leaving off the where clause entirely and removing the columns you don't want manually).
SELECT CHAR(13) + CHAR(10) + ' + CASE WHEN MIN(COALESCE(' + name + ',0)) = '
+ 'MAX(COALESCE(' + name + ',0)) THEN '',' + name + ''' ELSE '''' END'
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.splunge')
-- AND system_type_id = 56
-- AND name LIKE '%some pattern%'
;
The output will look like the middle of the first query, so you can copy & paste and then remove the first + and add the surrounding STUFF and query...
Here's a way that works for any table:
declare #tableName nvarchar(max) = N'myTable'
declare #columnName nvarchar(max)
create table #zeros (column_name varchar(max))
declare c cursor local forward_only read_only for
select column_name
from information_schema.COLUMNS WHERE table_name = #tableName
open c
fetch next from c into #columnName
while ##FETCH_STATUS = 0
begin
declare #retval int
declare #sql nvarchar(max) =
N'set #retval = (select count(*) from ' + #tableName + N' where coalesce(' + #columnName + N', 0) <> 0)'
exec sp_executesql #sql, N'#retval int out', #retval=#retval out
select #retval
if #retval = 0
begin
insert into #zeros (column_name) values (#columnName)
end
fetch next from c into #columnName
end
close c
deallocate c
select * from #zeros
drop table #zeros