Replacing cursor with set based query - sql-server

I want to find count of set of tables. The table names are values in another table.
--like
select count(*) from tablename
--tablename is obtained from
select tablename from table1
--table1 has around 171 tablenames
I was using cursor to get each table name and select count for each, but it is taking time. Can you please help how to replace cursor code with set based solution?
Below is my cursor code
SET NOCOUNT ON;
if( OBJECT_ID('tempdb..#temptablenew') IS NOT NULL )
BEGIN
DROP table #temptablenew
END
select * into #temptablenew from table1
declare #srccount int
declare #tablename nvarchar(max);
declare #q2 nvarchar(max);
declare #id int;
declare my_cursor cursor
local static read_only forward_only
for
select id,tablename from #temptablenew
open my_cursor
fetch next from my_cursor
into #id,#tablename
while ##fetch_status = 0
begin
set #q2 =N'select #srccount= count(*) from '+#tablename+' with (nolock)';
execute sp_executesql #q2,#PARAMS = N'#srccount INT OUTPUT',
#srccount = #srccount OUTPUT
select #srccount,#id,#tablename
fetch next from my_cursor
into #id,#tablename
end
close my_cursor;
deallocate my_cursor;
Thanks in advance

Try this,
SET NOCOUNT ON;
declare #q2 nvarchar(max) = '';
SELECT #q2 = #q2 + 'SELECT COUNT(*) AS cnt, ''' + tablename + ''' AS TableName FROM ' + tablename + ' (NOLOCK); '
FROM table1
--Print #q2
execute sp_executesql #q2

Related

Don't know how to use varchar variable in select clause of query

I''m trying to use a varchar variable in my select, but its registering as a string and outputting just the string itself instead of treating it like an actual column.
Not very many solutions online, as it seems like this isn't a problem very many run into.
Declare #counter INT = 0
Declare #totalcol INT
Declare #col VARCHAR(50)
select #totalcol = count(*)
FROM [Loyalty_DW].information_schema.columns
WHERE table_name = 'Transactions'
while (#counter < #totalcol)
begin
select #col = COLUMN_NAME
from [Loyalty_DW].information_schema.columns
where table_name = 'Transactions'
order by (select null)
offset #counter rows
fetch next 1 rows only
select distinct(#col)
from [Loyalty_DW].dbo.Transactions
set #counter += 1
end
The output is just a string with no actual data returned. The same as if I were to say select 'asdf' from tablename where ... it would just output 'asdf'.
You can use CURSOR statement rather than WHILE statement, as Gordon says you need to use dynamic SQL:
DECLARE #columName AS NVARCHAR(50);
DECLARE #sqlText AS NVARCHAR(1000);
DECLARE cursor_name CURSOR FOR
SELECT COLUMN_NAME
FROM [Loyalty_DW].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Transactions'
ORDER BY(SELECT NULL)
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #columName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlText = N'SELECT DISTINCT ' + #columName + ' FROM [Loyalty_DW].dbo.Transactions'
EXEC (#sqlText)
FETCH NEXT FROM cursor_name INTO #columName
END
CLOSE cursor_name
DEALLOCATE cursor_name

i use two cursor for this. but i want to minimize it to one cursor

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

Pass Variable in SELECT FROM Loop

I'm looking to select from multiple tables (MainTbl) but it will be based on the result set (StateTbl) of which tables would be pulled.
MainTables dbo.TABLE_MO, dbo.TABLE_CA, dbo.TABLE_AL, dbo.TABLE_MI
Only looking to pull based on resultset StateTbl MO, CA, WA
Declare #Loop_Count int = 0
DECLARE #State varchar(2)
DECLARE #SQL varchar(max)
DECLARE db_cursor CURSOR FOR SELECT State FROM StateTbl
OPEN db_cursor
FETCH db_cursor INTO #State
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #SQL =
'
dbo.TABLE_'+ #State +'
'
EXEC(#SQL)
SET #Loop_Count = #Loop_Count + 1
FETCH db_cursor INTO #SQL
END
CLOSE db_cursor
DEALLOCATE db_cursor
Instead of a loop you can leverage dynamic and the StateTbl to build your dynamic sql. Something like this.
declare #SQL nvarchar(max) = ''
select 'select * from TABLE_' + [State] + ' UNION ALL '
from StateTbl
select #SQL = left(#SQL, len(#SQL) - 10)
select #SQL
--uncomment the line below when you satisfied the dynamic sql is written the way you want it.
--exec sp_executesql #SQL

How to get return value from EXEC sp_executesql

How do I get the return value from EXEC sp_executesql #OpenQry so I can check if the value exists in IF EXISTS?
DECLARE #TableName VARCHAR(25)
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC sp_executesql #OpenQry
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName)
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
you just need put the variable that you want receive de return value before sp_executesql
try tis code and let me know if works
DECLARE #TableName VARCHAR(MAX)
DECLARE #TableNameToDrop VARCHAR(MAX) --NEW
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE #OpenQry NVARCHAR(MAX) -- Dont forget to declare this variable
declare #sqldrop nvarchar(max)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC TableNameToDrop = sp_executesql #OpenQry
/*
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName )
*/
--maybe you dont need check if the table name existis, jut try this
IF EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableNameToDrop )
BEGIN
set #sqldrop = 'drop table '+ #TableNameToDrop
EXEC sp_executesql #sqldrop
END
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
Regards

Error in Declaration of cursor

set #SQL=N' select #minTableId = MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
Declare Cursor For
EXEC SP_EXECUTESQL #SQL
if i have declared all the variables in above query but Declaration of cursor in above
query shows ERROR.
What is Solution?
In order to execute a cursor over dynamic SQL you must put the output of your dynamic sql into a temporary table and then cursor over the temporary table like this:
DECLARE #TableName NVARCHAR(100)
DECLARE #SQL NVARCHAR(1000)
CREATE TABLE #TempTABLE(email NVARCHAR(200))
SET #TableName='Users'
SELECT #SQL='INSERT INTO #TempTable SELECT email FROM ' + #TableName
EXEC (#SQL)
DECLARE MyCursor CURSOR FOR SELECT * FROM #TempTable
OPEN MyCursor
DECLARE #Email NVARCHAR(200)
FETCH NEXT FROM MyCursor INTO #Email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Email = ' + #Email
FETCH NEXT FROM MyCursor INTO #Email
END
CLOSE MyCursor
DEALLOCATE MyCursor
DROP TABLE #TempTABLE
I dont think you need a cursor for this
try
DECLARE #AcDB VARCHAR(10),
#Sql NVARCHAR(MAX)
set #SQL=N' select MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
DECLARE #Temp TABLE(
MinID INT
)
INSERT INTO #Temp EXEC SP_EXECUTESQL #SQL
DECLARE #minTableId INT
SELECT TOP 1 #minTableId = MinID FROM #Temp
SELECT #minTableId
EDIT: Also here is the actual CURSOR documentation
DECLARE CURSOR (Transact-SQL)

Resources