SQL Server : Coalesce with multiple values - sql-server

I am using SQL Server 2008 R2 version. I have a simple table which has multiple columns. One of the column in EmpId which is of type nvarchar(50)
I am writing a stored procedure in which I receive an input which can have one of the following values.
Single EmpId: '12345'
Multiple EmpId's comma separated: '12345, 56789, 98987'
null
What I want:
If empid is a single empId just return
select *
from table_name
where EmpId = #empId
If empid is multiple comma-separated values, just return
select *
from table_name
where EmpId in (select * from dbo.splitstring(#empId))
if empId is null just return
Select *
from table_name
No need for where clause.
To cover all the three condition this is what I am trying:
DECLARE #empId nvarchar(2000)
SET #empId = '97050001,97050003, 97050004'
SELECT TOP 10 empId
FROM Employee
WHERE empId in (COALESCE((select * from dbo.splitstring(#empId)),[empId]))
I am getting following error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I understand the error. COALESCE() is expecting a single value but when I am getting comma separated values, splitstring function returns multiple values.
I do not want to build a dynamic query, so besides duplicating the code with if else block where I check if empId is null run select * from table_name else run select * from table name where empId in (). What options do I have?
To split comma-separated string into table, I am using this function:
CREATE FUNCTION dbo.splitstring ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END

Try this with a bit more sophisticated version of split string fuction.
CREATE PROCEDURE myProc
#EmpId NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' SELECT * FROM Table_Name WHERE 1 = 1'
+ CASE WHEN #EmpId IS NOT NULL THEN
N' AND empId IN ( SELECT Split.a.value(''.'', ''VARCHAR(100)'') empId
FROM (
SELECT Cast (''<X>''
+ Replace(#EmpId, '','', ''</X><X>'')
+ ''</X>'' AS XML) AS Data
) AS t CROSS APPLY Data.nodes (''/X'') AS Split(a)
) ' ELSE N'' END
Exec sp_executesql #Sql
,N'#EmpId NVARCHAR(50)'
,#EmpId
END
This version will perform better because
More efficient string splitting.
Better execution plan because of the parameterised execution plan caching because of the sp_executesql .

Depends on what you need:-
I do not want to build dynamic query, so besides duplicating the code
with if else block where I check if empId is null run select * from
table_name else run select * from table name where empId in ()
and for avoiding the duplicate, use the next approach:-
DECLARE #empId nvarchar(2000)
set #empId = '97050001,97050003,97050004'
if CHARINDEX(',',#empId) > 0 -- multiple Values
begin
set #empId = '''' + replace (#empId,',',''',''') + ''''
end
else if #empId is null
begin
set #empId = 'select empId from Employee'
end
exec ('select top 10 empId from Employee where empId in (' + #empId + ')' )
This approach handles the three cases:-
Passing a single value.
Passing multiple Value.
Passing a Null.

Case 1 and 2 can both be handled for the code you have already written for Case 2. You just need to add an OR condition for Case 3.
select *
from table_name
where #empId is NULL
or EmpId in (select * from dbo.splitstring(#empId))
That being said, IN clauses wrapping select statements are generally bad practice for the following reasons
They are slower than joins when the select statement returns many rows
Joins are more idiomatic
In your case, if there are only a few rows returned by splitstring it likely won't make much difference, but the following would be a more general approach to this kind of query.
select *
from table_name t
left join dbo.splitstring(#empId) s
on t.EmpId = s.Name
where #empId is NULL
or s.Name is not NULL
Feel free to check the execution plan for your query and profile to see which one is quicker, though your initial implementation should be fine. To quote Donald Knuth, "premature optimization is the root of all evil."
Update
After double checking the execution plans used in the cases when #empId is null and non null, it looks like the query above will always use the same execution plan, i.e. to join against the contents of the in clause, regardless of whether #empId is null. This is probably not ideal, as pointed out by #m-ali.
To ensure the proper execution plan in each case, you can separate this into two queries:
IF #empId is NULL
select *
from table_name
ELSE
select *
from table_name
where EmpId in (select * from dbo.splitstring(#empId))
I've verified the proper execution plan in either case in SSMS.
Disclaimer: I haven't profiled it but the string splitting suggested by #m-ali is also likely faster.

Related

Dynamic Database Stored Procedure on SQL Server 2016

I'm trying to build a stored procedure that will query multiple database depending on the databases required.
For example:
SP_Users takes a list of #DATABASES as parameters.
For each database it needs to run the same query and union the results together.
I believe a CTE could be my best bet so I have something like this at the moment.
SET #DATABASES = 'DB_1, DB_2' -- Two databases in a string listed
-- I have a split string function that will extract each database
SET #CURRENT_DB = 'DB_1'
WITH UsersCTE (Name, Email)
AS (SELECT Name, Email
FROM [#CURRENT_DB].[dbo].Users),
SELECT #DATABASE as DB, Name, Email
FROM UsersCTE
What I don't want to do is hard code the databases in the query. The steps I image are:
Split the parameter #DATABASES to extract and set the #CURRENT_DB Variable
Iterate through the query with a Recursive CTE until all the #DATABASES have been processed
Union all results together and return the data.
Not sure if this is the right approach to tackling this problem.
Using #databases:
As mentioned in the comments to your question, variables cant be used to dynamically select a database. Dynamic sql is indicated. You can start by building your template sql statement:
declare #sql nvarchar(max) =
'union all ' +
'select ''#db'' as db, name, email ' +
'from [#db].dbo.users ';
Since you have sql server 2016, you can split using the string_split function, with your #databases variable as input. This will result in a table with 'value' as the column name, which holds the database names.
Use the replace function to replace #db in the template with value. This will result in one sql statement for each database you passed into #databases. Then, concatenate the statements back together. Unfortunately, in version 2016, there's no built in function to do that. So we have to use the famous for xml trick to join the statements, then we use .value to convert it to a string, and finally we use stuff to get rid of the leading union all statement.
Take the results of the concatenated output, and overwrite the #sql variable. It is ready to go at this point, so execute it.
I do all that is described in this code:
declare #databases nvarchar(max) = 'db_1,db_2';
set #sql = stuff(
(
select replace(#sql, '#db', value)
from string_split(#databases, ',')
for xml path(''), type
).value('.[1]', 'nvarchar(max)')
, 1, 9, '');
exec(#sql);
Untested, of course, but if you print instead of execute, it seems to give the proper sql statement for your needs.
Using msForEachDB:
Now, if you didn't want to have to know which databases had 'users', such as if you're in an environment where you have a different database for every client, you can use sp_msForEachDb and check the structure first to make sure it has a 'users' table with 'name' and 'email' columns. If so, execute the appropriate statement. If not, execute a dummy statement. I won't describe this one, I'll just give the code:
declare #aggregator table (
db sysname,
name int,
email nvarchar(255)
);
insert #aggregator
exec sp_msforeachdb '
declare #sql nvarchar(max) = ''select db = '''''''', name = '''''''', email = '''''''' where 1 = 2'';
select #sql = ''select db = ''''?'''', name, email from ['' + table_catalog + ''].dbo.users''
from [?].information_schema.columns
where table_schema = ''dbo''
and table_name = ''users''
and column_name in (''name'', ''email'')
group by table_catalog
having count(*) = 2
exec (#sql);
';
select *
from #aggregator
I took the valid advice from others here and went with this which works great for what I need:
I decided to use a loop to build the query up. Hope this helps someone else looking to do something similar.
CREATE PROCEDURE [dbo].[SP_Users](
#DATABASES VARCHAR(MAX) = NULL,
#PARAM1 VARCHAR(250),
#PARAM2 VARCHAR(250)
)
BEGIN
SET NOCOUNT ON;
--Local variables
DECLARE
#COUNTER INT = 0,
#SQL NVARCHAR(MAX) = '',
#CURRENTDB VARCHAR(50) = NULL,
#MAX INT = 0,
#ERRORMSG VARCHAR(MAX)
--Check we have databases entered
IF #DATABASES IS NULL
BEGIN
RAISERROR('ERROR: No Databases Provided,
Please Provide a list of databases to execute procedure. See stored procedure:
[SP_Users]', 16, 1)
RETURN
END
-- SET Number of iterations based on number of returned databases
SET #MAX = (SELECT COUNT(*) FROM
(SELECT ROW_NUMBER() OVER (ORDER BY i.value) AS RowNumber, i.value
FROM dbo.udf_SplitVariable(#DATABASES, ',') AS i)X)
-- Build SQL Statement
WHILE #COUNTER < #MAX
BEGIN
--Set the current database
SET #CURRENTDB = (SELECT X.Value FROM
(SELECT ROW_NUMBER() OVER (ORDER BY i.value) AS RowNumber, i.value
FROM dbo.udf_SplitVariable(#DATABASES, ',') AS i
ORDER BY RowNumber OFFSET #COUNTER
ROWS FETCH NEXT 1 ROWS ONLY) X);
SET #SQL = #SQL + N'
(
SELECT Name, Email
FROM [' + #CURRENTDB + '].[dbo].Users
WHERE
(Name = #PARAM1 OR #PARAM1 IS NULL)
(Email = #PARAM2 OR #PARAM2 IS NULL)
) '
+ N' UNION ALL '
END
PRINT #CURRENTDB
PRINT #SQL
SET #COUNTER = #COUNTER + 1
END
-- remove last N' UNION ALL '
IF LEN(#SQL) > 11
SET #SQL = LEFT(#SQL, LEN(#SQL) - 11)
EXEC sp_executesql #SQL, N'#CURRENTDB VARCHAR(50),
#PARAM1 VARCHAR(250),
#PARAM2 VARCHAR(250)',
#CURRENTDB,
#PARAM1 ,
#PARAM2
END
Split Variable Function
CREATE FUNCTION [dbo].[udf_SplitVariable]
(
#List varchar(8000),
#SplitOn varchar(5) = ','
)
RETURNS #RtnValue TABLE
(
Id INT IDENTITY(1,1),
Value VARCHAR(8000)
)
AS
BEGIN
--Account for ticks
SET #List = (REPLACE(#List, '''', ''))
--Account for 'emptynull'
IF LTRIM(RTRIM(#List)) = 'emptynull'
BEGIN
SET #List = ''
END
--Loop through all of the items in the string and add records for each item
WHILE (CHARINDEX(#SplitOn,#List)>0)
BEGIN
INSERT INTO #RtnValue (value)
SELECT Value = LTRIM(RTRIM(SUBSTRING(#List, 1, CHARINDEX(#SplitOn, #List)-1)))
SET #List = SUBSTRING(#List, CHARINDEX(#SplitOn,#List) + LEN(#SplitOn), LEN(#List))
END
INSERT INTO #RtnValue (Value)
SELECT Value = LTRIM(RTRIM(#List))
RETURN
END

Query to select next column if the current column returns no rows

The question is quite extensive, please bear with me. I have a single mapping table with the following structure:
This particular table is used in the process of generating a hierarchy. The order and position of the columns in the table indicate the order of hierarchy (Organization, Category, Continent, Country.. etc.) Each entity in this hierarchy has a related table with associated Id and Name. For example, there is a Country table with CountryId and CountryName. Note that since the MappingTable's values are all nullable there are no foreign key constraints.
I want to generate a procedure that will do the following:
Based on conditions provided, retrieve values of the next entity in the hierarchy. For example, if the OrganizationId and CategoryId are given, the values of ContinentId that satisfy said condition need to be retrieved.
Also, if the value of ContinentId is NULL, then the values of CountryId need to be retrieved. Here, given the condition OrganizationId = 1 and CategoryId = 1 the procedure should return the list of RegionId.
In addition to retrieving the RegionId, the corresponding RegionName should be retrieved from the Region Table.
So far, the procedure looks something like this - just a few things to explain here.
ALTER PROCEDURE [dbo].[GetHierarchy]
(
#MappingTableName VARCHAR(30),
#Position VARCHAR(5),
-- Given in the form of Key-value pairs 'OrganizationId:1,CategoryId:1'
#InputData VARCHAR(MAX),
#Separator CHAR(1),
#KeyValueSeperator CHAR(1)
)
AS
BEGIN
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Result NVARCHAR(MAX)
DECLARE #Sql1 NVARCHAR(MAX);
DECLARE #TableName NVARCHAR(30)
DECLARE #Exists bit
SELECT #TableName = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #MappingTableName AND ORDINAL_POSITION = #position
SET #TableName = SUBSTRING(#TableName,0,LEN(#TableName) - 1)
-- Returns a dynamic query like "SELECT ContinentId from Continent WHERE OrganizationId = 1 and CategoryId = 1".
SELECT #Sql = [dbo].[KeyValuePairs](#TableName, #InputData, #Separator, #KeyValueSeperator)
SET #Sql1 = N'SET #Exists = CASE WHEN EXISTS(' + #Sql + N' AND ' + #TableName + N'Id IS NOT NULL) THEN 1 ELSE 0 END'
PRINT #Sql
EXEC sp_executesql #Sql1,
N'#Exists bit OUTPUT',
#Exists = #Exists OUTPUT
IF(#Exists = 1)
BEGIN
SET #Sql1 = 'SELECT ' + #TableName + 'Id, ' + #TableName + 'Name FROM '+ #TableName+' WHERE ' + #TableName +'Id IN (' + #Sql + ')';
PRINT #Sql1
EXECUTE sp_executesql #SQL1
END
ELSE
BEGIN
--PRINT 'NOT EXISTS'
DECLARE #nextPosition INT
SELECT #nextPosition = CAST(#position AS INT)
SET #nextPosition = #nextPosition + 1
SET #Position = CONVERT(VARCHAR(5), CAST(#position AS INT))
EXEC [dbo].[GetHierarchy] #MappingTableName, #Position, #InputData, #Separator, #KeyValueSeperator
END
END
The logic of this procedure is such that, I get the name of the column at a particular position (based on the conditions here, it is Continent) and generate the dynamic query to retrieve the next column's values based on the condition of the input condition (I am using a separate function to do this for me).
Once retrieved, I run the query to check if it returns any rows. If the query returns rows, then I retrieve the corresponding ContinentName from the Continent table. If no rows are returns, I recursively call the procedure again with the next position as the input.
On the business side of things, it seems like a two step process. But, as a procedure it is quite complex, extensive and - not to mention, recursive. Is there an easier way to do this? I am not familiar with CTEs - can the same logic be implemented using CTEs?
This is quite similar to what is asked here: Working with a dynamic hierarchy SQL Server
Might be the little lengthy approach. Try this
DECLARE #T TABLE
(
SeqNo INT IDENTITY(1,1),
CatId INT,
Country INT,
StateId INT,
DistId INT
)
DECLARE #State TABLE
(
StateId INT,
StateNm VARCHAR(20)
)
DECLARE #Country TABLE
(
CountryId INT,
CountryNm VARCHAR(20)
)
INSERT INTO #State
VALUES(3,'FL')
INSERT INTO #Country
VALUES(2,'USA')
INSERT INTO #T(CatId)
VALUES(1)
INSERT INTO #T(CatId,Country)
VALUES(1,2)
INSERT INTO #T(CatId,StateId)
VALUES(1,3)
;WITH CTE
AS
(
SELECT
*,
IdVal = COALESCE(Country,StateId,DistId),
IdCol = COALESCE('Country '+CAST(Country AS VARCHAR(50)),'StateId '+CAST(StateId AS VARCHAR(50)),'DistId '+CAST(DistId AS VARCHAR(50)))
FROM #T
WHERE CatId = 1
),C2
AS
(
SELECT
SeqNo,
CatId,
Country,
StateId,
DistId,
IdVal,
IdCol = LTRIM(RTRIM(SUBSTRING(IdCol,1,CHARINDEX(' ',IdCol))))
FROM CTE
)
SELECT
C2.SeqNo,
C2.CatId,
S.StateNm,
C.CountryNm
FROM C2
LEFT JOIN #State S
ON C2.IdCol ='StateId'
AND C2.IdVal = S.StateId
LEFT JOIN #Country C
ON C2.IdCol ='Country '
AND C2.IdVal = c.CountryId

SQL Server: How to compare the results of two queries stored in variables

I'm trying to compare two resultsets from queries that are stored in variables.
I've tried the following:
DECLARE #sql1 varchar(8000) = 'SELECT * FROM table1'
DECLARE #sql2 varchar(8000) = 'SELECT Col2, Col1 FROM table1'
IF EXISTS(
(EXEC sp_executesql #sql1
EXCEPT
EXEC sp_executesql #sql2)
UNION ALL
(EXEC sp_executesql #sql2
EXCEPT
EXEC sp_executesql #sql1))
This approach has two problems: the if statement doesn't like EXEC statements (EXCEPT UNION ALL EXCEPT structure works when you use the actual queries instead of the variables).
The second problem is that, even if you use the actual queries, the order of the columns of both queries have to be the same or the resulsets will not match. For my purposes however, I need those resultsets to match. I think I need a way to order the columns but I'm not sure if that's even possible.
EDIT:
I have no control over the incoming queries because this is code for an application that's meant to check an answer query of a student against the teacher's query. I can't choose to not use *.
What you try to achieve is something that is not meant to be done in SQL Server. A rather clean way would be to have a code that executes each query and compares both resulting data sets, by metadata and value by value.
I do not recommend you to use the following approach.
For testing purposes I created this stored procedure:
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'SP_CompareQueryResults')
DROP PROCEDURE SP_CompareQueryResults
GO
CREATE PROCEDURE SP_CompareQueryResults
(
#sql1 NVARCHAR(4000)
, #sql2 NVARCHAR(4000)
)
AS
BEGIN
DECLARE #q1 NVARCHAR(MAX) = #sql1
, #q2 NVARCHAR(MAX) = #sql2
IF OBJECT_ID('tempdb..##q1') IS NOT NULL
DROP TABLE ##q1
IF OBJECT_ID('tempdb..##q2') IS NOT NULL
DROP TABLE ##q2
SET #q1 = 'SELECT * INTO ##q1 FROM (' + #q1 + ') r'
SET #q2 = 'SELECT * INTO ##q2 FROM (' + #q2 + ') r'
BEGIN TRY
EXEC (#q1)
EXEC (#q2)
END TRY
BEGIN CATCH
SELECT 'One of the source queries are not valid.'
RETURN
END CATCH
DECLARE #r NVARCHAR(MAX)
SELECT #r = COALESCE(#r + ', ', ' ') + COLUMN_NAME
FROM (
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '##q1'
INTERSECT
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '##q2'
) r
SET #r = 'SELECT 1 as SourceQuery, * FROM (SELECT ' + #r + ' FROM ##q1 EXCEPT SELECT' + #r + ' FROM ##q2) r'
+ ' UNION ALL SELECT 2 as SourceQuery, * FROM (SELECT ' + #r + ' FROM ##q2 EXCEPT SELECT' + #r + ' FROM ##q1) r'
BEGIN TRY
EXEC(#r)
END TRY
BEGIN CATCH
SELECT 'Queries have not matching metadata.'
RETURN
END CATCH
IF OBJECT_ID('tempdb..##q1') IS NOT NULL
DROP TABLE ##q1
IF OBJECT_ID('tempdb..##q2') IS NOT NULL
DROP TABLE ##q2
END
GO
It finds columns with the same names from both queries and compares each of the queries results, returning rows from query 1 not included in query 2 and the other way around.
Let's say you have two queries with following results:
and another one:
As you can see the second query has an additional column, columns are not in the same order and only one tuple is in both queries.
Executing the above SP like this:
EXEC SP_CompareQueryResults
#sql1 = N'
SELECT 1 AS ID
, ''test'' AS Value
, CAST(1 as BIT) AS Valid
UNION ALL
SELECT 2, ''test2'', 0',
#sql2 = N'
SELECT 1 AS ID
, CAST(1 as BIT) AS Valid
, ''test'' AS Value
, ''test'' AS AnotherValue
UNION ALL
SELECT 2, 1, ''test2'', ''whatever'''
gives you the none matching tuples from both queries:
If both queries yield the same results, the SP_CompareQueryResults will not return any rows, so you could say they have same values for the columns with matching names. Same thing happens if both queries have no resulting rows, giving a false positive. You can tweak the above procedure for your needs.
DO NOT USE this code in a production environment as it can be SQL injected and I have not tested this. Generally avoid dynamic sql, if not necessary.
As first stated, try to write for example a c# code is sql injection safe and compares the results.

How To Select Data From Generated Information Schema Tables

I have several tables with common table names. I want to select all data from them without specifying all tables and union them. I'm thinking of using information_schema.tables to accomplish this.
Tables:
tbl_20160201
tbl_20160202
tbl_20160203
tbl_20160204
tbl_20160205
Query:
SELECT *
FROM (
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%'
) a
However, this query only returns the table names and not the data in the tables. I need to union all the tables included in the query. Thank you.
Assuming these tables have the same number of columns and data types, you can use dynamic sql:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql +
'SELECT * FROM ' + TABLE_NAME + ' UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%';
SELECT #sql = SUBSTRING(#sql,1, LEN(#sql) - 11);
PRINT(#sql)
EXEC (#sql)
Otherwise, you'll get an error saying:
All queries combined using a UNION, INTERSECT or EXCEPT operator must
have an equal number of expressions in their target lists
Below query might help you :-
DECLARE #SQL VARCHAR(max)
DECLARE #T as sysname
-- Make sure below temporary table structure same as the tables e.g tbl_201602 which you want to union
declare #TT as Table(
Col1 [decimal](18, 2) NOT NULL,
Col2 [date] NOT NULL,
Col3 [varchar](200) NOT NULL
)
DECLARE TName CURSOR FORWARD_ONLY STATIC FOR
select TABLE_NAME
from
information_schema.tables where TABLE_NAME like 'tbl_201602%'
OPEN TName
FETCH NEXT FROM TName INTO #T
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #SQL = 'select * from' + ' ' + #T
INSERT INTO #TT
EXEC(#SQL)
FETCH NEXT FROM TName INTO #T
END
CLOSE TName
DEALLOCATE TName
select * from #TT
This will give the union of all tables in a single temporary table #TT
NOTE : Just make sure that all comman tables are having same no of columns and in same ordering, otherwise it may raise error

SQL Output contents of a table to string

I have a table that contains many rows of SQL commands that make up a single SQL statement (to which I am grateful for this answer, step 5 here)
I have followed the example in this answer and now have a table of SQL - each row is a line of SQL that build a query. I can copy and paste the contents of this table into a new query window and get the results however due to my lack of SQL knowledge I am not sure how I go about copying the contents of the table into a string variable which I can then execute.
Edit: The SQL statement in my table comprises of 1 row per each line of the statement i.e.
Row1: SELECT * FROM myTable
Row2: WHERE
Row3: col = #value
This statement if copied into a VARCHAR(MAX) exceeds the MAX limit.
I look forward to your replies. in the mean time I will try myself.
Thank you
You can use coalesce to concatenate the contents of a column into a string, e.g.
create table foo (sql varchar (max));
insert foo (sql) values ('select name from sys.objects')
insert foo (sql) values ('select name from sys.indexes')
declare #sql_output varchar (max)
set #sql_output = '' -- NULL + '' = NULL, so we need to have a seed
select #sql_output = -- string to avoid losing the first line.
coalesce (#sql_output + sql + char (10), '')
from foo
print #sql_output
Note: untested, just off the top of my head, but a working example of this should produce the following output:
select name from sys.objects
select name from sys.indexes
You can then execute the contents of the string with exec (#sql_output) or sp_executesql.
You can try something like this
DECLARE #TABLE TABLE(
SqlString VARCHAR(MAX)
)
INSERT INTO #TABLE (SqlString) SELECT 'SELECT 1'
DECLARE #SqlString VARCHAR(MAX)
SELECT TOP 1 #SqlString = SqlString FROM #TABLE
EXEC (#SqlString)
Concatenate string from multiple rows
DECLARE #Table TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO #Table (ID,Val) SELECT 1, 'SELECT *'
INSERT INTO #Table (ID,Val) SELECT 2, 'FROM YourTable'
INSERT INTO #Table (ID,Val) SELECT 3, 'WHERE 1 = 1'
DECLARE #SqlString VARCHAR(MAX)
--Concat
SELECT DISTINCT
#SqlString =
(
SELECT tIn.Val + ' '
FROM #Table tIn
ORDER BY ID
FOR XML PATH('')
)
FROM #Table t
PRINT #SqlString
if you want to execute a string of sql then use Exec() or sp_executeSql

Resources