SQL: Select columns WITHOUT NULL values only in a table - sql

This question is the exact opposite of SQL: Select columns with NULL values only.
Given a table with 1024 columns, how to find all columns WITHOUT null values?
Input:a table with 1024 columns
Output:col1_name(no null values) col2_name(no null values)...

If you want to avoid using a CURSOR, this method will simply list out the column names of any columns that have no NULL values in them anywhere in the table... just set the #TableName at the top:
DECLARE #tableName sysname;
DECLARE #sql nvarchar(max);
SET #sql = N'';
SET #tableName = N'Reports_table';
SELECT #sql += 'SELECT CASE WHEN EXISTS (SELECT 1 FROM ' + #tableName + ' WHERE '+ COLUMN_NAME + ' IS NULL) THEN NULL ELSE ''' + COLUMN_NAME +
''' END AS ColumnsWithNoNulls UNION ALL '
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName
SELECT #sql = SUBSTRING(#sql, 0, LEN(#sql) - 10);
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results;
CREATE TABLE #Results (ColumnsWithNoNulls sysname NULL);
INSERT INTO #Results EXEC(#sql);
SELECT * FROM #Results WHERE ColumnsWithNoNulls IS NOT NULL
As a bonus, the results are in a temp table, #Results, so you can query to get any information you want... counts, etc.

i modified the Select columns with NULL values only.
to work for your case :
SET ANSI_WARNINGS OFF
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID
WHERE t.Name = 'Reports_table'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF (SELECT sum(iif([' + #col + '] is null,1,null)) FROM Reports_table) is null BEGIN print ''' + #col + ''' end'
exec(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
SET ANSI_WARNINGS on

Related

iterate over all databases, insert data into temp table and display data [duplicate]

I have a stored procedure which scans all tables in my DB for a certain column (codevalues).
It then prints the distinct values for that column and its table it belongs in.
Each column can belong to many tables.
i.e. codeX can be found in Table A and Table B.
ActualCode in TableA
ActualCode
----------
0
1
2
(3 row(s) affected)
--------------------
ActualCode in TableB
ActualCode
----------
0
(1 row(s) affected)
I am trying to figure out how to scan each row in my results, and then insert that row into a new table in a new database.
For example, i want to grab 0 from below for TableA, and insert a row into a new table and store the Value(0) and the Table name (TableA)
And then do the same for value 1...2 etc, and then repeat the same for the next table.
My query is as this:
DECLARE cursorColumnNames CURSOR FOR SELECT DISTINCT COLUMN_NAME, TABLE_NAME FROM
INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%ActualCode%' AND TABLE_CATALOG = 'db1' AND COLUMN_NAME NOT
IN(select ColumnName from [DataDictionary].[dbo].[Code])
OPEN cursorColumnNames
FETCH NEXT FROM cursorColumnNames INTO #columnName, #tableName;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #columnName + ' in ' + #tableName;
PRINT ' '
SET #SqlQuery = 'SELECT DISTINCT ' + #columnName + ' FROM ' + #tableName;
EXEC (#SqlQuery);
PRINT '--------------------'
FETCH NEXT FROM cursorColumnNames INTO #columnName, #tableName;
END;
CLOSE cursorColumnNames;
DEALLOCATE cursorColumnNames;
I tried grabbing each top 1 value and then add that and repeat until all data has been transferred.
But my query looks for columns that are not in the second db so after the first iteration, my query returns 0 records.
So i am confused as what to do or try.
I do not want to create a new table, the table already exists.
The table i want to add the individual rows to is defined as this:
CodeInsertID TableName ColumnName CodeNo
4648 TableA ActualCode 0
4647 TableA ActualCode 1
4646 TableA ActualCode 2
4645 TableB ActualCode 0
can u try like this ? its work for me.
Declare #SqlQuery nvarchar(max),
#columnName nvarchar(50),
#tableName nvarchar(50)
Declare #table table (TableName nvarchar(50) , ColumnName nvarchar(50) , CodeNo int)
DECLARE cursorColumnNames CURSOR FOR SELECT DISTINCT COLUMN_NAME, TABLE_NAME FROM
INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%ActualCode%' AND TABLE_CATALOG = 'db1' AND COLUMN_NAME NOT
IN(select ColumnName from [DataDictionary].[dbo].[Code])
OPEN cursorColumnNames
FETCH NEXT FROM cursorColumnNames INTO #columnName, #tableName;
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #columnName + ' in ' + #tableName;
--PRINT ' '
SET #SqlQuery = 'SELECT DISTINCT ''' + #tablename+ ''', '''+ #columnName+ ''' , ' + #columnName + ' FROM [' + #tableName + ']';
PRINT #SqlQuery
INSERT INTO #table (TableName,ColumnName,CodeNo)
EXEC (#SqlQuery);
--PRINT '--------------------'
FETCH NEXT FROM cursorColumnNames INTO #columnName, #tableName;
END;
CLOSE cursorColumnNames;
DEALLOCATE cursorColumnNames;
SELECT * from #table
Cursors are really slow, you can do this in one single dynamic statement, which we can build in one shot.
You also need to quote the column and table names with QUOTENAME otherwise your code may not work.
Note: To escape a single quote ' in a string, use ''. Don't get confused about which code is part of the dynamic section and which part is static.
DECLARE #sql nvarchar(max) =
N'INSERT INTO myTABLE (TableName, ColumnName, CodeNo)
' +
(SELECT STRING_AGG(
N'SELECT
N' + QUOTENAME(t.name, '''') + N',
N' + QUOTENAME(c.name, '''') + N',
CAST(' + QUOTENAME(c.name) + N' AS nvarchar(max)) -- or whatever your column type is here
FROM db1.' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name),
CAST(N'
UNION ALL
' AS nvarchar(max))
FROM db1.sys.tables t
JOIN db1.sys.schemas s ON s.schema_id = t.schema_id
JOIN db1.sys.columns c ON c.object_id = t.object_id
WHERE c.name LIKE N'%ActualCode%'
AND c.name NOT IN (SELECT ColumnName FROM [DataDictionary].[dbo].[Code])
);
-- PRINT #sql; -- for testing
EXEC sp_executesql #sql;
You say you only have SQL Server 2014, so you would have to use either a cursor or FOR XML PATH. You can still build the query, then execute it in one shot:
DECLARE #sql nvarchar(max) =
N'INSERT INTO myTABLE (TableName, ColumnName, CodeNo)
';
DECLARE #cur CURSOR;
SET #cur = CURSOR FAST_FORWARD FOR
SELECT
N'SELECT
N' + QUOTENAME(t.name, '''') + N',
N' + QUOTENAME(c.name, '''') + N',
CAST(' + QUOTENAME(c.name) + N' AS nvarchar(max)) -- or whatever your column type is here
FROM db1.' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name)
FROM db1.sys.tables t
JOIN db1.sys.schemas s ON s.schema_id = t.schema_id
JOIN db1.sys.columns c ON c.object_id = t.object_id
WHERE c.name LIKE N'%ActualCode%'
AND c.name NOT IN (SELECT ColumnName FROM [DataDictionary].[dbo].[Code])
;
OPEN #cur;
DECLARE #tableSql nvarchar(max);
FETCH NEXT FROM #cur INTO #tableSql;
SET #sql = #sql + #tableSql;
WHILE (##FETCH_STATUS = 0)
BEGIN
FETCH NEXT FROM #cur INTO #tableSql;
SET #sql = #sql + N'
UNION ALL
' + #tableSql;
END;
-- PRINT #sql; -- for testing
EXEC sp_executesql #sql;
There is no need to close or deallocate as it's a local variable and will be closed automatically at the end of the batch

Spool Results from Cursor Into Table

I have a cursor that I'm using to find NULL columns in a database. I'm using this to eliminate NULL Columns from an upload of this data to Salesforce using dbAMP. I'd like to modify this to spool the results into a Table and include the Table Name and Column name.
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID =
c.Object_ID
WHERE t.Name = 'Account'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF NOT EXISTS (SELECT top 1 * FROM Account WHERE [' + #col +
'] IS NOT NULL) BEGIN print ''' + #col + ''' end'
EXEC(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
I've have not had any success in modifying this cursor to put results in a table. Any guidance would be appreciated.
Make the Print a Select then Insert into (tbl with same column definition).
Create a table with the same columns in the same order.
Then put an Insert into yourtable(your columns in the same order as output from the exec().
Any change in table columns in the future may break this. The table and the query should have the same columns. If you are cautious and control the order of columns in the select and insert, it shouldn't matter about the table column order, but it is still good practice imho.
Example (insert into table with dynamic sql)
if object_id('dbo.ColumnMatch','U') is not null drop table dbo.ColumnMatch;
create table dbo.ColumnMatch (
id int identity(1,1) not null primary key
,column_name varchar(512)
);
declare #col varchar(256) = 'This Column Name'
declare #s varchar(max) = 'select ''' + #col + '''';
insert into ColumnMatch (column_name)
exec(#s);
select * from ColumnMatch;
Not Print but select and fix the Insert Into statement. :)
if object_id('dbo.ColumnMatch','U') is not null drop table dbo.ColumnMatch;
create table dbo.ColumnMatch (
id int identity(1,1) not null primary key
,column_name varchar(512)
);
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID =
c.Object_ID
WHERE t.Name = 'Account'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF NOT EXISTS (SELECT top 1 * FROM Account WHERE [' + #col +
'] IS NOT NULL) BEGIN select ''' + #col + ''' column_name end'
Insert into ColumnMatch (column_name)
EXEC(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
select * from ColumnMatch;

How can I return a distinct count of a variable for all tables in my database?

I have a SQL database with 60+ tables, almost all of which are populated with a CLIENTID field. I want to count the number of unique client IDs in each table.
The results I'm looking for are:
TABLE_NAME; CLIENTID_COUNT
dbo.HISTORY; 650
dbo.VISITS; 596
dbo.SALES; 1053
...; ...
This seems like it should be so simple but I've been playing around with cursors for hours and can't figure this one out. Please help!
IF OBJECT_ID('tempdb..#temp_RESULTS') IS NOT NULL DROP TABLE #temp_RESULTS
CREATE TABLE #TEMP_RESULTS
(
TABLENAME VARCHAR(MAX),
CLIENTCNT BIGINT
)
DECLARE #TABLENAME VARCHAR(MAX)
DECLARE #command VARCHAR(MAX)
IF OBJECT_ID('tempdb..#temp_PROCESS') IS NOT NULL DROP TABLE #temp_PROCESS
SELECT * INTO #TEMP_PROCESS FROM sys.tables
WHILE EXISTS(SELECT * FROM [#TEMP_PROCESS])
BEGIN
SET #TABLENAME = (SELECT TOP 1 [NAME] FROM [#TEMP_PROCESS])
SET #command = ('SELECT ''' + #TABLENAME + ''', COUNT(DISTINCT CLIENTID) AS CLIENTCNT FROM ' + #TABLENAME)
SELECT #command
INSERT INTO #TEMP_RESULTS
EXEC(#command)
DELETE FROM [#TEMP_PROCESS] WHERE [NAME] = #TABLENAME
END
SELECT * FROM [#TEMP_RESULTS]
Assuming the column is exactly ClientId in every table, you should be able to use this as is:
DROP TABLE IF EXISTS #clientId
CREATE TABLE #clientId
(
TableName nvarchar(1000),
ClientIdCount bigint
)
DECLARE #TableName nvarchar(1000);
DECLARE #CurrentQuery nvarchar(2000);
DECLARE result_cursor CURSOR local fast_forward FOR
SELECT DISTINCT
'['+TABLE_SCHEMA + '].[' + TABLE_NAME + ']'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME = 'ClientId'
OPEN result_cursor
FETCH NEXT FROM result_cursor into #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CurrentQuery = 'SELECT ''' + #TableName + ''', COUNT(DISTINCT ClientId) FROM ' + #TableName
--print #CurrentQuery
INSERT INTO
#clientId
(
TableName,
ClientIdCount
)
EXEC(#CurrentQuery)
FETCH NEXT FROM result_cursor into #TableName
END
--end loop
--clean up
CLOSE result_cursor
DEALLOCATE result_cursor
GO
SELECT
*
FROM
#clientId
You could use dynamic sql.
This will read through your system tables, find those that have a ClientID column, and build the text of a query that's in the general shape of 'Select Count(DISTINCT ClientID)' from each table.
DECLARE #SQLQuery as nvarchar(max) = ''
------------------------------------
-- GET THE TABLES THAT HAVE A CLIENTID FROM SCHEMA
SELECT #SQLQuery = #SQLQuery + qryTxt FROM (
SELECT DISTINCT 'SELECT ''' + tables.name + ''', COUNT(DISTINCT CLIENTID) FROM ' + tables.name + ' UNION ' AS qryTxt
FROM sys.columns left join sys.tables on columns.object_id = tables.object_id where columns.name = CLIENTID AND isnull(tables.name, '') <> '') subquery
------------------------------------
-- REMOVE THE LAST 'UNION' KEYWORD FROM SQLQUERY
SET #SQLQuery = left(#sqlQuery, len(#sqlQuery) - 5)
------------------------------------
-- EXECUTE
execute sp_executesql #SQLQuery
I really dislike cursors and loops. Even though this is not going to be much difference for a performance perspective I like to share how you can leverage the system tables and dynamic sql to avoid using a cursor, while loop or temp tables for something like this.
This code is literally all you need to to do this.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select TableName = ''' + t.name + ''', ClientID_Count = count(distinct clientID)
from ' + QUOTENAME(t.name) + ' UNION ALL '
from sys.tables t
join sys.columns c on c.object_id = t.object_id
where c.name = 'clientID'
select #SQL = left(#SQL, len(#SQL) - 10) --removes the last UNION ALL
select #SQL
--once your comfortable the dynamic sql is correct just uncomment the line below.
--exec sp_executesql #SQL
A similar pattern to other answers here, but this is how I would tackle it:
IF OBJECT_ID('#Tables', 'U') IS NOT NULL
DROP TABLE #Tables;
SELECT ID = IDENTITY(INT, 1, 1),
SchemaName = OBJECT_SCHEMA_NAME([object_id]),
TableName = OBJECT_NAME([object_id]),
ColumnName = name,
DistinctCount = 0
INTO #Tables
FROM sys.columns
WHERE name = 'CLIENTID';
DECLARE #ID INT = 1,
#MaxID INT = (SELECT MAX(ID) FROM #Tables);
WHILE #ID < #MaxID
BEGIN;
DECLARE #SQLCommand VARCHAR(MAX);
SELECT #SQLCommand = FORMATMESSAGE('
UPDATE #Tables SET DistinctCount = (
SELECT COUNT(DISTINCT %s) FROM %s.%s
)
WHERE ID = %i;',
QUOTENAME(ColumnName), QUOTENAME(SchemaName), QUOTENAME(TableName), ID)
FROM #Tables
WHERE ID = #ID;
EXEC (#SQLCommand);
SET #ID += 1;
END;
SELECT *
FROM #Tables;

SQL query to dynamically COUNT(FIELD) for all fields of table X

This should be such an easy thing, but it has me totally stumped.
You can easily return the count of each field of a table manually, with oneliners such as:
select count(FIELD1) from TABLE1 --42,706
select count(FIELD5) from TABLE1 --42,686
select count(FIELD9) from TABLE1 --2,918
This is slow and painful if you want to review several dozen tables the same way, and requires you to know the names of the fields in advance.
How handy would it be to have a script you can connect to any database, simply feed it a table name, and it will automatically return the counts for each field of that table?
Seems you can get half the work done with:
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'
Something is flawed even with my barebones approach (explicitly hitting one field instead of them all):
declare #TABLENAME varchar(30), #FIELDNAME varchar(30)
set #TABLENAME = 'TABLE1'
set #FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TABLENAME
and COLUMN_NAME = 'FIELD9')
select #FIELDNAME, count(#FIELDNAME) from TABLE1
The result is 42,706. Recall from my example above that FIELD9 only contains 2,918 values.
Even if that wasn't a problem, the more dynamic query would replace the last line with:
select #FIELDNAME, count(#FIELDNAME) from #TABLENAME
But SQL Server returns:
Must declare the table variable "#TABLENAME".
So I can avoid that by restructuring the query with a temp table:
declare #FIELDNAME varchar(30)
set #FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'
and COLUMN_NAME = 'FIELD9')
if OBJECT_ID('TEMPDB..#TEMP1') is not null
drop table #TEMP1
select *
into #TEMP1
from TABLE1 --still not exactly dynamic!
select #FIELDNAME, count(#FIELDNAME) from #TEMP1
But that still brings us back to the original problem of returning 42,706 instead of 2,918.
I am running SQL Server 2008 R2, if it makes any difference.
Your query:
SELECT #FIELDNAME, COUNT(#FIELDNAME) FROM TABLE1
does not count FIELD9, #FIELDNAME is treated as a constant. It's like doing a COUNT(*).
You should use dynamic sql:
DECLARE #sql VARCHAR(MAX)
SET #sql = 'SELECT ''' + #fieldName + ''', COUNT([' + #fieldName + ']) FROM [' + #tableName + ']'
EXEC(#sql)
To get all columns and return it in a single result set without using a Temporary Table and CURSOR:
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql = #sql +
'SELECT ''' + COLUMN_NAME + ''' AS ColName, COUNT([' + COLUMN_NAME + ']) FROM [' + #tableName + ']' + CHAR(10) +
'UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName
SELECT #sql = LEFT(#sql, LEN(#sql) - 10)
EXEC(#sql)
Just set the #TargetTableName will do the job
DECLARE #TargetTableName sysname = '*'
SET NOCOUNT ON
DECLARE #TableName sysname, #ColumnName sysname, #Sql nvarchar(max)
DECLARE #TableAndColumn table
(
TableName sysname,
ColumnName sysname
)
DECLARE #Result table
(
TableName sysname,
ColumnName sysname,
NonNullRecords int
)
INSERT #TableAndColumn
SELECT o.name, c.name FROM sys.objects o INNER JOIN sys.columns c ON o.object_id = c.object_id
WHERE (o.name = #TargetTableName OR #TargetTableName = '*') AND o.type = 'U' AND c.system_type_id NOT IN (34, 35, 99) -- 34:image 35:text 99:ntext
ORDER BY c.column_id
DECLARE column_cursor CURSOR FOR SELECT TableName, ColumnName FROM #TableAndColumn
OPEN column_cursor
FETCH NEXT FROM column_cursor
INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Sql = 'SELECT ''' + #TableName + ''' AS TableName, ''' + #ColumnName + ''' AS ColumnName, COUNT([' + #ColumnName + ']) AS NonNullRecords FROM [' + #TableName + ']'
print #Sql
INSERT #Result
EXEC (#Sql)
FETCH NEXT FROM column_cursor
INTO #TableName, #ColumnName
END
CLOSE column_cursor;
DEALLOCATE column_cursor;
SET NOCOUNT OFF
SELECT * FROM #Result

Rowcount for all tables by year in dbo schema

I'm looking for a query in SQL Server that will return the total number of rows for a given year across all tables in a particular schema, in this case dbo.
All tables in this schema have a column named UPDT_TS which stores the last time the row was updated and can be used for this date calculation.
You can use a simple cursor, and dynamic SQL.
declare #v_Year int
set #v_Year = 2013
declare #v_TableName nvarchar(256)
declare #v_SQL nvarchar(max)
declare #v_Count int
declare #v_ResultTable table
(
TableName nvarchar(256),
[RowCount] int
)
declare cur cursor local fast_forward read_only for
select t.name from sys.tables as t
inner join sys.columns as c on t.object_id = c.object_id
where c.name = 'UPDT_TS' and t.schema_id = schema_id('dbo')
open cur
while (1=1)
begin
fetch next from cur into #v_TableName
if ##FETCH_STATUS <> 0
break;
set #v_SQL = 'select #v_Count = count(*) from '+QUOTENAME(#v_TableName)+' where year(UPDT_TS) = ' + cast(#v_Year as nvarchar(4))
exec sp_executesql #v_SQL, N'#v_Count int output', #v_Count output
insert into #v_ResultTable
select #v_TableName, #v_Count
end
close cur
deallocate cur
select * from #v_ResultTable
EDIT
Just saw Goat's answer after posting :) looks like we hade the same idea :)
Assuming you have a Results table to insert records into, you can do this with a simple cursor:
DECLARE #Iterator varchar(255)
,#strSQL varchar(MAX)
DECLARE xyz CURSOR CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT t.name
FROM sys.schemas s
JOIN sys.tables t
ON s.schema_id = t.schema_id
WHERE s.name = 'dbo'
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
SET #strSQL = 'INSERT INTO Results
SELECT COUNT(*) as Rows, '''+#Iterator+''' as TableName
FROM '+QUOTENAME(#Iterator)+'
WHERE YEAR(UPDT_TS) = 2013
'
EXEC (#strSQL)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
Whenever dealing with dynamic sql it can be helpful to change EXEC (#strSQL) to PRINT (#strSQL) to ensure you've got the syntax nailed down.
This will work also:
DECLARE #sql NVARCHAR(max)
,#year INT
SET #year = 2013
SELECT #sql = 'select sum (total) from (' + SUBSTRING(cast((
SELECT ' union select count(*) as Total
from ' + quotename(table_name) + '
where
UPDT_TS between dateadd(year,#year-1900,0) and dateadd(s,-1,dateadd(year,#year-1900+1,0))'
FROM information_schema.columns
WHERE column_name = 'UPDT_TS'
AND TABLE_SCHEMA = 'dbo'
FOR XML path('')
) AS NVARCHAR(max)), 8, 8000) + ') as Presum'
PRINT #sql
EXEC sp_executesql #sql
,N'#year int'
,#year = #year