Dynamically generate query in SQL - sql

I have four tables like table 1, table 2, table 3 and table 4 and columns are
t1: 1,a, e
t2: 1,b, f
t3: 1,c, g
t4: 1,d, h
These table are in temporary table
I need output like this
1,a,b,c,d,e,f,h
Dynamically

you can use this below logic-
SELECT
MAX(c1),MAX(c2),MAX(c3),MAX(c4),
MAX(c5),MAX(c6),MAX(c7),MAX(c8)
FROM
(
SELECT c1,c2,NULL c3,NULL c4,NULL c5,NULL c6,NULL c7,NULL c8 FROM t1
UNION ALL
SELECT NULL c1,NULL c2,c3,c4,NULL c5,NULL c6,NULL c7,NULL c8 FROM t2
UNION ALL
SELECT NULL c1,NULL c2,NULL c3,NULL c4,c5,c6,NULL c7,NULL c8 FROM t3
UNION ALL
SELECT NULL c1,NULL c2,NULL c3,NULL c4,NULL c5,NULL c6,c7,c8 FROM t4
)A

You can use the INFORMATION_SCHEMA to find the table structure and generate a dynamic query as per below.
FOR DB Tables
Create TABLE Table1
(
Column1 int,
Column5 int,
)
Create TABLE Table2
(
Column2 int,
Column6 int,
)
Create TABLE Table3
(
Column3 int,
Column7 int,
)
Create TABLE Table4
(
Column4 int,
Column8 int,
)
DECLARE #Columns VARCHAR(MAX)
,#From VARCHAR(MAX)
SELECT #Columns = (SELECT
',' + C.TABLE_NAME + '.' + C.COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS AS C
WHERE
C.TABLE_NAME like 'Table%'
ORDER BY
C.COLUMN_NAME
FOR XML PATH (''))
SELECT #From = (SELECT
'CROSS JOIN ' + T.TABLE_NAME + ' '
FROM
INFORMATION_SCHEMA.Tables AS T
WHERE
T.TABLE_NAME like 'Table%'
ORDER BY
T.TABLE_NAME
FOR XML PATH (''))
DECLARE #FullQuery VARCHAR(MAX) = 'SELECT '+ substring(#Columns,2,LEN(#Columns)-1) + ' FROM '
+ substring(#From,12,LEN(#From)-11)
EXEC (#FullQuery)
DROP TABLE table1
DROP TABLE table2
DROP TABLE table3
DROP TABLE table4
For Temp Tables
Create TABLE #Table1
(
Column1 int,
Column5 int,
)
Create TABLE #Table2
(
Column2 int,
Column6 int,
)
Create TABLE #Table3
(
Column3 int,
Column7 int,
)
Create TABLE #Table4
(
Column4 int,
Column8 int,
)
DECLARE #Columns VARCHAR(MAX)
,#From VARCHAR(MAX)
SELECT #Columns = (SELECT
',' + SUBSTRING(T.name,1,CHARINDEX('_', T.name) - 1) + '.' + C.name
FROM
Tempdb.Sys.Tables AS T
INNER JOIN Tempdb.Sys.Columns AS C ON C.object_id = T.object_id
where
T.name like '#Table%'
ORDER BY
C.name
FOR XML PATH (''))
SELECT #From = (SELECT
'CROSS JOIN ' + SUBSTRING(T.name,1,CHARINDEX('_', T.name) - 1) + ' '
FROM
Tempdb.Sys.Tables AS T
where
T.name like '#Table%'
ORDER BY
T.name
FOR XML PATH (''))
DECLARE #FullQuery VARCHAR(MAX) = 'SELECT DISTINCT '+ substring(#Columns,2,LEN(#Columns)-1) + ' FROM '
+ substring(#From,12,LEN(#From)-11)
EXEC (#FullQuery)
DROP TABLE #table1
DROP TABLE #table2
DROP TABLE #table3
DROP TABLE #table4
You need to control the table and columns based on your need.

From what I understand, your data is something like:
Your final output is:
One solution I propose is the following:
Get the data from all tables (table1,table2,table3,table4) in respective temp tables.
For eg. the corresponding temp table for table1 is:
Create a table to consolidate all the data from all the 4 temp tables created in the previous step.
create table consolidated_data
(
dataVal VARCHAR(1000)
,colName VARCHAR(1000)
)
insert into consolidated_data
select * from #temp1
union all
select * from #temp2
union all
select * from #temp3
union all
select * from #temp4
Write dynamic pivot sql query. This is a good reference.
begin
declare #query nvarchar(max);
declare #cols nvarchar(max);
with cte as (select distinct colHeaderId, colHeaderName from [dbo].ColHeader)
select #cols = STUFF((SELECT ',' + QUOTENAME(colheaderName)
FROM cte
order by colHeaderId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query =
'select * from (
select d.dataVal as dataVal,row_number() OVER(partition by d.colName order
by d.colName) as rownum
,c.colHeaderName as colHeaderName from consolidated_data d left outer join
ColHeader c on d.colName = c.colHeaderName) as t
PIVOT
(
MAX(dataVal)
FOR colheaderName IN( ' + #cols + ' )' +
' ) AS p ; ';
execute(#query);
end

Related

Dynamic SQL stored procedure not populating temporary table

I am using a stored procedure with one parameter (#tablename) to generate a table of attributes about the table named via the parameter.
I call the stored procedure as follows
EXEC sp_Schema_Presentation #tablename = 'UserID'
And run the stored procedure (at the bottom of this post).
I have created a #DynamicSQL string in order to use my #tablename parameter. However, the SELECT statement, in which it's used, also creates the #TEMP table.
The rest of the query uses this #TEMP table so I DECLARE its structure at the top.
However, when I run the stored procedure, the #TEMP table is empty
If I hard code the #tablename, the query will work. Any ideas how I can fix this?
Thanks
CREATE TABLE #TEMP
(
SampleKey nvarchar(MAX),
SampleData nvarchar(MAX)
)
DECLARE #DynamicSQL NVARCHAR(MAX)
SET #DynamicSQL = N'SELECT B.*
INTO dbo.#TEMP
FROM (
SELECT * FROM ' + #Tablename + N' ORDER BY 1 DESC
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY
) A
CROSS APPLY (
SELECT [Key] AS SampleKey
,Value AS SampleData
FROM OpenJson( (SELECT A.* FOR JSON Path, Without_Array_Wrapper,INCLUDE_NULL_VALUES ) )
) B'
Full stored procedure in SQL Server 2016:
ALTER PROCEDURE [dbo].[sp_Schema_Presentation]
#TableName nvarchar(MAX)
AS
BEGIN
CREATE TABLE #TEMP
(
SampleKey nvarchar(MAX),
SampleData nvarchar(MAX)
)
DECLARE #DynamicSQL NVARCHAR(MAX)
SET #DynamicSQL = N'SELECT B.*
INTO dbo.#TEMP
FROM (
SELECT * FROM ' + #Tablename + N' ORDER BY 1 DESC
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY
) A
CROSS APPLY (
SELECT [Key] AS SampleKey
,Value AS SampleData
FROM OpenJson( (SELECT A.* FOR JSON Path, Without_Array_Wrapper,INCLUDE_NULL_VALUES ) )
) B'
DECLARE #Columns as NVARCHAR(MAX)
SELECT #Columns = COALESCE(#Columns + ', ','') + QUOTENAME(COLUMN_NAME)
FROM
(
SELECT COLUMN_NAME FROM PRESENTATION_PP.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N''' + #TableName + '''
) AS B
EXECUTE sp_executesql #DynamicSQL
SELECT a.COLUMN_NAME,
CASE WHEN a.COLUMN_NAME LIKE '%[_]_key' THEN a.COLUMN_NAME
ELSE REPLACE(a.COLUMN_NAME,'_',' ') END AS DISPLAY_NAME,
a.DATA_TYPE, COALESCE(a.CHARACTER_MAXIMUM_LENGTH, a.NUMERIC_PRECISION) AS SIZE,
CASE WHEN NUMERIC_SCALE IS NULL THEN 0
ELSE NUMERIC_SCALE END AS SCALE,
a.IS_NULLABLE AS NULLABLE,
CASE WHEN i.is_primary_key IS NOT NULL THEN 'YES'
ELSE 'NO' END AS PK,
#TEMP.SampleData
FROM PRESENTATION_PP.INFORMATION_SCHEMA.COLUMNS a
LEFT JOIN
sys.columns c ON a.COLUMN_NAME = c.name
LEFT JOIN
sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT JOIN
sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
LEFT JOIN
#TEMP ON a.COLUMN_NAME COLLATE SQL_Latin1_General_CP1_CI_AI = #TEMP.SampleKey COLLATE SQL_Latin1_General_CP1_CI_AI
WHERE TABLE_NAME = #TableName AND c.object_id = OBJECT_ID(#TableName)
SELECT * FROM #TEMP
DROP TABLE #TEMP
END
Create the #Temp table first, and then INSERT INTO not Select ... Into #Temp
CREATE TABLE #TEMP (SampleKey nvarchar(MAX), SampleData nvarchar(MAX))
DECLARE #DynamicSQL NVARCHAR(MAX)
SET #DynamicSQL = N'
Insert Into #Temp
SELECT B.*
FROM (
SELECT * FROM ' + #Tablename + N' ORDER BY 1 DESC
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY
) A
CROSS APPLY (
SELECT [Key] AS SampleKey
,Value AS SampleData
FROM OpenJson( (SELECT A.* FOR JSON Path, Without_Array_Wrapper,INCLUDE_NULL_VALUES ) )
) B
'
Exec(#DynamicSQL)
Select * from #Temp

How to loop through a list of table names and see if a specific value in a column exists?

I have produced a table in SQL with a list of tables. This list of tables is stored under the column 'table_name'. I want to loop through each entry under 'table_name' and return a 1 if that table has a value in a specific column or 0 if that table does not have a value in a specific column.
How would I do that?
Edited With sample data
table_name
tabel1
table2
table3
table4
Pseudo Code
For i in table_name
if count(table_name["col_name"] = "value") > 0
return 1
else
return 0
Try this:
drop table if exists #t
create table #t (A int)
insert into #t
select 1
drop table if exists #t2
create table #t2 (A int)
insert into #t2
select 0
drop table if exists #tables
create table #tables (tab varchar(100))
declare
#loop table (rn int, tab varchar(100))
declare
#res table (cnt int)
declare
#i int=1
,#tab varchar (100)=''
,#query nvarchar (max)
insert into #tables
select '#t'
union all
select '#t2'
insert into #loop
select ROW_NUMBER () over (partition by (select 1) order by tab),tab from #tables
while #i<=(select max(rn) from #loop)
begin
select #tab=tab from #loop where rn=#i
set #query='select count(*) from '+#tab+' where a=1'
insert into #res
exec(#query)
if (select cnt from #res)>0 select 'Exists' else select 'Not Exists'
delete #res
set #i=#i+1
end
Assuming you have a singular column/value in question, you can try the following in SSMS:
DECLARE #Tables table ( table_name varchar(50) );
INSERT INTO #Tables VALUES
( 'Child' ), ( 'COS' ), ( 'CustomData' ), ( 'Misc' ), ( 'Misc2' );
DECLARE
#col varchar(50) = 'id', -- column to be queried
#val varchar(50) = '1', -- value to be queried
#sql varchar(MAX) = '' -- important! set to empty string;
SELECT
#sql = #sql + CASE WHEN LEN( #sql ) > 0 THEN ' UNION ' ELSE '' END
+ 'SELECT ' + QUOTENAME( table_name, '''' ) + ' AS [table_name], COUNT(*) AS [value_count] FROM [' + table_name + '] WHERE [' + #col + ']=' + QUOTENAME( #val, '''' )
FROM #Tables t WHERE EXISTS (
SELECT * FROM sys.columns c WHERE c.object_id = OBJECT_ID( t.table_name ) AND c.[name] = #col
);
EXEC( #sql );
In my environment this returns:
+------------+-------------+
| table_name | value_count |
+------------+-------------+
| Child | 1 |
| Misc | 1 |
| Misc2 | 0 |
+------------+-------------+
This is the (beautified) dynamic SQL created:
SELECT 'Child' AS [table_name], COUNT(*) AS [value_count] FROM [Child] WHERE [id]='1'
UNION
SELECT 'Misc' AS [table_name], COUNT(*) AS [value_count] FROM [Misc] WHERE [id]='1'
UNION
SELECT 'Misc2' AS [table_name], COUNT(*) AS [value_count] FROM [Misc2] WHERE [id]='1'
The EXISTS in the WHERE clause eliminates any tables that do not have the column in question, and thereby any errors related to it.

Count rows in all tables, that meets conditions

I have database with 7,000 tables, most of these have a column DataAreaId, but not all tables, since some are global.
I would like to list all tables that have the column DataAreaId and their row count where the column DataAreaId contains "FR".
So for one table it would be:
SELECT COUNT(*)
FROM Table
WHERE DataAreaId = 'FR'
Any suggestions?
You can use the following
CREATE TABLE T1(
Dataareaid VARCHAR(45)
);
CREATE TABLE T2(
Dataareaid VARCHAR(45)
);
INSERT INTO T1 VALUES
('FR'),
('ALG'),
('FR');
DECLARE #SQL NVARCHAR(max) = N'';
SELECT #SQL = (
SELECT CONCAT(
N'UNION ALL ',
N'SELECT ''',
t.name,
N''' AS TableName, ',
N'Cnt = (SELECT COUNT(1)',
' FROM ',
QUOTENAME(t.name),
N' WHERE [Dataareaid] = ''FR'')'
)
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'Dataareaid'
FOR XML PATH('')
)
SET #SQL = STUFF(#SQL, 1, 10, N'');
EXEC sp_executesql #SQL;
Returns:
+-----------+-----+
| TableName | Cnt |
+-----------+-----+
| T1 | 2 |
| T2 | 0 |
+-----------+-----+
Live Demo
One way to do this is to query all tables containing that column, and build a query statement for each
select 'union all select ' + QUOTENAME(t.name,N'''') + ', count(1) from ' + t.name + ' where Dataareaid = ''FR'''
from sys.columns c
join sys.tables t ON c.object_id = t.object_id
where c.name = 'Dataareaid'
each row would look like this
union all select 'SomeTable', count(1) from SomeTable where Dataareaid = 'FR'
Now just put all statements together and remove the first union all
My answer gets the column metadata, executes the statement in a loop and publishes the results to a table variable:
if object_id('tempdb..#LoopList') is not null
drop table #LoopList
select
s.[name] as SchemaName
,t.[name] as TableName
,row_number() over (order by t.[object_id] asc) as RowNumber
into #LoopList
from sys.columns as c
inner join sys.tables as t
on c.[object_id] = t.[object_id]
inner join sys.schemas as s
on t.[schema_id] = s.[schema_id]
where c.[name] = 'Dataareaid'
declare
#a int = 1
,#b int = (select max(RowNumber) from #LoopList)
,#c nvarchar(max)
,#d nvarchar(max)
,#e int
declare #count table (RowCounter int)
declare #resultsTable table (TableName nvarchar(500), RowCounter int)
while #a <= #b
begin
delete from #count
set #c = concat ((select quotename(SchemaName) from #LoopList where RowNumber = #a)
,'.'
,(select quotename(TableName) from #LoopList where RowNumber = #a)
)
set #d = concat(N'select count(*) from '
,#c
,N' where Dataareaid = ''FR'''
)
insert into #count (
RowCounter
)
exec sp_executesql #d
set #e = (select top 1 RowCounter from #count)
insert into #resultsTable (
TableName
,RowCounter
)
values (#c,#e)
set #a += 1;
end
select * from #resultsTable

Every distinct value for every column

I'd like to create a dynamic select that returns every distinct value for each column in a wide table. I.e.
select distinct #mycolumn
from #mytable
for every column and the results combined to a single table.
Edit1:
Example:
Edit2: The order of the returned data won't matter, and the source table can have all sorts of data types.
Any advice appreciated, thank you!
The only way I can think of is very cumbersome and probably extremely slow:
Using a Tally table (I've generated one using a recursive cte for the sake of this answer, but that's also not a very good way to do that...) and multiple derived tables left joined to that tally table I was able to come up with something that will generate the desired output.
However, as I wrote on the top - it's very cumbersome and probably extremely slow (I've tested only on a table with 5 columns and 6 rows so I have no idea about execution speed).
DECLARE #Count int
select #Count = COUNT(1)
FROM YourTable
;with tally as (
select 1 as n
union all
select n + 1
from tally
where n < #Count
)
SELECT Column1, Column2, Column3, Column4, Column5
FROM tally
LEFT JOIN
(
SELECT Column1, ROW_NUMBER() OVER (ORDER BY Column1) rn
FROM
(
SELECT DISTINCT Column1
FROM YourTable
) t1
) d1 ON(n = d1.rn)
LEFT JOIN
(
SELECT Column2, ROW_NUMBER() OVER (ORDER BY Column2) rn
FROM
(
SELECT DISTINCT Column2
FROM YourTable
) t1
) d2 ON(n = d2.rn)
LEFT JOIN
(
SELECT Column3, ROW_NUMBER() OVER (ORDER BY Column3) rn
FROM
(
SELECT DISTINCT Column3
FROM YourTable
) t1
) d3 ON(n = d3.rn)
LEFT JOIN
(
SELECT Column4, ROW_NUMBER() OVER (ORDER BY Column4) rn
FROM
(
SELECT DISTINCT Column4
FROM YourTable
) t1
) d4 ON(n = d4.rn)
LEFT JOIN
(
SELECT Column5, ROW_NUMBER() OVER (ORDER BY Column5) rn
FROM
(
SELECT DISTINCT Column5
FROM YourTable
) t1
) d5 ON(n = d5.rn)
Dynamic version:
DECLARE #TableName sysname = 'YourTableName'
DECLARE #Sql nvarchar(max) =
'
DECLARE #Count int
select #Count = COUNT(1)
FROM '+ #TableName +'
;with tally as (
select 1 as n
union all
select n + 1
from tally
where n < #Count
)
SELECT '
SELECT #Sql = #Sql + Column_Name +','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
SELECT #Sql = LEFT(#Sql, LEN(#Sql) - 1) + ' FROM tally t'
SELECT #Sql = #Sql + ' LEFT JOIN (SELECT '+ Column_Name +', ROW_NUMBER() OVER (ORDER BY ' + Column_Name +') rn
FROM
(
SELECT DISTINCT '+ Column_Name +' FROM '+ #TableName +') t
) c_'+ Column_Name + ' ON(n = c_'+ Column_Name + '.rn)'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
EXEC(#Sql)
Update
Tested on a table with 22 columns and 47,000 rows, my suggestion took 46 seconds when using a proper tally table. on Sql server 2014.
I was surprised - I thought it would take at least 2-3 minutes.
Here's a dynamic set I was working on. I'm running out of time so it's not cleaned up, and it determines the dynamic row numbers by the max number of rows in the table as a whole, meaning that if you have any duplicates in any column at all, you'll be left with rows where every single column is null.
But other than that, this should work perfectly fine, and the script contains the necessary info showing you how to concatenate a final "WHERE S1.COLNAME IS NOT NULL AND S2.COLNAME IS NOT NULL AND .." filter to the result table, to eliminate those full-null rows.
Other than that, here's the script. It's gonna be heavy, obviously, so I included a (nolock) hint in it, and a "WHERE ColName is not null" to remove useless results.
Try this on a smaller table and see it work.
/*
Set your table and schema on #MYTABLE and #MYSCHEMA variables.
*/
SET NOCOUNT ON
DECLARE #MYTABLE SYSNAME = 'Mytablename here'
, #MYSCHEMA sysname = 'dbo'
DECLARE #SQL NVARCHAR(MAX) = '', #COLNAME sysname = '', #MYCOLS NVARCHAR(max) = ''
DECLARE #COL_NOW INT = 1, #COL_MAX INT =
(SELECT COUNT(*)
FROM sys.columns
WHERE object_id = (SELECT object_id FROM sys.tables where name = #MYTABLE and SCHEMA_NAME(schema_id) = #MYSCHEMA))
SELECT #COLNAME = name
FROM sys.columns
WHERE column_id = 1
and object_id = (SELECT object_id FROM sys.tables where name = #MYTABLE and SCHEMA_NAME(schema_id) = #MYSCHEMA)
SET #SQL = 'FROM
(SELECT ROW_NUMBER() OVER (ORDER BY '+#COLNAME+' ASC) RN
FROM '+#MYSCHEMA+'.'+#MYTABLE+' (nolock)) S'
WHILE #COL_NOW <= #COL_MAX
BEGIN
SELECT #COLNAME = name
FROM sys.columns
WHERE column_id = #COL_NOW
and object_id = (SELECT object_id FROM sys.tables where name = #MYTABLE and SCHEMA_NAME(schema_id) = #MYSCHEMA)
SELECT #SQL = #SQL+'
FULL JOIN
(SELECT DISTINCT DENSE_RANK() OVER (ORDER BY '+#COLNAME+' ASC) RN, '+#COLNAME+'
FROM '+#MYSCHEMA+'.'+#MYTABLE+' (nolock)
WHERE '+#COLNAME+' IS NOT NULL) S'+CAST(#COL_NOW AS NVARCHAR(25))+' ON S'+CAST(#COL_NOW AS NVARCHAR(25))+'.RN = S.RN'
IF #COL_NOW = 1
SELECT #MYCOLS = #MYCOLS+' S'+CAST(#COL_NOW AS NVARCHAR(25))+'.'+#COLNAME
ELSE
SELECT #MYCOLS = #MYCOLS+', S'+CAST(#COL_NOW AS NVARCHAR(25))+'.'+#COLNAME
SET #COL_NOW = #COL_NOW+1
END
SELECT #SQL = 'SELECT'+#MYCOLS+'
'+#SQL+'
ORDER BY S1.RN ASC';
--PRINT(#SQL); -- To check resulting dynamic SQL without executing it (Warning, print will only show first 8k characters)
EXEC sp_executesql #SQL;
GO

SQL- How to write dynamic where cluse using table values

I have a temp table like: #temp321
select * from #temp321
it's return value like
Field1|Field2|Field3|....|FieldN|
--------------------------------
Value1|Value2|Value3|....|ValueN|
This #temp321 table is auto generated from another set of query.So, the number of #temp321 table's column can be vary(1-25). But their will be always a single row. Now i need to write a where statement using those value.
Example :
where Field1='Value1' and Field2='Value2' and Field3='Value3'.....FieldN='ValueN'..(depends on number of column available into the #temp321 table)
How i do it. Thanks in advance
You can do this:
SELECT *
FROM Othertable t1
INNER JOIN #temp321 t2 ON t1.Field1 = t2.field1
AND t1.Field2 = t2.field2
AND t1.Field3 = t2.field3;
Update: This what I could do:
JOIN the two tables dynamically instead of the WHERE clause:
DECLARE #DynamicJOINCondition AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
;WITH cte
AS
(
SELECT t1.COLUMN_NAME 'T1', t2.COLUMN_NAME 'T2'
FROM
(
SELECT
ordinal_position ,
column_name
FROM information_schema.columns
WHERE table_name LIKE '#temp321%'
) t1
INNER JOIN
(
SELECT
ordinal_position ,
column_name
FROM information_schema.columns
WHERE table_name = 'othertable'
) t2 ON t1.ORDINAL_POSITION = t2.ORDINAL_POSITION
)
SELECT #DynamicJOINCondition = STUFF((SELECT distinct ' AND ' +
' t1.' + T1 + ' = ' + 't2.' + T2
FROM cte
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 4,1,'');
-- Remove the first AND
SELECT #DynamicJOINCondition = RIGHT(#DynamicJOINCondition,
LEN(#DynamicJOINCondition) - 4);
SET #query = 'SELECT COUNT(*) ' +
' FROM #temp321 t1 INNER JOIN Othertable t2 ON ' +
#DynamicJOINCondition;
EXECUTE(#query);
How this works?
First I generated the JOIN condition dynamically, by getting the list of the columns names from the temp table and the other list of columns' names from the other table. Note that it. I used the columns' ORDINAL_POSITIONs, which is a metadata stored in the table information_schema.columns for each column in the database, to compare the list of columns names from the two tables. For exapmle the column with the ordincary postition 1 will be joined with the column name with the ordinary position 1 from the second table, and so so. So, you have to watch out the temp table columns positions so that the columns listed in the same order of the other columns list of the second table.
Update2:
Generate the WHERE clause dynamically:
If you want to generate the WHERE clause dynamically instead of using JOIN, in this case it will need more work. Like so:
DECLARE #DynamicWHERECondition AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
;WITH cte
AS
(
SELECT t1.COLUMN_NAME 'T1', t2.COLUMN_NAME 'T2'
FROM
(
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name LIKE '#temp321%'
) t1
INNER JOIN
(
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name = 'othertable'
) t2 ON t1.ORDINAL_POSITION = t2.ORDINAL_POSITION
), cte2
AS
(
SELECT t2, fieldvalue
FROM cte t1
INNER JOIN
(
SELECT FieldName, fieldvalue
FROM
(
SELECT field1, field2, field3, field4
FROM #temp321
) p
UNPIVOT
(
fieldvalue FOR FieldName IN (field1, field2, field3, field4)
) AS u
) t2 ON t1.T2 = t2.FieldName
)
SELECT #DynamicWHERECondition = STUFF((SELECT distinct ' AND ' +
T2 + ' = ' + '''' + fieldvalue + ''''
FROM cte2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 4,1,'');
SELECT #DynamicWHERECondition = RIGHT(#DynamicWHERECondition, LEN(#DynamicWHERECondition) - 4);
SET #query = 'SELECT COUNT(*) ' +
' FROM Othertable t2 WHERE ' + #DynamicWHERECondition;
EXECUTE(#query);
How this works?
Since the temp table contains only one rows with the values that will be used to form the dynamically created WHERE clause, I UNPIVOTed this one rows into two columns: fieldname: field1, field2, ... and fieldvalue: value1, value2, ....
Later I joined this unpivoted columns with the two tables I used with my first query:
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name LIKE '#temp321%';
and:
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name = 'othertable';
With the join condition be the same as the condition in the first case, by the ordinal positions of the columns in the two tables.
Generate the WHERE clause dynamically, with dynamically UNPIVOTing the temp tables' values:
But the previous approach has a big problem. The selection of
You need to unpivot the table temp dynamically like so:
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name LIKE '#temp321%'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SET #dynamicunpivotquery = ' SELECT FieldName, fieldvalue ' +
' FROM ' +
' ( SELECT * FROM #temp321 ' +
' ) p ' +
' UNPIVOT ' +
' ( ' +
' fieldvalue FOR FieldName IN (' + #cols + ' ) ' +
' ) AS u ';
But, in order to use the dynamically unpivoted table later in our query, you have to create a temp table:
DECLARE #unpivotedTable TABLE(FieldName varchar(50), fieldvalue VARCHAR(50));
INSERT INTO #unpivotedTable
exec(#dynamicunpivotquery);
So that you could use it like so:
SELECT t2, fieldvalue
FROM cte t1
INNER JOIN #unpivotedTable t2 ON t1.T2 = t2.FieldName
Here is the updated sql with dynamic unpivot:
DECLARE #DynamicWHERECondition AS NVARCHAR(MAX);
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #dynamicunpivotquery AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
DECLARE #unpivotedTable TABLE(FieldName varchar(50), fieldvalue VARCHAR(50));
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name LIKE '#temp321%'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SET #dynamicunpivotquery = ' SELECT FieldName, fieldvalue ' +
' FROM ' +
' ( SELECT * FROM #temp321 ' +
' ) p ' +
' UNPIVOT ' +
' ( ' +
' fieldvalue FOR FieldName IN (' + #cols + ' ) ' +
' ) AS u ';
INSERT INTO #unpivotedTable
exec(#dynamicunpivotquery);
;WITH cte
AS
(
SELECT t1.COLUMN_NAME 'T1', t2.COLUMN_NAME 'T2'
FROM
(
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name LIKE '#temp321%'
) t1
INNER JOIN
(
SELECT
ordinal_position,
column_name
FROM information_schema.columns
WHERE table_name = 'othertable'
) t2 ON t1.ORDINAL_POSITION = t2.ORDINAL_POSITION
), cte2
AS
(
SELECT t2, fieldvalue
FROM cte t1
INNER JOIN #unpivotedTable t2 ON t1.T2 = t2.FieldName
)
SELECT #DynamicWHERECondition = STUFF((SELECT distinct ' AND ' +
T2 + ' = ' + '''' + fieldvalue + ''''
FROM cte2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 4,1,'');
SELECT #DynamicWHERECondition = RIGHT(#DynamicWHERECondition,
LEN(#DynamicWHERECondition) - 4);
SET #query = 'SELECT COUNT(*) ' +
' FROM Othertable t2 WHERE ' + #DynamicWHERECondition;
EXECUTE(#query);