Fill dynamic array with values from an array - sql

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

Related

Creating a loop script in 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

how to find all strings in between commas in sql server

I want to display a string in a table format as shown below:
For a string like 'hi,is,1,question,thanks,.,.,n'
I need this result:
column1 column2 column3 column4 ..... column
hi is 1 question ..... n
DECLARE #string VARCHAR(MAX);
SET #string = 'hi,is,1,question,thanks,.,.,n';
DECLARE #SQL VARCHAR(MAX);
SET #SQL = 'SELECT ''' + REPLACE(#string, ',', ''',''') + '''';
EXEC (#SQL);
Result:
Add SELECT ' at beginning and ' at the end of string
Replace all , with ',' inside string
So string 'hi,is,1,question,thanks,.,.,n' is replace by 'SELECT 'hi','is','1','question','thanks','.','.','n''
Executed as SQL query
PS: If you want to use it on column you will have to combine it with CURSOR
Update
DECLARE #table TABLE
(
ID INT IDENTITY,
string VARCHAR(MAX)
);
INSERT INTO #table
VALUES
('This,is,a,string,,n,elements,..');
INSERT INTO #table
VALUES
('And,one,more');
INSERT INTO #table
VALUES
('Ugly,but,works,,,Yay!,..,,,10,11,12,13,14,15,16,17,18,19,..');
SELECT * FROM #table
DECLARE #string_to_split VARCHAR(MAX);
DECLARE #sql_query_to_execute VARCHAR(MAX);
DECLARE #max_elements INT, #id INT, #i INT;
SET #i = 1;
DECLARE string_cursor CURSOR FOR SELECT ID, string FROM #table;
SELECT #max_elements = MAX(LEN(string) - LEN(REPLACE(string, ',', ''))) + 1 -- Find max number of elements */
FROM #table;
IF OBJECT_ID('tempdb..##my_temp_table_for_splitted_columns') <> 0 -- Create new temp table with valid amount of columns
DROP TABLE ##my_temp_table_for_splited_columns;
SET #sql_query_to_execute = 'create table ##my_temp_table_for_splitted_columns ( ID int,';
WHILE #i <= #max_elements
BEGIN
SET #sql_query_to_execute = #sql_query_to_execute + ' Col' + CAST(#i AS VARCHAR(max)) + ' varchar(25), ';
SET #i = #i + 1;
END;
SELECT #sql_query_to_execute = SUBSTRING(#sql_query_to_execute, 1, LEN(#sql_query_to_execute) - 1) + ')';
EXEC (#sql_query_to_execute);
/* Split string for each row */
OPEN string_cursor;
FETCH NEXT FROM string_cursor
INTO #id,
#string_to_split
WHILE ##FETCH_STATUS = 0
BEGIN
SET #i = MAX(LEN(#string_to_split) - LEN(REPLACE(#string_to_split, ',', ''))) + 1; -- check amount of columns for current string
WHILE #i < #max_elements
BEGIN
SET #string_to_split = #string_to_split + ','; -- add missing columns
SET #i = #i + 1;
END;
SET #sql_query_to_execute = 'SELECT ' + CAST(#id AS VARCHAR(MAX)) + ',''' + REPLACE(#string_to_split, ',', ''',''') + '''';
INSERT INTO ##my_temp_table_for_splitted_columns --insert result to temp table
EXEC (#sql_query_to_execute);
FETCH NEXT FROM string_cursor
INTO #id,
#string_to_split;
END;
CLOSE string_cursor;
DEALLOCATE string_cursor;
SELECT *
FROM ##my_temp_table_for_splitted_columns;
This is not trivial. You will find a lot of examples how to split your string in a set of fragments. And you will find a lot of examples how to pivot a row set to a single row. But - adding quite some difficulty - you have an unknown count of columns. There are three approaches:
Split this and return your set with a known maximum of columns
Use a dynamically created statement and use EXEC. But this will not work in VIEWs or iTVFs, nor will it work against a table.
Instead of a column list you return a generic container like XML
with a known maximum of columns
One example for the first was this
DECLARE #str VARCHAR(1000)='This,is,a,string,with,n,elements,...';
SELECT p.*
FROM
(
SELECT A.[value]
,CONCAT('Column',A.[key]+1) AS ColumnName
FROM OPENJSON('["' + REPLACE(#str,',','","') + '"]') A
) t
PIVOT
(
MAX(t.[value]) FOR ColumnName IN(Column1,Column2,Column3,Column4,Column5,Column6,Column7,Column8,Column9 /*add as many as you need*/)
) p
Hint: My approach to split the string uses OPENJSON, not available before version 2016. But there are many other approaches you'll find easily. It's just an example to show you the combination of a splitter with PIVOT using a running index to build up a column name.
Unknown count of columns
And the same example with a dynamically created column list was this:
DECLARE #str VARCHAR(1000)='This,is,a,string,with,n,elements,...';
DECLARE #CountElements INT=LEN(#str)-LEN(REPLACE(#str,',',''))+1;
DECLARE #columnList NVARCHAR(MAX)=
STUFF((
SELECT TOP(#CountElements)
CONCAT(',Column',ROW_NUMBER() OVER(ORDER BY (SELECT 1)))
FROM master..spt_values /*has a lot of rows*/
FOR XML PATH('')
),1,1,'');
DECLARE #Command NVARCHAR(MAX)=
N'SELECT p.*
FROM
(
SELECT A.[value]
,CONCAT(''Column'',A.[key]+1) AS ColumnName
FROM OPENJSON(''["'' + REPLACE(''' + #str + ''','','',''","'') + ''"]'') A
) t
PIVOT
(
MAX(t.[value]) FOR ColumnName IN(' + #columnList + ')
) p;';
EXEC(#Command);
Hint: The statement created is exactly the same as above. But the column list in the pivot's IN is created dynamically. This will work with (almost) any count of words generically.
If you need more help, please use the edit option of your question and provide some more details.
An inlineable approach for a table returning a generic container
If you need this against a table, you might try something along this:
DECLARE #tbl TABLE(ID INT IDENTITY,YourList NVARCHAR(MAX));
INSERT INTO #tbl VALUES('This,is,a,string,with,n,elements,...')
,('And,one,more');
SELECT *
,CAST('<x>' + REPLACE((SELECT t.YourList AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML) AS Splitted
FROM #tbl t
This will return your list as an XML like
<x>This</x>
<x>is</x>
<x>a</x>
<x>string</x>
<x>with</x>
<x>n</x>
<x>elements</x>
<x>...</x>
You can grab - if needed - each element by its index like here
TheXml.value('/x[1]','nvarchar(max)') AS Element1

How to convert Table data to JSON value in SQL Server using column values as nodes?

Hi I'm newbie to JSON process in SQL server.
Here is my table:
DECLARE #Example TABLE
(
ThirdPartyInterfaceData VARCHAR(10)
,ThirdPartyInterfaceName VARCHAR(10)
,Name VARCHAR(512)
,Value VARCHAR(800)
)
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthID','MAMDQ1ODNJN2JMMZMWZD')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthToken','YWU5NTlhNzgxYTA1OWY4NTFkMTM4NWY4ZjM5Y2Zl')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSFrom','Venue Metro')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURL','http://www.google.com')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURLMethod','POST')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSRestAPIVersion','v1')
I want to convert my table data to JSON value in a particular manner like below:
Expected Output:
{
"ThirdPartyInterfaceData": {
"SMS":
{
"ThirdPartyInterfaceName": "PLIVO",
"SYSTEM_SMSAuthID": "MAMDQ1ODNJN2JMMZMWZD",
"SYSTEM_SMSAuthToken": "YWU5NTlhNzgxYTA1OWY4NTFkMTM4NWY4ZjM5Y2Zl",
"SYSTEM_SMSFrom": "Venue Metro",
"SYSTEM_SMSStatusMonitorURL": "http://www.google.com",
"SYSTEM_SMSStatusMonitorURLMethod": "POST",
"SYSTEM_SMSRestAPIVersion": "v1"
}
}
}
I tried something, using FOR JSON AUTO, it gives the following value:
Current Output:
[
{
"ThirdPartyInterfaceData":"SMS",
"ThirdPartyInterfaceName":"PLIVO",
"Name":"SYSTEM_SMSAuthID",
"Value":"MAMDQ1ODNJN2JMMZMWZD"
},
{
"ThirdPartyInterfaceData":"SMS",
"ThirdPartyInterfaceName":"PLIVO",
"Name":"SYSTEM_SMSAuthToken",
"Value":"YWU5NTlhNzgxYTA1OWY4NTFkMTM4NWY4ZjM5Y2Zl"
},..
..."
}
]
But my requirement is the above format, is it possible? Can anybody help me to get this.
Thanks in advance.
We can use dynamic SQL. I am deleting rows from #Example table, so if you want to use my solution and do not delete rows from main table, you have to create additional table and insert all values. It should work for multiple different values in columns ThirdPartyInterfaceData and ThirdPartyInterfaceName.
SQL Server 2016 is needed.
FIRST WAY (with WHILE loop)
DECLARE #Example TABLE
(
ThirdPartyInterfaceData VARCHAR(10)
,ThirdPartyInterfaceName VARCHAR(10)
,Name VARCHAR(512)
,Value VARCHAR(800)
)
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthID','MAMDQ1ODNJN2JMMZMWZD')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthToken','YWU5NTlhNzgxYTA1OWY4NTFkMTM4NWY4ZjM5Y2Zl')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSFrom','Venue Metro')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURL','http://www.google.com')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURLMethod','POST')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSRestAPIVersion','v1')
DECLARE #sql NVARCHAR(MAX)
DECLARE #value VARCHAR(800)
DECLARE #name VARCHAR(512)
DECLARE #ThirdPartyInterfaceData VARCHAR(10)
DECLARE #ThirdPartyInterfaceName VARCHAR(10)
DECLARE #new bit = 1 --We have to recognize when to add element ""ThirdPartyInterfaceName":"PLIVO","
DECLARE #start bit = 1 --This variable is only used in first iteration of WHILE
DECLARE #json NVARCHAR(MAX)
SET #sql = 'SELECT #json = (SELECT '
/*
We are going to delete rows from table #Example, so we can use EXISTS in WHILE
*/
WHILE EXISTS(SELECT * FROM #Example)
BEGIN
SELECT TOP 1
#new = CASE WHEN #start = 0 THEN CASE
WHEN #ThirdPartyInterfaceData IS NOT NULL AND #ThirdPartyInterfaceName IS NOT NULL
AND (#ThirdPartyInterfaceData <> ThirdPartyInterfaceData OR #ThirdPartyInterfaceName <> ThirdPartyInterfaceName)
THEN 1
ELSE 0
END
ELSE 1 END,
#ThirdPartyInterfaceData = ThirdPartyInterfaceData,
#ThirdPartyInterfaceName = ThirdPartyInterfaceName,
#name = name,
#value = value
FROM #Example
ORDER BY ThirdPartyInterfaceData, ThirdPartyInterfaceName, Name
SET #start = 0
IF #new = 1
BEGIN
SET #sql = #sql + '''' + #ThirdPartyInterfaceName + ''' AS ''ThirdPartyInterfaceData.' + #ThirdPartyInterfaceData + '.ThirdPartyInterfaceName'', '
END
/*
Adding next element into JSON
*/
SET #sql = #sql + '''' + #value + '''' + ' AS ''ThirdPartyInterfaceData.' + #ThirdPartyInterfaceData + '.' + #name + ''', '
/*
Deleting current row
*/
DELETE FROM #Example WHERE #ThirdPartyInterfaceData = ThirdPartyInterfaceData AND
#ThirdPartyInterfaceName = ThirdPartyInterfaceName AND
#name = name AND
#value = value
END
/*
Deleting last, unnecessary comma and adding ' FOR JSON PATH'
*/
SET #sql = SUBSTRING(#sql,1,LEN(#sql) - 1) + ' FOR JSON PATH)'
/*
Executing sql, setting JSON into variable
*/
EXECUTE sp_executesql #sql, N'#json nvarchar(max) OUTPUT', #json = #json OUTPUT
/*
You can do whatever you want with this variable (insert into table etc.)
*/
SELECT #json
SECOND WAY (without WHILE loop):
DECLARE #Example TABLE
(
ThirdPartyInterfaceData VARCHAR(10)
,ThirdPartyInterfaceName VARCHAR(10)
,Name VARCHAR(512)
,Value VARCHAR(800)
)
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthID','MAMDQ1ODNJN2JMMZMWZD')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSAuthToken','YWU5NTlhNzgxYTA1OWY4NTFkMTM4NWY4ZjM5Y2Zl')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSFrom','Venue Metro')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURL','http://www.google.com')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSStatusMonitorURLMethod','POST')
INSERT INTO #Example VALUES('SMS','PLIVO','SYSTEM_SMSRestAPIVersion','v1')
DECLARE #sql NVARCHAR(MAX) = 'SELECT #json = (SELECT '
DECLARE #json NVARCHAR(MAX)
SELECT
#sql = #sql + JSON_Part
FROM (SELECT
ThirdPartyInterfaceData,
ROW_NUMBER() OVER (PARTITION BY ThirdPartyInterfaceData, ThirdPartyInterfaceName ORDER BY ThirdPartyInterfaceData, ThirdPartyInterfaceName, Name) AS Number,
'''' + Value + ''' AS ''ThirdPartyInterfaceData.' + ThirdPartyInterfaceData + '.' + Name + ''',' AS JSON_Part
FROM #Example
UNION ALL
SELECT DISTINCT
ThirdPartyInterfaceData,
0 AS Number,
'''' + ThirdPartyInterfaceName + ''' AS ''ThirdPartyInterfaceData.' + ThirdPartyInterfaceData + '.ThirdPartyInterfaceName'', ' AS JSON_Part
FROM #Example) a
ORDER BY ThirdPartyInterfaceData, Number
/*
Deleting last, unnecessary comma and adding ' FOR JSON PATH)'
*/
SET #sql = SUBSTRING(#sql,1,LEN(#sql) - 1) + ' FOR JSON PATH)'
PRINT #sql
/*
Executing sql
*/
EXECUTE sp_executesql #sql, N'#json nvarchar(max) OUTPUT', #json = #json OUTPUT
SELECT #json

Select all from tables where table names are from another table in SQL

I have a temp table which has a TableName column. I would like to loop through the temporary table and select everything in the the table (where table is the TableName column in the temp table).
I have been looking through the following link and related links however I am unable to adapt it to my needs. Any help is greatly appreciated.
I am using SQL Server 2014
Something which i have tried
Declare #id int
WHILE EXISTS(SELECT * FROM ##tt_tableList)
BEGIN
Select Top 1 #id = Id from ##tt_tableList
-- Do the work --
declare #query nvarchar(max)
set #query = 'Select * from (select TableName from ##tt_tablelist where id = '' +Cast(#id as nvarchar(50))+'')'
select #query
declare #tableName nvarchar(50)
set #tableName = (select TableName from ##tt_tableList where id = #id)
select #tableName
execute(#query)
-- Scrap the ID and Move On --
Delete ##tt_tableList where ID = #id
END
If I understood you correctly this is what you are asking for:
DECLARE #tbl table (TableName varchar(50))
insert into #tbl values ('SomeTableName')
insert into #tbl values ('AnotherTableName')
DECLARE #Tables VARCHAR(8000)
SELECT #Tables = COALESCE(#Tables + CHAR(13), '') + 'SELECT * FROM '+ TableName
FROM #tbl
exec(#Tables)
Just insert your table names in #tbl
I tried this based on answer from one of our fellow stack overflower and it works.
DECLARE #Tables VARCHAR(8000)
SELECT #Tables = COALESCE(#Tables + CHAR(13), '') + 'SELECT * FROM '+ TableName + ' Where Event like ''%CM_Manual_Change%'''
FROM ##tt_tableList
select #Tables
exec(#Tables)

Create a list of dynamic queries from an array in SQL Server 2008

I split this question of from Fill dynamic array with values from an array. Below is the actual code I now have.
First the dynamic code, second the 3 example queries that I would like the dynamic code to output (but which it doesn't). I get an error Invalid column name "field" or, when I change field into #tmp_auditlog_fields.field, the error that the column "field" cannot be bound. The latter error is probably due to me not joining any tables but I have no clue on which field to join the tables since the #tmp_auditlogs_fields is nothing more than an array to create a bunch of queries, unrelated to any real data in the database.
How can I get the desired output? Any help is greatly appreciated :-)
-- dynamic code
create table #tmp_auditlog_fields (
id int identity(1,1),
field varchar(255),
details varchar(255)
);
insert into #tmp_auditlog_fields (field, details) values
('a', 'foo'),
('b', 'bar'),
('c', 'baz'),
;
declare #sql nvarchar(max);
declare #cnt int, #i int = 1;
select #cnt = max(id) from #tmp_auditlog_fields;
while #i <= #cnt
begin
select #sql =
'select t.objectID, t.DocID as ' + field +
' into #tmp_auditlog_' + field +
' from #tmp_auditlog_subselection t where Details like ' +
details FROM tmp_auditlog_fields WHERE id = #i
exec sp_executesql #sql
select #i = #i + 1
end
;
-- example queries
select t.ObjectID, t.DocID as a
into #tmp_auditlog_a from #tmp_auditlog_subselection t
where Details like 'foo';
select t.ObjectID, t.DocID as b
into #tmp_auditlog_b from #tmp_auditlog_subselection t
where Details like 'bar';
select t.ObjectID, t.DocID as c
into #tmp_auditlog_c from #tmp_auditlog_subselection t
where Details like 'baz';
SQL Fiddle attempt:
declare #sql nvarchar(max);
declare #id int;
create table #tmp_auditlog_subselection (
stuff varchar(255),
other varchar(255)
);
insert into #tmp_auditlog_subselection (stuff, other) values
('foo', 'bar'),
('baz', 'bat'),
('lorem', 'ipsum')
;
create table #tmp_auditlog_fields (
id int identity(1,1),
field varchar(255),
details varchar(255)
);
insert into #tmp_auditlog_fields (field, details) values
('a', 'foo'),
('b', 'bar'),
('c', 'baz')
;
while exists(select * from #tmp_auditlog_fields where id >= #id)
begin
select #sql =
'select t.objectID, t.DocID as ' + field +
' into #tmp_auditlog_' + field +
' from #tmp_auditlog_subselection t where Details like ' +
details FROM tmp_auditlog_fields WHERE id = #i;
exec #sql
select #id = min(id) from #tmp_auditlog_fields where id > #id;
end
;
select * from #tmp_auditlog_a;
You're missing the from statement and where statement here:
while #i <= #cnt
begin
select #sql =
'select t.objectID, t.DocID as ' + field +
' into #tmp_auditlog_' + field +
' from #tmp_auditlog_subselection t where Details like ' + details FROM #tmp_auditlog_fields WHERE id = #i
exec sp_executesql #sql
select #i = #i + 1
end