I have 64 columns and I am trying to automate the loop process. The loop runs but it shows 0 affected rows. If I update the table, column by column, it works.
Any idea why its showing 0 affected rows and what can be done ?
update temp set col1 = 'C' where col1 IS Null; -- works (276 rows affected)--
declare #count as int;
declare #name as varchar(max);
set #count = 2;
while #count < (SELECT Count(*) FROM INFORMATION_SCHEMA.Columns where TABLE_NAME = 'temp')+1
Begin
Set #name = (select name from (select colorder, name from (SELECT *
FROM syscolumns WHERE id=OBJECT_ID('temp')) colnames) as cl where colorder = #count)
Print #name
update temp set #name = 'C' where #name IS Null;
SET #count = #count + 1;
END;
You need to use dynamic sql to update the different columns during runtime as below.
Note: I just added/modified the dynamic sql part.
declare #count as int;
declare #name as varchar(max)
declare #sql nvarchar (1000)
set #count = 2
while #count < (SELECT Count(*) FROM INFORMATION_SCHEMA.Columns where TABLE_NAME = 'temp')+1
Begin
Set #name = (select name from (select colorder, name from (SELECT *
FROM syscolumns WHERE id=OBJECT_ID('temp')) colnames) as cl where colorder = #count)
Print #name
set #sql = N'update temp set ' + #name + '= ''C'' where ' + #name + ' is null ';
exec sp_executesql #sql
SET #count = #count + 1
END;
Related
My SQL Query working fine with "EXEC(Query)"
I added "SELECT * INTO #TempData" code before "EXEC(Query)" to insert all the records into a single table instead of multiple separate table records. but getting that error. I even added Aliases but still getting that error. Please suggest. Thanks!
With Temp AS
(SELECT companyID,DBName, ROW_NUMBER()over(order by dbname) as [Rownumber]
from CentralHub.dbo.Company where companyID in (16,66,101,196,94,210,215,26,37,194,179))
SELECT * INTO #temp FROM Temp
Declare #Count INT = 1;
Declare #Query Varchar(MAX);
Declare #Dbname Varchar(MAX);
Declare #ID INT;
WHILE(#Count <= (SELECT count(companyID) from #temp))
BEGIN
SET #Dbname = (SELECT dbname from #temp where Rownumber = #Count);
SET #ID = (SELECT companyID from #temp where Rownumber = #Count);
SET #Query = '
USE [Db]
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = ''dbo''
AND TABLE_NAME = ''RQSalesFlatten''))
BEGIN
IF(EXISTS(SELECT top 1 1 FROM RQSalesFlatten ))
BEGIN
SELECT [ID] AS [CompanyID],''[Db]'' AS [DbName], max(rqDate) AS [LastSalesDate] FROM RQSalesFlatten
END
ELSE
SELECT [ID] AS [CompanyID],''[Db]'' AS [DbName], max(invoiceDate) AS [LastSalesDate] FROM Sales_Flatten
END
ELSE
BEGIN
SELECT [ID]AS [CompanyID],''[Db]'' AS [DbName], max(invoiceDate)AS [LastSalesDate] FROM Sales_Flatten;
END
';
SET #Query = Replace(#Query,'[Db]',#Dbname);
SET #Query = Replace(#Query,'[ID]',#ID);
SELECT * INTO #TempData EXEC(#Query);
SET #Count = #Count+1;
END
This is the table creation and insertion query
If not exists(select * from sysobjects where name='hrs')
Create table hrs(hr int)
declare #cnt int =1
while #cnt <= 12
begin
insert into hrs values(#cnt)
set #cnt=#cnt+1
end
The above code gives the output like
but I just want that
declare #cnt1 int = 1
while #cnt1<=12
begin
EXEC('select he'+#cnt1+' = case when hr = 1 then '+#cnt1+' end from hrs')
set #cnt1=#cnt1+1
end
The above code returns the 12 different table but i just want the all records in one table (without creating any new table).
So, how can i do this?
Please help me.
Thanks.
Here the all column are created dynamically through loop
Here are the full query
declare #s varchar(MAX)=''
declare #j int = 1
while #j<=12
begin
if #j = 12
Set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end)'
else
set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end),'
set #j=#j+1
end
set #s = 'select '+#s+' from hrs'
exec(#s)
Your query doesn't make a lot of sense, but you can build a list of columns and then exec that:
declare #columns nvarchar(max)
declare #cnt int = 1
while #cnt <= 12
begin
set #columns = isnull(#columns + ', ', '') + 'He' + cast(#cnt as nvarchar) +
' = sum(case when hr = ' + cast(#cnt as nvarchar) + ' then hr end)'
end
declare #sql nvarchar(max) = 'select ' + #columns ' + from hr'
exec (#sql)
How do I find out how many tables are on my instance of SQL Server? I can get it for a single schema using select count(*) from sysobjects where type = 'U'
(from how to count number of tables/views/index in my database)
You're using the word "schema", but I think you're really asking to count tables across all "databases".
declare #t table (
DBName sysname,
NumTables int
)
insert into #t
exec sp_MSforeachdb N'select ''?'', count(*)
from [?].dbo.sysobjects
where type = ''U'''
select DBName, NumTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
order by DBName
select SUM(NumTables) as TotalTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
An option without using the hidden, undocumented sp_MSforeachdb
declare #sql nvarchar(max)
select #sql = coalesce(#sql + ' + ', '') + REPLACE('
(select count(*)
from ::DB::.sys.objects
where is_ms_shipped = 0
and type_desc = ''USER_TABLE'')', '::DB::', QUOTENAME(name))
from master.sys.databases
where owner_sid != 0x01
select #sql = 'select ' + #sql
exec (#sql) -- returns a single count of all [user] tables in the instance
>
A note on performance. It is insignificant in the greater scheme of things, but with all things interesting, someone is bound to time it. Here is a comparison of the ms_foreachdb approach passing through a temp table (it internally uses a cursor) against the string-concat method.
-- all the variables that we will use
declare #i int -- loop variable
declare #sql nvarchar(max) -- statement var used for 1st approach
declare #t table (DBName sysname, NumTables int) -- table used for 2nd approach
-- init plan cache and buffers
dbcc freeproccache dbcc dropcleanbuffers
print convert(varchar(30), getdate(), 121)
set #i = 0 while #i < 5 begin
set #sql = null
select #sql = coalesce(#sql, '') + REPLACE('
select #c = #c + count(*)
from ::DB::.sys.objects
where is_ms_shipped = 0
and type_desc = ''USER_TABLE''', '::DB::', QUOTENAME(name))
from master.sys.databases
where owner_sid != 0x01
select #sql = 'set nocount on declare #c int set #c = 0 ' + #sql + ' select #c'
exec (#sql)
-- clear plan cache and buffers after each run
dbcc freeproccache dbcc dropcleanbuffers set #i = #i + 1
end
print convert(varchar(30), getdate(), 121)
set #i = 0 while #i < 5 begin
insert into #t
exec sp_MSforeachdb N'select ''?'', count(*)
from [?].dbo.sysobjects
where type = ''U'''
select SUM(NumTables) as TotalTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
-- unfortunately this is required
delete from #t
-- clear plan cache and buffers after each run
dbcc freeproccache dbcc dropcleanbuffers set #i = #i + 1
end
print convert(varchar(30), getdate(), 121)
The result obtained for only 5 invocations (loop iterations) of each. YMMV
start : 2011-01-21 14:21:45.180
end of string-concat : 2011-01-21 14:21:57.497 (12.317)
end of sp_msforeachdb : 2011-01-21 14:22:13.937 (16.440)
It has to be noted that the temp table has to be emptied between each iteration of the 2nd approach, so that could contribute to the total time. It should have been insignificant though
Here is an answer that does not use undocumented functions and works in SQL Server 2005, 2008 and 2008R2. This answer can be used with minor modifications to run any statement across databases.
DECLARE #sql varchar(200), #dbname sysname, #dbid smallint;
CREATE table #alltables
(dbname sysname,
[number of tables] int);
SELECT top 1 #dbname = name, #dbid = database_id
FROM sys.databases
where database_id > 4;
WHILE (#dbname is not null)
begin
-- the statement below could contain any valid select statement
set #sql = 'use ' + #dbname + '; insert into #alltables select ''' + #dbname + ''', count(*) from sys.tables';
EXEC (#sql)
set #dbname = null;
SELECT top 1 #dbname = name, #dbid = database_id
FROM sys.databases
where database_id > #dbid;
end;
select * FROM #alltables;
SELECT sum([number of tables]) "Total Number of Tables in all user databases" from #alltables;
drop table #alltables;
Select Count(*)
From INFORMATION_SCHEMA.TABLES
Where TABLE_TYPE = 'BASE TABLE'
If what you are seeking is a way to determine how many tables exist across all databases on a given SQL Server instance, then you need to cycle through each database. One way would be:
Declare #Databases Cursor
Declare #DbName as nvarchar(64)
Declare #SQL nvarchar(max)
Declare #BaseSQL nvarchar(max)
Declare #Count int
Declare #TotalCount int
Set #Databases = Cursor Fast_Forward For
select [name]
from master..sysdatabases
where [name] Not In('master','model','msdb','tempdb')
Open #Databases
Fetch Next From #Databases Into #DbName
Set #BaseSQL = 'Select #Count = Count(*)
From DatabaseName.INFORMATION_SCHEMA.TABLES
Where TABLE_TYPE = ''BASE TABLE'''
Set #TotalCount = 0
While ##Fetch_Status = 0
Begin
Set #Count = 0
Set #SQL = Replace(#BaseSQL, 'DatabaseName', QuoteName(#DbName))
exec sp_executesql #SQL, N'#Count int OUTPUT', #Count OUTPUT
Set #TotalCount = #TotalCount + #Count
Fetch Next From #Databases Into #DbName
End
Close #Databases
Deallocate #Databases
Select #TotalCount
This solution has the advantage of not using any undocumented features such as sp_MSforeachdb however it is obviously more verbose.
We have a database setup that consists of two parts: a static structure, and dynamic additions. For each database, the dynamic can be different, and sometimes we don't have data for all the dynamic fields. Rigt now, we check for empties by looking at the total count of records in the entire table, but we want to move to a more refined method of checking for empties if possible. Is it possible to quickly check through several hundred fields and see which ones are empty and which ones are populated?
For searching for any rows that have NULLS in any column you can do this, first create this proc which is based on the code here Search all columns in all the tables in a database for a specific value
CREATE PROCEDURE FindMyData_StringNull
#DataToFind NVARCHAR(4000),
#ExactMatch BIT = 0
AS
SET NOCOUNT ON
DECLARE #Temp TABLE(RowId INT IDENTITY(1,1), SchemaName sysname, TableName sysname, ColumnName SysName, DataType VARCHAR(100), DataFound BIT)
INSERT INTO #Temp(TableName,SchemaName, ColumnName, DataType)
SELECT C.Table_Name,C.TABLE_SCHEMA, C.Column_Name, C.Data_Type
FROM Information_Schema.Columns AS C
INNER Join Information_Schema.Tables AS T
ON C.Table_Name = T.Table_Name
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
WHERE Table_Type = 'Base Table'
DECLARE #i INT
DECLARE #MAX INT
DECLARE #TableName sysname
DECLARE #ColumnName sysname
DECLARE #SchemaName sysname
DECLARE #SQL NVARCHAR(4000)
DECLARE #PARAMETERS NVARCHAR(4000)
DECLARE #DataExists BIT
DECLARE #SQLTemplate NVARCHAR(4000)
SELECT #SQLTemplate = 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
IS NULL
)
Set #DataExists = 1
Else
Set #DataExists = 0'
,
#PARAMETERS = '#DataExists Bit OUTPUT',
#i = 1
SELECT #i = 1, #MAX = MAX(RowId)
FROM #Temp
WHILE #i <= #MAX
BEGIN
SELECT #SQL = REPLACE(REPLACE(#SQLTemplate, 'ReplaceTableName', QUOTENAME(SchemaName) + '.' + QUOTENAME(TableName)), 'ReplaceColumnName', ColumnName)
FROM #Temp
WHERE RowId = #i
PRINT #SQL
EXEC SP_EXECUTESQL #SQL, #PARAMETERS, #DataExists = #DataExists OUTPUT
IF #DataExists =1
UPDATE #Temp SET DataFound = 1 WHERE RowId = #i
SET #i = #i + 1
END
SELECT SchemaName,TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
Call it like this
FindMyData_StringNull NULL,1
Assuming that you are just checking for whether or not there are any non-NULL values in the column, using EXISTS should generally be faster than getting a COUNT(*). The COUNT needs to scan the whole table to come up with the correct number. EXISTS just needs to find one row that satisfies the condition before it stops looking.
If the whole column is NULL then the time will be about the same, but in all of those cases where you have values it could be substantially shorter.
From Search all columns in all the tables in a database for a specific value
first create this function
CREATE PROCEDURE FindMyData_String
#DataToFind NVARCHAR(4000),
#ExactMatch BIT = 0
AS
SET NOCOUNT ON
DECLARE #Temp TABLE(RowId INT IDENTITY(1,1), SchemaName sysname, TableName sysname, ColumnName SysName, DataType VARCHAR(100), DataFound BIT)
INSERT INTO #Temp(TableName,SchemaName, ColumnName, DataType)
SELECT C.Table_Name,C.TABLE_SCHEMA, C.Column_Name, C.Data_Type
FROM Information_Schema.Columns AS C
INNER Join Information_Schema.Tables AS T
ON C.Table_Name = T.Table_Name
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
WHERE Table_Type = 'Base Table'
And Data_Type In ('ntext','text','nvarchar','nchar','varchar','char')
DECLARE #i INT
DECLARE #MAX INT
DECLARE #TableName sysname
DECLARE #ColumnName sysname
DECLARE #SchemaName sysname
DECLARE #SQL NVARCHAR(4000)
DECLARE #PARAMETERS NVARCHAR(4000)
DECLARE #DataExists BIT
DECLARE #SQLTemplate NVARCHAR(4000)
SELECT #SQLTemplate = CASE WHEN #ExactMatch = 1
THEN 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
= ''' + #DataToFind + '''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
ELSE 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
Like ''%' + #DataToFind + '%''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
END,
#PARAMETERS = '#DataExists Bit OUTPUT',
#i = 1
SELECT #i = 1, #MAX = MAX(RowId)
FROM #Temp
WHILE #i <= #MAX
BEGIN
SELECT #SQL = REPLACE(REPLACE(#SQLTemplate, 'ReplaceTableName', QUOTENAME(SchemaName) + '.' + QUOTENAME(TableName)), 'ReplaceColumnName', ColumnName)
FROM #Temp
WHERE RowId = #i
PRINT #SQL
EXEC SP_EXECUTESQL #SQL, #PARAMETERS, #DataExists = #DataExists OUTPUT
IF #DataExists =1
UPDATE #Temp SET DataFound = 1 WHERE RowId = #i
SET #i = #i + 1
END
SELECT SchemaName,TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
Now call it like this for rows with empty strings in any string type columns
exec FindMyData_String '',1
it will give you an output with column name, table name and schema name
Just keep in mind that it will search all tables
I would think the simplest solution is to use the CHECKSUM function. First you would want to determine the checksum on an empty row and then compare that to the other rows.
Select Checksum(*)
From Table
The catch with using * here is that it will include the PK. You would likely have to specify the individual columns excluding the PK to get an accurate read. So something like:
Select Checksum(Col1, Col2, Col3)
From Table
Checksum Function.
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