Creating a loop script in SQL - sql

I have been asked to create a loop script in SQL which will pull table name, column name and an operation from a list which I will create. Then loop through each row in the list and create a query. See below my thinking.
Table 1, Column x, Sum;
Table 2, Column z, Sum;
Table 3, Column F, Sum;
Loop
Select operation(ColumnName)
From TableName
End Loop (When there is nothing left in the list)
Am I going along the correct path with this or am I going wrong somewhere? Or is it even possible?
Any help you can offer will be really appreciated

DECLARE #ToSquare INT
DECLARE #Square INT
SET #ToSquare = 0
WHILE #ToSquare < 100
BEGIN
SET #ToSquare = #ToSquare + 1
SET #Square = #ToSquare * #ToSquare
IF #Square > 1000
BREAK
PRINT #Square
END

I think you can using CURSOR.
Some thing like this:
DECLARE #your_Cursor CURSOR
SET #your_Cursor = CURSOR FAST_FORWARD
FOR SELECT ColumnName FROM TableName
OPEN #your_Cursor
FETCH NEXT FROM #your_Cursor
INTO #value
WHILE ##FETCH_STATUS = 0 BEGIN
-- do something
END
CLOSE #your_Cursor
DEALLOCATE #your_Cursor

You don't really need a loop, you can use a simple select statement to generate the queries, for example:
Demo on DB Fiddle
create table query_parts(table_name varchar(100), col_name varchar(100), agg_func varchar(100));
insert into query_parts
select 'Table1', 'Column X', 'Sum';
insert into query_parts
select 'Table2', 'Column Y', 'Avg';
select 'select ' + agg_func + '(' + quotename(col_name) + ') from ' + quotename(table_name) + ';' as query
from query_parts
UPDATE
If you also want to execute the queries, you can use the following example to get started:
Demo on DB Fiddle
create table query_parts(id int identity, table_name varchar(100), col_name varchar(100), agg_func varchar(100), result decimal(12,6));
insert into query_parts (table_name, col_name, agg_func)
select 'Table1', 'Column_X', 'Sum';
insert into query_parts (table_name, col_name, agg_func)
select 'Table1', 'Column_Y', 'Avg';
create table Table1 (Column_X int, Column_Y decimal(12,6))
insert into Table1
select 5, 5;
insert into Table1
select 10, 10;
Loop through the query_parts table and execute queries:
declare #i int;
declare #query nvarchar(1000);
select #i = min(id)
from query_parts;
while (#i is not null)
begin
select #query = ('update query_parts set result = ('
+ ('select ' + agg_func + '(' + quotename(col_name) + ') from ' + quotename(table_name))
+ ') where id = ' + cast(#i as nvarchar))
from query_parts
where id = #i;
exec sp_executesql #query;
select #i = min(id)
from query_parts
where id > #i;
end

Related

How to iterate in SQL Server without while loop

I have a temp table with few table names as below:
Id TableName
-------------
1 TableA
2 TableB
3 TableC
These table names should be replaced in the query shown, and should get executed. I can achieve it with a while loop, but I don't want to use while. Is there any alternative concept?
I have the following SQL statement:
SELECT
TDU.ColHeader, TDU.ColValues
FROM
(SELECT
' + #ColumnsCasted + '
FROM
' + #TableName + ' ' +
#WhereCondition + ') TD
UNPIVOT
(ColValues FOR ColHeader IN (' + #ColumnsUnpivot + ')
) AS TDU;
The #TableName, #ColumnsCasted, #ColumnsUnpivot are based upon the table name which is stored in a temp table. I used while loop to iterate each table name and replace it here in the statement.
Can anyone suggest a method without using while loop?
You may try to generate and execute dynamic SQL. Next example is just for SELECT * FROM Table statement, you must change it for your query:
-- Create table
CREATE TABLE #TempTable (
Id int,
TableName nvarchar(50)
)
INSERT INTO #TempTable (Id, TableName)
VALUES
(1, 'TableA'),
(2, 'TableB'),
(3, 'TableC')
-- Dynamic SQL
DECLARE #stm nvarchar(max)
DECLARE #err int
SET #stm = N''
SELECT #stm =
#stm +
'SELECT * FROM ' +
QUOTENAME(TableName) +
'; '
FROM #TempTable
-- Statement execution
PRINT #stm
EXEC #err = sp_executesql #stm
IF #err <> 0 PRINT 'Error'
ELSE PRINT 'OK'
Generated statement:
SELECT * FROM [TableA]; SELECT * FROM [TableB]; SELECT * FROM [TableC];
There is non-documented function sp_MSforeachtable that executes query for each table in databse, maybe it can help you:
EXEC sp_MSforeachtable 'SELECT COUNT(*) FROM ?'

How to pass table name as parameter in select statement in SQL Sever

Table:
Col
------
Table1
table2
table3
Query:
select count(*)
from #tablename
I wanted to pass table1, table2, table3 as parameters for #tablename in the select query and get the count for each table
Desired output:
2 (table 1 count) 3 (table 2 count) 4 (table 3 count)
you can use dynamic sql and a cursor to run through them:
Create temp table for testing:
DECLARE #tablenametable TABLE(tablename VARCHAR(100));
INSERT INTO #tablenametable
VALUES('table1'), ('table2'), ('table3');
Use a cursor to run through all tablenames in the table
DECLARE #tablename VARCHAR(100);
DECLARE dbcursor CURSOR
FOR
SELECT tablename
FROM #tablenametable;
OPEN dbcursor;
FETCH NEXT FROM dbcursor INTO #tablename;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql VARCHAR(MAX);
SET #sql = 'select count(*) from '+#tablename;
PRINT(#sql);
FETCH NEXT FROM dbcursor INTO #tablename;
END;
CLOSE dbcursor;
DEALLOCATE dbcursor;
Give the following results:
select count(*) from table1
select count(*) from table2
select count(*) from table3
Just change PRINT(#SQL) to EXEC(#SQL) when your happy with it
You can use dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ' union all '
+ 'select cast(count(*) as varchar(100))
+ ' + char(39) + '(' + [Col] +' Count)' + char(39)
+ ' as [table_counts] '
+ ' from ' + [col]
from [your_table_name]
for xml path('')
)
, 1, 11, ''
);
exec(#sql);
Find a demo here
As mentioned here, you have to use dynamic SQL.
First approach is where you specify table name yourself:
declare #tablename varchar(30), #SQL varchar(30)
set #tablename = 'Table1' --here you specify the name
set #SQL = concat('SELECT COUNT(*) FROM ', #tablename) --here you build the query
EXEC(#SQL)
Second approach lets you use table with names of tables:
declare #SQL varchar(8000)
set #SQL = ''
declare #TableNames table(name varchar(30))
insert into #TableNames values ('Table1'), ('Table2'), ('Table3')
--here you build the query
select #SQL = #SQL + ' SELECT ''' + name + ''' AS [TableName], COUNT(*) AS [Count] FROM ' + name + ' UNION ALL' from #TableNames
-- get rid of last "UNION ALL"
set #SQL = LEFT(#SQL, LEN(#SQL) - 10)
--execute the query
EXEC(#SQL)
The result of it will be:
TableName Count
Table1 3
Table2 6
Table3 4
You can use sys.dm_db_partition_stats like this [1]:
select
t.col tableName, sum(s.row_count) tableCount
from
yourTable t
join
sys.dm_db_partition_stats s
on
(object_name(s.object_id) = t.col )
and
(s.index_id < 2)
group by
t.col;
[1]. Related answer
One line output version will be:
select
sum(s.row_count), ' ('+t.col +' count) '
from
yourTable t
join
sys.dm_db_partition_stats s
on
(object_name(s.object_id) = t.col )
and
(s.index_id < 2)
group by
t.col
for xml path('');
output:
2 (Table1 count) 3 (table2 count) 4 (table3 count)

Select columns based on values from any table(number of columns is variable) SQL

I have a following table:
Table 1
And I want to show only columns that have at least one value under 50:
Table 2
I need a stored procedure that does this. The trick is that I want to use it on multiple tables but the number of columns can vary from table to table.
Can this be done?
Wish this will have some help.
SET NOCOUNT ON
DECLARE
#tablename VARCHAR(50) = 'Table1',
#valuetocompare INT = 50,
#otherfields VARCHAR(100) = 'Date, Hour,';
DECLARE #t AS TABLE (cname VARCHAR(10), cvalue INT)
DECLARE #sql NVARCHAR(1000);
DECLARE #cname VARCHAR(128);
DECLARE c CURSOR
FOR
SELECT NAME
FROM sys.[columns] AS c
WHERE c.[object_id] = OBJECT_ID(#tablename)
;
OPEN c;
FETCH NEXT FROM c INTO #cname;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'select ''' + #cname + ''', ' + #cname + ' from ' + #tablename;
INSERT INTO #t
(
cname,
cvalue
)
EXECUTE (#sql);
FETCH NEXT FROM c INTO #cname;
END
CLOSE c;
DEALLOCATE c;
DECLARE #cnames VARCHAR(100) = '';
WITH dcnames AS (
SELECT DISTINCT cname
FROM #t
WHERE cvalue < #valuetocompare
)
SELECT #cnames = #cnames + cname + ','
FROM dcnames;
IF #cnames = ''
PRINT 'No column value is less than ' + CAST(#valuetocompare AS VARCHAR);
ELSE
BEGIN
SET #sql = 'select ' + #otherfields + LEFT(#cnames, LEN(#cnames) - 1) + ' from ' + #tablename;
EXECUTE (#sql);
END

Fill dynamic array with values from an array

I have to execute a similar query on many tables. Instead of writing an n amount of almost similar similar queries, I would like to use a dynamic query. Pseudo:
array = (
'table_a' => 'value_a',
'table_b' => 'value_b',
'table_c' => 'value_c'
);
foreach (array as table => value)
exec(
'select ' + #value + ' into #' + #table + ' from ' + #table
);
end
Is something like this possible in SQL Server 2008 RE? Any help would be greatly appreciated!
You can do this in SQL Server using something like the following script:
CREATE TABLE #temp (id INT IDENTITY(1,1), tablename VARCHAR(50))
INSERT INTO #temp
( tablename )
VALUES ( 'table_a' ),('table_b'),('table_c')
DECLARE #sql NVARCHAR(MAX)
DECLARE #tblcount INT, #i INT = 1
SELECT #tblcount = MAX(id) FROM #temp
WHILE #i <= #tblcount
BEGIN
SELECT #sql = 'SELECT t.somefield, t.otherfield as ' + tablename + ' INTO #some_temptable_' + tablename + ' FROM #mytable t where SomeField like ''1''' FROM #temp WHERE id = #i
EXEC sp_executesql #sql
--SELECT #sql
SELECT #i = #i + 1
END
DROP TABLE #temp

Creating A Script To Replicate A Table And Its Contents?

I know you can create a script to replicate a table using:
right click table > script table as > create to > new query editor window
But how can I generate a script that contains a bunch of insert commands for each row in the table?
Table1
Id1, Row1
Id2, Row2
Id3, Row3
Insert into Table1 values(Row1);
Insert into Table1 values(Row2);
Insert into Table1 values(Row3);
I ended up doing this
right click database > Tasks > Generate Scripts ... > selected the tables > in the advanced options I set "Types of data to script" to "Schema and data"
Select
'Insert into Table (
IntField1
StringField2
Column3)
values (' +
IntField1 + ',' +
+ '''' + StringField2 + ''',' +
Column2 + ')' as InsertQuery
From Table
Something like this, just remember if your string contains a single quote you will need to make sure you replace it like this replace(stringfield, '''', '''''')
So this isnt super pretty cuz I kind of took one of my sp's and hacked it up for this. But basically this will take any table and print a series of insert statements into a table called tbl_text (which you would need to create)
The arguments are the table name and the table ID from sysobjects
--this is how you get the tbl_id
SELECT id FROM sysobjects WHERE type = 'U' AND name = 'tablename'
CREATE PROCEDURE dbo.sp_export_table
#tblhdr varchar(100),
#tblID varchar(100)
AS
SET NOCOUNT ON
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DROP TABLE ##temptable
END
DECLARE #identity bit
DECLARE #typestmt nvarchar(100)
DECLARE #typeval int
DECLARE #rowstmt nvarchar(1000)
DECLARE #rowID varchar(50)
DECLARE #orderby nvarchar(100)
DECLARE #clmnstmt varchar(200)
DECLARE #clmnhdr varchar(50)
DECLARE #clmnstring varchar(1000)
DECLARE #valuestmt nvarchar(200)
DECLARE #valuestring nvarchar(3000)
DECLARE #value nvarchar(1000)
DECLARE #insertstmt varchar(1000)
DECLARE #params nvarchar(100)
DECLARE #param2 nvarchar(100)
SELECT #rowstmt = N'SELECT TOP 1 #inside_var = name FROM syscolumns WHERE id = ' + #tblID + ' ORDER BY colorder'
SELECT #params = N'#inside_var NVARCHAR(1000) OUTPUT'
EXEC sp_executesql #rowstmt, #params, #inside_var = #orderby OUTPUT
SELECT #rowstmt = 'SELECT *, ROW_NUMBER() OVER (ORDER BY ' + #orderby + ') AS row INTO ##temptable FROM ' + #tblhdr
exec(#rowstmt)
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DECLARE row_cursor CURSOR FOR
SELECT row FROM ##temptable
OPEN row_cursor
FETCH NEXT FROM row_cursor
INTO #rowID
--if table has identity and has records write identity_insert on
SET #identity = 0
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE OBJECTPROPERTY(OBJECT_ID(TABLE_NAME),
'TableHasIdentity') = 1 AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME = #tblhdr) AND EXISTS(SELECT * FROM ##temptable)
BEGIN
SET #identity = 1
INSERT INTO dbo.tbl_text VALUES('SET IDENTITY_INSERT dbo.' + #tblhdr + ' ON')
END
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #clmnstmt = 'DECLARE column_cursor CURSOR FOR SELECT name FROM syscolumns WHERE id = ' + #tblID + ' ORDER BY colorder'
exec(#clmnstmt)
OPEN column_cursor
FETCH NEXT FROM column_cursor
INTO #clmnhdr
SELECT #clmnstring = '('
SELECT #valuestring = '('
WHILE ##FETCH_STATUS = 0
BEGIN
IF #clmnhdr <> 'row'
BEGIN
SELECT #clmnstring = #clmnstring + #clmnhdr + ','
SELECT #valuestmt = N'SELECT #inside_var = ' + #clmnhdr + ' FROM ##temptable WHERE row = ' + #rowID
EXEC sp_executesql #valuestmt, #params, #inside_var = #value OUTPUT
SELECT #typestmt = N'SELECT #inside_var2 = xtype FROM syscolumns WHERE name = ''' + #clmnhdr + ''' AND id = ' + #tblID
SELECT #param2 = N'#inside_var2 INT OUTPUT'
EXEC sp_executesql #typestmt, #param2, #inside_var2 = #typeval OUTPUT
IF #typeval NOT IN (48,52,56,59,60,62,104,108,122,127)
BEGIN
SET #value = REPLACE(#value,'''','''''')
SET #value = '''' + #value + ''''
SET #value = ISNULL(#value, '''''')
END
IF NOT (#typeval = 34)
BEGIN
SELECT #valuestring = #valuestring + #value + ','
END
ELSE
BEGIN
SELECT #valuestring = #valuestring + '''''' + ','
END
END
FETCH NEXT FROM column_cursor
INTO #clmnhdr
END
SET #clmnstring = LEFT(#clmnstring, LEN(#clmnstring) - 1)
SET #valuestring = LEFT(#valuestring, LEN(#valuestring) - 1)
INSERT INTO dbo.tbl_text VALUES('INSERT INTO dbo.' + #tblhdr + ' ' + #clmnstring + ') VALUES' + #valuestring + ')')
FETCH NEXT FROM row_cursor
INTO #rowID
CLOSE column_cursor
DEALLOCATE column_cursor
END
--if it wrote identity_insert on, turn it off
IF (#identity = 1)
BEGIN
INSERT INTO dbo.tbl_text VALUES('SET IDENTITY_INSERT dbo.' + #tblhdr + ' OFF')
END
CLOSE row_cursor
DEALLOCATE row_cursor
END
IF object_id('tempdb..##temptable') IS NOT NULL
BEGIN
DROP TABLE ##temptable
END
GO
If you've got an account on SSC, you can use the script I published last year. It works without cursors an it enables custom filtering.
http://www.sqlservercentral.com/scripts/Script+Data/65998/
Hope this helps
Assuming Row is an INT NOT NULL. You could write a SELECT statement that outputs SQL;
SELECT N'INSERT INTO Table1 VALUES (' + CAST(Row AS NVARCHAR(10)) + N');'
FROM Table1
Then output your results to text.