I want to write a function that counts non null and non empty entries of a field. My problem is that the query does not run since the #tableName variable is not recognized in the select statement and I do not know why
create function dbo.getCount(#cod int, #columnName as varchar(20), #tableName as varchar(20))
Returns int as
Begin
--Count all filled entries
Return (select COUNT(*) from #tableName
where #columnName <> '' and #columnName is not null)
End;
go
As mentioned in the comments, but to reiterate, as I'll delete them after this answer:
You can't do this with a function, for multiple reasons. SELECT
COUNT(*) FROM #TableName means count the number of rows in the
table variable #TableName not the table who's name is the value of #TableName. WHERE #ColumnName <> '' would mean where the value of the scalar variable doesn't have the value '',
not where the column (in the aforementioned table) with the name of value of #ColumnName doesn't have the value ''.
And you can't do this in a function as to do this type of thing, you
need dynamic SQL; and you can't use dynamic SQL in a function (as you
can't use the EXEC command).
You can, however, do this with a Stored Procedure:
CREATE PROC dbo.GetCount #SchemaName sysname = N'dbo', #TableName sysname, #ColumnName sysname, #Count int OUTPUT AS
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SQL = N'SELECT #Count = COUNT(NULLIF(' + QUOTENAME(c.[name]) + N',''''))' + #CRLF +
N'FROM ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.[name]) + N';'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE s.[name] = #SchemaName
AND t.[name] = #TableName
AND c.[name] = #ColumnName;
--PRINT #SQL; --Your debugging friend
EXEC sp_executesql #SQL, N'#Count int OUTPUT', #Count OUTPUT;
END
GO
And you run the SP like below (with sample table):
CREATE TABLE dbo.TestTable (SomeColumn varchar(10));
INSERT INTO dbo.TestTable (SomeColumn)
VALUES(''),('abc'),(NULL);
GO
DECLARE #Count int;
EXEC dbo.GetCount #TableName = N'TestTable', #ColumnName = N'SomeColumn', #Count = #Count OUTPUT;
SELECT #Count; --Returns 1
GO
DB<>Fiddle
Related
I am trying to execute this query:
declare #tablename varchar(50)
set #tablename = 'test'
select * from #tablename
This produces the following error:
Msg 1087, Level 16, State 1, Line 5
Must declare the table variable "#tablename".
What's the right way to have the table name populated dynamically?
For static queries, like the one in your question, table names and column names need to be static.
For dynamic queries, you should generate the full SQL dynamically, and use sp_executesql to execute it.
Here is an example of a script used to compare data between the same tables of different databases:
Static query:
SELECT * FROM [DB_ONE].[dbo].[ACTY]
EXCEPT
SELECT * FROM [DB_TWO].[dbo].[ACTY]
Since I want to easily change the name of table and schema, I have created this dynamic query:
declare #schema sysname;
declare #table sysname;
declare #query nvarchar(max);
set #schema = 'dbo'
set #table = 'ACTY'
set #query = '
SELECT * FROM [DB_ONE].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table) + '
EXCEPT
SELECT * FROM [DB_TWO].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table);
EXEC sp_executesql #query
Since dynamic queries have many details that need to be considered and they are hard to maintain, I recommend that you read: The curse and blessings of dynamic SQL
Change your last statement to this:
EXEC('SELECT * FROM ' + #tablename)
This is how I do mine in a stored procedure. The first block will declare the variable, and set the table name based on the current year and month name, in this case TEST_2012OCTOBER. I then check if it exists in the database already, and remove if it does. Then the next block will use a SELECT INTO statement to create the table and populate it with records from another table with parameters.
--DECLARE TABLE NAME VARIABLE DYNAMICALLY
DECLARE #table_name varchar(max)
SET #table_name =
(SELECT 'TEST_'
+ DATENAME(YEAR,GETDATE())
+ UPPER(DATENAME(MONTH,GETDATE())) )
--DROP THE TABLE IF IT ALREADY EXISTS
IF EXISTS(SELECT name
FROM sysobjects
WHERE name = #table_name AND xtype = 'U')
BEGIN
EXEC('drop table ' + #table_name)
END
--CREATES TABLE FROM DYNAMIC VARIABLE AND INSERTS ROWS FROM ANOTHER TABLE
EXEC('SELECT * INTO ' + #table_name + ' FROM dbo.MASTER WHERE STATUS_CD = ''A''')
Use:
CREATE PROCEDURE [dbo].[GetByName]
#TableName NVARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sSQL nvarchar(500);
SELECT #sSQL = N'SELECT * FROM' + QUOTENAME(#TableName);
EXEC sp_executesql #sSQL
END
You can't use a table name for a variable. You'd have to do this instead:
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT * from yourtable'
EXEC (#sqlCommand)
You'll need to generate the SQL content dynamically:
declare #tablename varchar(50)
set #tablename = 'test'
declare #sql varchar(500)
set #sql = 'select * from ' + #tablename
exec (#sql)
Use sp_executesql to execute any SQL, e.g.
DECLARE #tbl sysname,
#sql nvarchar(4000),
#params nvarchar(4000),
#count int
DECLARE tblcur CURSOR STATIC LOCAL FOR
SELECT object_name(id) FROM syscolumns WHERE name = 'LastUpdated'
ORDER BY 1
OPEN tblcur
WHILE 1 = 1
BEGIN
FETCH tblcur INTO #tbl
IF ##fetch_status <> 0
BREAK
SELECT #sql =
N' SELECT #cnt = COUNT(*) FROM dbo.' + quotename(#tbl) +
N' WHERE LastUpdated BETWEEN #fromdate AND ' +
N' coalesce(#todate, ''99991231'')'
SELECT #params = N'#fromdate datetime, ' +
N'#todate datetime = NULL, ' +
N'#cnt int OUTPUT'
EXEC sp_executesql #sql, #params, '20060101', #cnt = #count OUTPUT
PRINT #tbl + ': ' + convert(varchar(10), #count) + ' modified rows.'
END
DEALLOCATE tblcur
You need to use the SQL Server dynamic SQL:
DECLARE #table NVARCHAR(128),
#sql NVARCHAR(MAX);
SET #table = N'tableName';
SET #sql = N'SELECT * FROM ' + #table;
Use EXEC to execute any SQL:
EXEC (#sql)
Use EXEC sp_executesql to execute any SQL:
EXEC sp_executesql #sql;
Use EXECUTE sp_executesql to execute any SQL:
EXECUTE sp_executesql #sql
Declare #tablename varchar(50)
set #tablename = 'Your table Name'
EXEC('select * from ' + #tablename)
Also, you can use this...
DECLARE #SeqID varchar(150);
DECLARE #TableName varchar(150);
SET #TableName = (Select TableName from Table);
SET #SeqID = 'SELECT NEXT VALUE FOR ' + #TableName + '_Data'
exec (#SeqID)
Declare #fs_e int, #C_Tables CURSOR, #Table varchar(50)
SET #C_Tables = CURSOR FOR
select name from sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 AND name like 'TR_%'
OPEN #C_Tables
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
WHILE ( #fs_e <> -1)
BEGIN
exec('Select * from ' + #Table)
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
END
I am trying to write a stored procedure that will check a table if there are any null values in the table at all. I want this to be able to be called on any table that I ask it to. I'm having a hard time wit the code if anyone could help me please.
Create Procedure NullCheck
#table VarChar(128)
as
Begin
Declare #query Varchar(Max)
set #query = N'WITH xmlnamespaces('http://www.w3.org/2001/XMLSchema-instance' AS ns)
SELECT *
FROM' + QUOTENAME(#table) + 'AS T1
WHERE (
SELECT T1.*
FOR XML PATH' + '('row')' +', ELEMENTS XSINIL, TYPE
).exist' + '(' + '//*/#ns:nil'+ ')' + '= 1'
EXEC #query
END
Try this:
DECLARE #TableName NVARCHAR(128) = '[dbo].[SurveyInstances]'; -- or SYSNAME
DECLARE #DynamicSQLStatement NVARCHAR(MAX);
SET #DynamicSQLStatement = 'SELECT * FROM ' + #TableName + ' WHERE ' +
STUFF
(
(
SELECT ' OR [' + [name] + '] IS NULL'
FROM [sys].[columns]
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,3
,''
);
EXEC sp_executesql #DynamicSQLStatement;
IF EXISTS (SELECT 1 FROM [sys].[objects] WHERE [object_id] = OBJECT_ID(N'[dbo].[usp_GetRowsWithAtLeastOneNULLvalue') AND [type] IN (N'P', N'PC'))
BEGIN
DROP PROCEDURE [dbo].[usp_GetRowsWithAtLeastOneNULLvalue];
END;
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_GetRowsWithAtLeastOneNULLvalue]
(
#TableName NVARCHAR(128)
)
AS
BEGIN;
DECLARE #DynamicSQLStatement NVARCHAR(MAX);
SET #DynamicSQLStatement = 'SELECT * FROM ' + #TableName + ' WHERE ' +
STUFF
(
(
SELECT ' OR [' + [name] + '] IS NULL'
FROM [sys].[columns]
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,3
,''
);
EXEC sp_executesql #DynamicSQLStatement;
END;
Here's my answer. This only checks columns that are nullable:
--/*
create procedure dbo.test_nullCheck (#SchemaName nvarchar(128), #TableName nvarchar(128)) as
begin
--declare #SchemaName nvarchar(128);
--declare #TableName nvarchar(128);
set #SchemaName = isnull(#SchemaName,'dbo')
--set #TableName = '';
declare #sql nvarchar(max);
declare #select nvarchar(max);
declare #from nvarchar(max);
declare #where nvarchar(max);
select #select = 'select * ';
select #from = ' from '+quotename(s.name)+'.'+quotename(o.name)+' '
from sys.objects as o
inner join sys.schemas as s on o.schema_id=s.schema_id
and o.is_ms_shipped = 0
and o.type = 'U'
and o.name = #TableName
and s.name = #SchemaName
select #where = 'where '+
stuff((
select ' or '+quotename(c.name)+' is null'
from sys.columns as c
inner join sys.objects as o on c.object_id = o.object_id
and o.is_ms_shipped = 0
and c.is_nullable = 1
and o.type = 'U'
and o.name = #TableName
inner join sys.schemas as s on o.schema_id=s.schema_id
and s.name = #SchemaName
order by c.column_id
for xml path (''), type).value('.','nvarchar(max)')
, 1,4,'')+ ';'
set #sql = #select + #from + #where
print #sql
if #sql is not null
begin
exec sp_executesql #sql
end;
end;
--*/
exec dbo.test_NullCheck #schemaname = null, #tablename = 'Calendar'
-- does nothing in my database because the schema is ref
exec dbo.test_NullCheck #schemaname = 'ref', #tablename = 'Calendar'
-- returns rows with nulls
exec dbo.test_NullCheck #schemaname = 'ref', #tablename = 'Calendar; Drop Table Calendar; select * from Calendar'
-- does nothing because there isn't a table name like that
I need to store a query as a stored procedure in SQL Server.
I need also to pass parameters which define tablenames and column names.
This is the query I owuld like to have, I tried to store it in a string and then EXECUTE it but without success, ho can I solve this?
CREATE PROCEDURE sp_selectAllParents #id int, #tableid varchar(30), #tablename varchar(30)
AS BEGIN
SET NOCOUNT ON;
WITH ct AS (
SELECT * FROM #tablename t WHERE #tableid = #id
UNION ALL
SELECT t.* FROM #tablename t JOIN ct ON t.parentId = ct.#tableid
)
SELECT * FROM #tablename t WHERE #tableid NOT IN (SELECT #tableid FROM ct)
END
EDIT:
my attempt was:
DECLARE #sql varchar(255)
SET #sql = 'WITH ct AS (SELECT * FROM #tablename t WHERE #tableid = #id UNION ALL SELECT t.* FROM #tablename t JOIN ct ON t.parentId = ct.#tableid) SELECT * FROM #tablename t WHERE #tableid NOT IN (SELECT #tableid FROM ct)'
EXEC(#sql)
As I already in the comment section, it is a bad idea to do this. You should really rethink your solution.
The Stored Procedure would have to look like this:
CREATE PROCEDURE selectAllParents #id int, #tableid sysname, #tablename sysname
AS
BEGIN
SET NOCOUNT ON;
-- Guards against SQL Injection attacks (replace ' with '')
SET #tableid=REPLACE(#tableid,'''','''''');
SET #tablename=REPLACE(#tablename,'''','''''');
DECLARE #stmt NVARCHAR(4000);
SET #stmt=
';WITH ct AS ('+
'SELECT * FROM ' + QUOTENAME(#tablename) + ' t WHERE ' + QUOTENAME(#tableid) + '= #id ' +
'UNION ALL ' +
'SELECT t.* FROM ' + QUOTENAME(#tablename) + ' t JOIN ct ON t.parentId = ct.' + QUOTENAME(#tableid) +
')'+
'SELECT * FROM ' + QUOTENAME(#tablename) +' t WHERE ' + QUOTENAME(#tableid) + ' NOT IN (SELECT ' + QUOTENAME(#tableid) +' FROM ct);';
EXEC sp_executesql
#stmt,
N'#id int',
#id;
END
GO
if your are sending Table name as parameter in that case you need to create dynamic query string. may help below script
CREATE PROCEDURE sp_selectAllParents #id int, #tableid varchar(30), #tablename varchar(30)
AS BEGIN
SET NOCOUNT ON;
SET #query=N'WITH ct AS (
SELECT * FROM #tablename t WHERE #tableid = #id
UNION ALL
SELECT t.* FROM #tablename t JOIN ct ON t.parentId = ct.#tableid
)
SELECT * FROM #tablename t WHERE #tableid NOT IN (SELECT #tableid FROM ct)'
EXECUTE sp_executesql #query, #id,#tablename,#tableid
END
I would like to create the stored procedure and generate insert statement for the table dynamically. The input parameters for the stored procedure are supposed to be schema, table name, #col1, #col2, ..., #colN. This stored procedure is supposed to take 1 random record from another server and based on this record is supposed to generate INSERT statement. #col1, #col2, ..., #colN parameters are optional in case you would like to overwrite original value with the one you need.
The insert record is supposed to look like that:
INSERT INTO schema_name.table_name VALUES (
col1,
col2,
...,
colN)
VALUES (
COALESCE(#col1, 'col1_value'),
COALESCE(#col2, 'col2_value'),
...,
COALESCE(#colN, 'colN_value')
);
Currently I can not realize how to take the real data and put it to the statement. What I already did is:
CREATE PROCEDURE dbo.GenerateSampleDataInsertSP
#SchemaName VARCHAR(255),
#TableName VARCHAR(255)
AS
SET NOCOUNT ON;
DECLARE #sql VARCHAR(MAX) = '',
#columns VARCHAR(MAX) = '',
#columnsWithCoalesce VARCHAR(MAX) = '';
SELECT c.name
INTO #column
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types tt ON c.system_type_id = tt.system_type_id
WHERE t.name = #TableName
AND s.name = #SchemaName
AND tt.name NOT IN ( 'timestamp' );
SET #columns = NULL;
SELECT #columns = ISNULL(#columns + ', ', '') + name
FROM #column;
SET #sql = 'SELECT TOP 1 ' + #columns + ' FROM AnotherDatabase.' + #SchemaName + '.' + #TableName + ' ORDER BY NEWID();';
SET #sql = 'INSERT INTO [' + #SchemaName + '].[' + #TableName + '] (' + #columns + ') VALUES ();';
SELECT #sql;
I do not care about ideal code or solution. I need result and that's it.
UPDATED:
-- Example #1
USE tempdb
GO
/*CREATE PROCEDURE dbo.GenerateSampleDataInsertSP ...*/
CREATE TABLE dbo.Employee (ID INT, EmployeeName VARCHAR(255));
INSERT INTO dbo.Employee VALUES (1, 'John Smith');
EXEC dbo.GenerateSampleDataInsertSP #SchemaName = 'dbo', #TableName = 'Employees';
------------------------ EXPECTED OUTPUT OF THE PROCEDURE (NOT THE ACTION, BUT PLAIN TEXT) ------------------
INSERT INTO dbo.Employee
(
ID,
EmployeeName
)
VALUES
(
COALESCE(#ID, '1'),
COALESCE(#EmployeeName, 'John Smith')
);
-- Example #2
USE tempdb
GO
/*CREATE PROCEDURE dbo.GenerateSampleDataInsertSP ...*/
CREATE TABLE dbo.Orders (ID INT, OrderNbr VARCHAR(10), OrderDate DATE, CustomerID ID);
INSERT INTO dbo.Orders VALUES (7, '12345678', GETDATE(), 1024);
EXEC dbo.GenerateSampleDataInsertSP #SchemaName = 'dbo', #TableName = 'Orders';
------------------------ EXPECTED OUTPUT OF THE PROCEDURE (NOT THE ACTION, BUT PLAIN TEXT) ------------------
INSERT INTO dbo.Orders
(
ID,
OrderNbr,
OrderDate,
CustomerId
)
VALUES
(
COALESCE(#ID, '7'),
COALESCE(#OrderNbr,'12345678'),
COALESCE(#OrderDate, '2015-07-05'),
COALESCE(#CustomerId, '1024')
);
Ok, I'll answer my own question. As I said that I do not care about the code beauty and performance, I just need the result so anyone who would provide more elegant solution would be accepted as solved solution. Here is the code:
CREATE PROCEDURE dbo.GenerateSampleDataInsertSP
#SchemaName VARCHAR(255),
#TableName VARCHAR(255)
AS
SET NOCOUNT ON;
IF EXISTS ( SELECT name
FROM tempdb.sys.tables
WHERE name LIKE '%##record%' )
BEGIN
DROP TABLE ##record;
END;
DECLARE #sql VARCHAR(MAX) = '',
#columns VARCHAR(MAX) = '',
#columnsWithCoalesce VARCHAR(MAX) = '';
SELECT c.name
INTO #column
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types tt ON c.system_type_id = tt.system_type_id
WHERE t.name = #TableName
AND s.name = #SchemaName
AND tt.name NOT IN ( 'timestamp' );
SET #columns = NULL;
SELECT #columns = ISNULL(#columns + ', ', '') + name
FROM #column;
SET #sql = 'SELECT TOP 1 ' + #columns + ' INTO ##record FROM AnotherDataBase.' + #SchemaName + '.' + #TableName + ' ORDER BY NEWID();';
EXEC (#sql);
SET #sql = 'INSERT INTO [' + #SchemaName + '].[' + #TableName + '] (' + #columns + ') VALUES (';
DECLARE #columnsCur CURSOR, #ColumnName VARCHAR(255), #tmpValue VARCHAR(MAX), #sqlCommand nvarchar(1000);
SET #columnsCur = CURSOR FOR
SELECT name
FROM #column;
OPEN #columnsCur;
FETCH NEXT
FROM #columnsCur INTO #ColumnName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlCommand = 'SELECT #value=CAST(' + #ColumnName + ' AS VARCHAR(MAX)) FROM ##record;'
EXECUTE sp_executesql #sqlCommand, N'#value VARCHAR(MAX) OUTPUT', #value=#tmpValue OUTPUT
SET #sql = #sql + 'COALESCE(#'+ #ColumnName +', ''' + #tmpValue + '''),';
FETCH NEXT
FROM #columnsCur INTO #ColumnName;
END;
CLOSE #columnsCur;
DEALLOCATE #columnsCur;
SET #sql = #sql + ');'
SET #sql = REPLACE(#sql, ',);', ');');
SELECT #sql;
GO
How do I select all the columns in a table that only contain NULL values for all the rows? I'm using MS SQL Server 2005. I'm trying to find out which columns are not used in the table so I can delete them.
Here is the sql 2005 or later version: Replace ADDR_Address with your tablename.
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 = 'ADDR_Address'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF NOT EXISTS (SELECT top 1 * FROM ADDR_Address WHERE [' + #col + '] IS NOT NULL) BEGIN print ''' + #col + ''' end'
EXEC(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
SELECT cols
FROM table
WHERE cols IS NULL
This should give you a list of all columns in the table "Person" that has only NULL-values. You will get the results as multiple result-sets, which are either empty or contains the name of a single column. You need to replace "Person" in two places to use it with another table.
DECLARE crs CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM syscolumns WHERE id=OBJECT_ID('Person')
OPEN crs
DECLARE #name sysname
FETCH NEXT FROM crs INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC('SELECT ''' + #name + ''' WHERE NOT EXISTS (SELECT * FROM Person WHERE ' + #name + ' IS NOT NULL)')
FETCH NEXT FROM crs INTO #name
END
CLOSE crs
DEALLOCATE crs
Or did you want to just see if a column only has NULL values (and, thus, is probably unused)?
Further clarification of the question might help.
EDIT:
Ok.. here's some really rough code to get you going...
SET NOCOUNT ON
DECLARE #TableName Varchar(100)
SET #TableName='YourTableName'
CREATE TABLE #NullColumns (ColumnName Varchar(100), OnlyNulls BIT)
INSERT INTO #NullColumns (ColumnName, OnlyNulls) SELECT c.name, 0 FROM syscolumns c INNER JOIN sysobjects o ON c.id = o.id AND o.name = #TableName AND o.xtype = 'U'
DECLARE #DynamicSQL AS Nvarchar(2000)
DECLARE #ColumnName Varchar(100)
DECLARE #RC INT
SELECT TOP 1 #ColumnName = ColumnName FROM #NullColumns WHERE OnlyNulls=0
WHILE ##ROWCOUNT > 0
BEGIN
SET #RC=0
SET #DynamicSQL = 'SELECT TOP 1 1 As HasNonNulls FROM ' + #TableName + ' (nolock) WHERE ''' + #ColumnName + ''' IS NOT NULL'
EXEC sp_executesql #DynamicSQL
set #RC=##rowcount
IF #RC=1
BEGIN
SET #DynamicSQL = 'UPDATE #NullColumns SET OnlyNulls=1 WHERE ColumnName=''' + #ColumnName + ''''
EXEC sp_executesql #DynamicSQL
END
ELSE
BEGIN
SET #DynamicSQL = 'DELETE FROM #NullColumns WHERE ColumnName=''' + #ColumnName+ ''''
EXEC sp_executesql #DynamicSQL
END
SELECT TOP 1 #ColumnName = ColumnName FROM #NullColumns WHERE OnlyNulls=0
END
SELECT * FROM #NullColumns
DROP TABLE #NullColumns
SET NOCOUNT OFF
Yes, there are easier ways, but I have a meeting to go to right now. Good luck!
Here is an updated version of Bryan's query for 2008 and later. It uses INFORMATION_SCHEMA.COLUMNS, adds variables for the table schema and table name. The column data type was added to the output. Including the column data type helps when looking for a column of a particular data type. I didn't added the column widths or anything.
For output the RAISERROR ... WITH NOWAIT is used so text will display immediately instead of all at once (for the most part) at the end like PRINT does.
SET NOCOUNT ON;
DECLARE
#ColumnName sysname
,#DataType nvarchar(128)
,#cmd nvarchar(max)
,#TableSchema nvarchar(128) = 'dbo'
,#TableName sysname = 'TableName';
DECLARE getinfo CURSOR FOR
SELECT
c.COLUMN_NAME
,c.DATA_TYPE
FROM
INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.TABLE_SCHEMA = #TableSchema
AND c.TABLE_NAME = #TableName;
OPEN getinfo;
FETCH NEXT FROM getinfo INTO #ColumnName, #DataType;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = N'IF NOT EXISTS (SELECT * FROM ' + #TableSchema + N'.' + #TableName + N' WHERE [' + #ColumnName + N'] IS NOT NULL) RAISERROR(''' + #ColumnName + N' (' + #DataType + N')'', 0, 0) WITH NOWAIT;';
EXECUTE (#cmd);
FETCH NEXT FROM getinfo INTO #ColumnName, #DataType;
END;
CLOSE getinfo;
DEALLOCATE getinfo;
You can do:
select
count(<columnName>)
from
<tableName>
If the count returns 0 that means that all rows in that column all NULL (or there is no rows at all in the table)
can be changed to
select
case(count(<columnName>)) when 0 then 'Nulls Only' else 'Some Values' end
from
<tableName>
If you want to automate it you can use system tables to iterate the column names in the table you are interested in
If you need to list all rows where all the column values are NULL, then i'd use the COLLATE function. This takes a list of values and returns the first non-null value. If you add all the column names to the list, then use IS NULL, you should get all the rows containing only nulls.
SELECT * FROM MyTable WHERE COLLATE(Col1, Col2, Col3, Col4......) IS NULL
You shouldn't really have any tables with ALL the columns null, as this means you don't have a primary key (not allowed to be null). Not having a primary key is something to be avoided; this breaks the first normal form.
Try this -
DECLARE #table VARCHAR(100) = 'dbo.table'
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql = #sql + 'IF NOT EXISTS(SELECT 1 FROM ' + #table + ' WHERE ' + c.name + ' IS NOT NULL) PRINT ''' + c.name + ''''
FROM sys.objects o
JOIN sys.columns c ON o.[object_id] = c.[object_id]
WHERE o.[type] = 'U'
AND o.[object_id] = OBJECT_ID(#table)
AND c.is_nullable = 1
EXEC(#sql)
Not actually sure about 2005, but 2008 ate it:
USE [DATABASE_NAME] -- !
GO
DECLARE #SQL NVARCHAR(MAX)
DECLARE #TableName VARCHAR(255)
SET #TableName = 'TABLE_NAME' -- !
SELECT #SQL =
(
SELECT
CHAR(10)
+'DELETE FROM ['+t1.TABLE_CATALOG+'].['+t1.TABLE_SCHEMA+'].['+t1.TABLE_NAME+'] WHERE '
+(
SELECT
CASE t2.ORDINAL_POSITION
WHEN (SELECT MIN(t3.ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS t3 WHERE t3.TABLE_NAME=t2.TABLE_NAME) THEN ''
ELSE 'AND '
END
+'['+COLUMN_NAME+'] IS NULL' AS 'data()'
FROM INFORMATION_SCHEMA.COLUMNS t2 WHERE t2.TABLE_NAME=t1.TABLE_NAME FOR XML PATH('')
) AS 'data()'
FROM INFORMATION_SCHEMA.TABLES t1 WHERE t1.TABLE_NAME = #TableName FOR XML PATH('')
)
SELECT #SQL -- EXEC(#SQL)
Here I have created a script for any kind of SQL table. please copy this stored procedure and create this on your Environment and run this stored procedure with your Table.
exec [dbo].[SP_RemoveNullValues] 'Your_Table_Name'
stored procedure
GO
/****** Object: StoredProcedure [dbo].[SP_RemoveNullValues] Script Date: 09/09/2019 11:26:53 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- akila liyanaarachchi
Create procedure [dbo].[SP_RemoveNullValues](#PTableName Varchar(50) ) as
begin
DECLARE Cussor CURSOR FOR
SELECT COLUMN_NAME,TABLE_NAME,DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #PTableName
OPEN Cussor;
Declare #ColumnName Varchar(50)
Declare #TableName Varchar(50)
Declare #DataType Varchar(50)
Declare #Flage int
FETCH NEXT FROM Cussor INTO #ColumnName,#TableName,#DataType
WHILE ##FETCH_STATUS = 0
BEGIN
set #Flage=0
If(#DataType in('bigint','numeric','bit','smallint','decimal','smallmoney','int','tinyint','money','float','real'))
begin
set #Flage=1
end
If(#DataType in('date','atetimeoffset','datetime2','smalldatetime','datetime','time'))
begin
set #Flage=2
end
If(#DataType in('char','varchar','text','nchar','nvarchar','ntext'))
begin
set #Flage=3
end
If(#DataType in('binary','varbinary'))
begin
set #Flage=4
end
DECLARE #SQL VARCHAR(MAX)
if (#Flage in(1,4))
begin
SET #SQL =' update ['+#TableName+'] set ['+#ColumnName+']=0 where ['+#ColumnName+'] is null'
end
if (#Flage =3)
begin
SET #SQL =' update ['+#TableName+'] set ['+#ColumnName+'] = '''' where ['+#ColumnName+'] is null '
end
if (#Flage =2)
begin
SET #SQL =' update ['+#TableName+'] set ['+#ColumnName+'] ='+'''1901-01-01 00:00:00.000'''+' where ['+#ColumnName+'] is null '
end
EXEC(#SQL)
FETCH NEXT FROM Cussor INTO #ColumnName,#TableName,#DataType
END
CLOSE Cussor
DEALLOCATE Cussor
END
You'll have to loop over the set of columns and check each one. You should be able to get a list of all columns with a DESCRIBE table command.
Pseudo-code:
foreach $column ($cols) {
query("SELECT count(*) FROM table WHERE $column IS NOT NULL")
if($result is zero) {
# $column contains only null values"
push #onlyNullColumns, $column;
} else {
# $column contains non-null values
}
}
return #onlyNullColumns;
I know this seems a little counterintuitive but SQL does not provide a native method of selecting columns, only rows.
I would also recommend to search for fields which all have the same value, not just NULL.
That is, for each column in each table do the query:
SELECT COUNT(DISTINCT field) FROM tableName
and concentrate on those which return 1 as a result.
SELECT t.column_name
FROM user_tab_columns t
WHERE t.nullable = 'Y' AND t.table_name = 'table name here' AND t.num_distinct = 0;
An updated version of 'user2466387' version, with an additional small test which can improve performance, because it's useless to test non nullable columns:
AND IS_NULLABLE = 'YES'
The full code:
SET NOCOUNT ON;
DECLARE
#ColumnName sysname
,#DataType nvarchar(128)
,#cmd nvarchar(max)
,#TableSchema nvarchar(128) = 'dbo'
,#TableName sysname = 'TableName';
DECLARE getinfo CURSOR FOR
SELECT
c.COLUMN_NAME
,c.DATA_TYPE
FROM
INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.TABLE_SCHEMA = #TableSchema
AND c.TABLE_NAME = #TableName
AND IS_NULLABLE = 'YES';
OPEN getinfo;
FETCH NEXT FROM getinfo INTO #ColumnName, #DataType;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = N'IF NOT EXISTS (SELECT * FROM ' + #TableSchema + N'.' + #TableName + N' WHERE [' + #ColumnName + N'] IS NOT NULL) RAISERROR(''' + #ColumnName + N' (' + #DataType + N')'', 0, 0) WITH NOWAIT;';
EXECUTE (#cmd);
FETCH NEXT FROM getinfo INTO #ColumnName, #DataType;
END;
CLOSE getinfo;
DEALLOCATE getinfo;
You might need to clarify a bit. What are you really trying to accomplish? If you really want to find out the column names that only contain null values, then you will have to loop through the scheama and do a dynamic query based on that.
I don't know which DBMS you are using, so I'll put some pseudo-code here.
for each col
begin
#cmd = 'if not exists (select * from tablename where ' + col + ' is not null begin print ' + col + ' end'
exec(#cmd)
end