SQL- How to write dynamic where cluse using table values - sql

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);

Related

Dynamically generate query in 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

unpivot with dynamic columns plus column names

I'm trying to unpivot a table with a large number of columns in the format of:
PID UID col1 col2 col3...
The dynamic SQL below will get me almost everything except the name of the column. The goal is to fill in the "ID" field with the name of the column from which the unpivot value originated.
-- Build list of cols we want to unpivot (skip PID & UID)
declare #cols nvarchar(max)
select #cols = coalesce(#cols+N',', N'') + quotename(c.name) from syscolumns c
inner join sysobjects o on c.id = o.id and o.xtype = 'u'
where o.name = 'MyTable' and c.name not in ('PID', 'UID') order by c.colid
declare #query nvarchar(max)
select #query = N'
select PID, [UID], ID, Val
from
(
select PID, UID, ''ID'' as ID, ' + #cols + '
from MyTable
where UID <> 0
) as cp
unpivot
(
Val for Vals in (' + #cols + ')
) as up
'
exec sp_executesql #query
I thought maybe I could do some sort of join with syscolumns & MyTable and then do a second unpivot but I haven't been able to figure it out.
Ultimately my query should return
PID UID ID Val
123 456 'col1 name' 'xyz'
123 456 'col2 name' 'def'
123 333 'col1 name' 'fdf'
...
So while I know how to get the name of the columns in order to generate the dynamic SQL for the unpivot, I don't know how to join the name of the columns into the output of the unpivot.
You can reference the column name from the val for col in part of the unpivot. Col gets the column name
Example Fiddle
-- Build list of cols we want to unpivot (skip PID & UID)
declare #cols nvarchar(max)
select #cols = coalesce(#cols+N',', N'') + quotename(c.name) from syscolumns c
inner join sysobjects o on c.id = o.id and o.xtype = 'u'
where o.name = 'MyTable' and c.name not in ('PID', 'UID') order by c.colid
declare #query nvarchar(max)
select #query = N'
select PID, [UID], Col as ID, Val
from
(
select PID, UID, ' + #cols + '
from MyTable
where UID <> 0
) as cp
unpivot
(
Val for Col in (' + #cols + ')
) as up
'
exec sp_executesql #query

How to select column names from multiple tables in SQL server 2008 by avoiding certain columns which reoccur in all tables

I have a database that stores the complete details of the employees of a firm.
I need to select column names from multiple tables in the database which when executed would provide me the columns required to list specific details. So, I tried the following query which returns the columns of the specified table only.
SELECT TABLE_NAME,COLUMN_NAME
FROM mydatabase.INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME=N'TBLemppersonaldetails'
All the tables have a Column named 'EmployeeId' which can be considered as a primary key.
Now, how could i get all required columns from all the tables by avoiding certain columns such as Update Date,Mode that repeats in all the tables.
I would prefer using 'Inner Join' to join all the tables.
For example consider the tables below:
Table1 Table2
EmployeeId Name Address Mode EmployeeId Gender BloodGroup Mode
---------------------------------------- -------------------------------------------
001 abc No.9,vv 1 001 Male O+ve 1
street,
Considering the example above, I require the columns EmployeeId,Name,Address,Gender,BloodGroup to be listed after the execution of the query while excluding the column named 'Mode' from both the tables.
I request you to consider another scenario where the so called 'PrimaryKey' constraint doesn't exist for the 'EmployeeId' Column.
PS: I am using SQL Server 2008
You need to dynamically create a SQL statement and then run that command
Update 22.06.2013
IF OBJECT_ID('dbo.Table1') IS NOT NULL DROP TABLE dbo.Table1
CREATE TABLE dbo.Table1
(
EmployeeId int PRIMARY KEY,
Name varchar(100),
Address varchar(100),
Mode int
)
INSERT dbo.Table1
VALUES(1, 'abc', 'Address', 1)
IF OBJECT_ID('dbo.Table2') IS NOT NULL DROP TABLE dbo.Table2
CREATE TABLE dbo.Table2
(
EmployeeId int PRIMARY KEY,
Gender varchar(100),
BloodGroup varchar(100),
Mode int
)
INSERT dbo.Table2
VALUES(1, 'Male', 'O+ve', 1)
DECLARE #TablesWithAlias TABLE([schema] varchar(10), name varchar(20), alias varchar(10))
INSERT #TablesWithAlias
VALUES('dbo', 'Table1', 't1.'),
('dbo', 'Table2', 't2.')
DECLARE #dsql nvarchar(max)
SELECT #dsql = COALESCE(#dsql + ',', '') + MIN(twa.alias) + CASE WHEN o.IsPrimaryKey IS NOT NULL THEN c.name ELSE c.name END
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
OUTER APPLY (
SELECT CASE WHEN OBJECTPROPERTY(object_id(constraint_name), 'IsPrimaryKey') = 1 THEN 1 END AS IsPrimaryKey
FROM information_schema.key_column_usage k
WHERE s.name = k.table_schema AND t.name = k.table_name
AND c.name = k.COLUMN_NAME
) o
JOIN #TablesWithAlias twa ON s.name = twa.[schema] AND t.name = twa.name
GROUP BY c.column_id, c.name, o.IsPrimaryKey
HAVING COUNT(*) = 1 OR o.IsPrimaryKey IS NOT NULL
ORDER BY c.column_id
PRINT #dsql
--Your 'Inner Join' to join all the tables.
SET #dsql =
'SELECT ' + #dsql +
' FROM dbo.Table1 t1 JOIN dbo.Table2 t2 ON t1.EmployeeId = t2.EmployeeId '
PRINT #dsql
EXEC sp_executesql #dsql
Try this one -
DDL:
SET NOCOUNT ON;
IF OBJECT_ID (N'dbo.Table1') IS NOT NULL
DROP TABLE dbo.Table1
CREATE TABLE dbo.Table1
(
EmployeeId INT
, Name VARCHAR(30)
, [Address] VARCHAR(150)
, Mode INT
, Img IMAGE
, UpdateDate DATETIME
, IpAddress VARCHAR(20)
)
IF OBJECT_ID (N'dbo.Table2') IS NOT NULL
DROP TABLE dbo.Table2
CREATE TABLE dbo.Table2
(
EmployeeId INT
, Gender VARCHAR(6)
, BloodGroup VARCHAR(20)
, Mode INT
)
INSERT INTO dbo.Table1 (EmployeeId, Name, [Address], Mode, UpdateDate, IpAddress)
VALUES ('001', 'abc', 'No.9,vv street', 1,'06/04/2013 12:00:00','192.168.0.1')
INSERT INTO dbo.Table2 (EmployeeId, Gender, BloodGroup, Mode)
VALUES ('001', 'Male', 'O+ve', 1)
Query:
DECLARE #SQL NVARCHAR(MAX)
;WITH cte AS
(
SELECT
column_name = '[' + c.name + ']'
, table_name = '[' + s.name + '].[' + o.name + ']'
, [type_name] = t.name
FROM sys.columns c WITH (NOLOCK)
JOIN sys.objects o WITH (NOLOCK) ON c.[object_id] = o.[object_id]
JOIN sys.schemas s WITH (NOLOCK) ON o.[schema_id] = s.[schema_id]
JOIN sys.types t WITH (NOLOCK) ON c.user_type_id = t.user_type_id
WHERE o.name IN ('Table1', 'Table2')
AND s.name = 'dbo'
AND o.[type] = 'U'
AND c.name NOT IN ('Mode', 'UpdateDate', 'IpAddress')
), unicol AS (
SELECT TOP 1 column_name
FROM cte
GROUP BY cte.column_name
HAVING COUNT(cte.column_name) > 1
), cols AS
(
SELECT DISTINCT column_name, [type_name]
FROM cte
), tbl AS
(
SELECT DISTINCT table_name
FROM cte
), rs AS
(
SELECT
tbl.table_name
, column_name = ISNULL(cte.column_name, cols.column_name + ' = NULL')
FROM cols
CROSS JOIN tbl
LEFT JOIN cte ON cols.column_name = cte.column_name AND cte.table_name = tbl.table_name
), rs2 AS (
SELECT uni = ' UNION ALL' + CHAR(13) + 'SELECT ' + STUFF((
SELECT ', ' + rs.column_name
FROM rs
WHERE tbl.table_name = rs.table_name
GROUP BY rs.column_name
ORDER BY rs.column_name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') +
' FROM ' + table_name
FROM tbl
)
SELECT #SQL = 'SELECT
' + STUFF((
SELECT CHAR(13) + ', ' + ISNULL(unicol.column_name, cols.column_name + ' = MAX('
+ CASE
WHEN [type_name] = 'image' THEN 'CONVERT(VARBINARY(MAX), ' + cols.column_name + ')'
WHEN [type_name] = 'bit' THEN 'CAST(' + cols.column_name + ' AS CHAR(1))'
ELSE cols.column_name
END + ')')
FROM cols
LEFT JOIN unicol ON cols.column_name = unicol.column_name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')
+ '
FROM
(' + STUFF((
SELECT CHAR(10) + uni
FROM rs2
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 11, '') + CHAR(13) +
') t
GROUP BY ' + (SELECT column_name FROM unicol)
PRINT #SQL
EXECUTE sys.sp_executesql #SQL
Output:
Address BloodGroup EmployeeId Gender Img Name
------------------ ------------ ----------- ------ -------- -------
No.9,vv street O+ve 1 Male NULL abc
Search your column name using this :
SELECT OBJECT_NAME (object_id ),*
FROM sys.columns
WHERE name LIKE 'ColumnName'

SQL Compare with one column, but returns all columns if matched

I have the below compare sql code.
Both tables contain a column IDCodeField.
How can I compare only IDCodeField, but return all fields if match IDCodeField?
Currently I am using below, however, it will compare all fields instead of IDCodeField only.
ALTER PROCEDURE [dbo].[usp_comparetables](#table1 varchar(100),
#table2 Varchar(100), #columnlist Varchar(1000))
AS
DECLARE #sql VARCHAR(8000)
SET #sql =
'SELECT ''' + #table1 + ''' AS DataState, * FROM
(SELECT ' + #columnlist + ' FROM ' + #table1 + '
EXCEPT
SELECT ' + #columnlist + ' FROM ' + #table2 + ') x
UNION
SELECT ''' + #table2 + ''' AS DataState, * from
(SELECT ' + #columnlist + ' FROM ' + #table2 + '
INTERSECT
SELECT ' + #columnlist + ' FROM ' + #table1 +') x'
EXEC(#sql)
Used Answer:
DECLARE #sql VARCHAR(8000)
SET #sql =
'SELECT ''' + #table1 + ''' AS DataState, '+#columnlist+' FROM ' + #table1 + ' where '+#compareparameter+' not in (select '+#compareparameter+' from '+#table2+')
UNION ALL
SELECT ''' + #table2 + ''' AS DataState, '+#columnlist+' FROM ' + #table2 + ' where '+#compareparameter+' not in (select '+#compareparameter+' from '+#table1+')'
EXEC(#sql)
I think you want all rows from Table1 and Table2, such that each IDCodeField values only appears in one of the tables or the other. You wish to exclude rows where the same value appears in both tables.
Ignoring, for the moment, the question of what to do if the same value appears in the same table, the simplest query would be:
SELECT * from Table1 T1 full outer join Table2
ON T1.IDCodeField = T2.IDCodeField
WHERE T1.IDCodeField is null or T2.IDCodeField is null
This will give you the results, but possibly not in the format you're seeking - the result rows will be as wide as both tables combined, and the columns from the non-matching table will be NULL.
Or, we could do it in the UNION style from your question.
SELECT * from Table1 where IDCodeField not in (select IDCodeField from Table2)
UNION ALL
SELECT * from Table2 where IDCodeField not in (select IDCOdeField from Table1)
Both of the above queries will return rows if the same IDCodeField value is duplicated only within a single table. If you wish to exclude this possibility, you might try finding the unique values first:
;With UniqueIDs as (
SELECT IDCodeField
FROM (
SELECT IDCodeField from Table1
union all
select IDCodeField from Table2) t
GROUP BY IDCodeField
HAVING COUNT(*) = 1
)
SELECT * from (
SELECT * from Table1
union all
select * from Table2
) t
INNER JOIN
UniqueIDs u
ON
t.IDCodeField = u.IDCodeField
(Of course, all the uses of SELECT * above should be replaced with appropriate column lists)
select * from table1 t1
inner join table2 t2 on t1.IDCodeField = t2.IDCodeField

Flattening of a 1 row table into a key-value pair table

What's the best way to get a key-value pair result set that represents column-value in a row?
Given the following table A with only 1 row
Column1 Column2 Column3 ...
Value1 Value2 Value3
I want to query it and insert into another table B:
Key Value
Column1 Value1
Column2 Value2
Column3 Value3
A set of columns in table A is not known in advance.
NOTE: I was looking at FOR XML and PIVOT features as well as dynamic SQL to do something like this:
DECLARE #sql nvarchar(max)
SET #sql = (SELECT STUFF((SELECT ',' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name='TableA'
ORDER BY column_name FOR XML PATH('')), 1, 1, ''))
SET #sql = 'SELECT ' + #sql + ' FROM TableA'
EXEC(#sql)
A version where there is no dynamic involved. If you have column names that is invalid to use as element names in XML this will fail.
select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
T2.N.value('text()[1]', 'nvarchar(max)') as Value
from (select *
from TableA
for xml path(''), type) as T1(X)
cross apply T1.X.nodes('/*') as T2(N)
A working sample:
declare #T table
(
Column1 varchar(10),
Column2 varchar(10),
Column3 varchar(10)
)
insert into #T values('V1','V2','V3')
select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
T2.N.value('text()[1]', 'nvarchar(max)') as Value
from (select *
from #T
for xml path(''), type) as T1(X)
cross apply T1.X.nodes('/*') as T2(N)
Result:
Key Value
-------------------- -----
Column1 V1
Column2 V2
Column3 V3
Update
For a query with more than one table you could use for xml auto to get the table names in the XML. Note, if you use alias for table names in the query you will get the alias instead.
select X2.N.value('local-name(..)', 'nvarchar(128)') as TableName,
X2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
X2.N.value('text()[1]', 'nvarchar(max)') as Value
from (
-- Your query starts here
select T1.T1ID,
T1.T1Col,
T2.T2ID,
T2.T2Col
from T1
inner join T2
on T1.T1ID = T2.T1ID
-- Your query ends here
for xml auto, elements, type
) as X1(X)
cross apply X1.X.nodes('//*[text()]') as X2(N)
SQL Fiddle
I think you're halfway there. Just use UNPIVOT and dynamic SQL as Martin recommended:
CREATE TABLE TableA (
Code VARCHAR(10),
Name VARCHAR(10),
Details VARCHAR(10)
)
INSERT TableA VALUES ('Foo', 'Bar', 'Baz')
GO
DECLARE #sql nvarchar(max)
SET #sql = (SELECT STUFF((SELECT ',' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name='TableA'
ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))
SET #sql = N'SELECT [Key], Val FROM (SELECT ' + #sql + ' FROM TableA) x '
+ 'UNPIVOT ( Val FOR [Key] IN (' + #sql + ')) AS unpiv'
EXEC (#sql)
Results:
Key Val
------------ ------------
Code Foo
Name Bar
Details Baz
There is a caveat, of course. All your columns will need to be the same data type for the above code to work. If they are not, you will get this error:
Msg 8167, Level 16, State 1, Line 1
The type of column "Col" conflicts with the type of
other columns specified in the UNPIVOT list.
In order to get around this, you'll need to create two column string statements. One to get the columns and one to cast them all as the data type for your Val column.
For multiple column types:
CREATE TABLE TableA (
Code INT,
Name VARCHAR(10),
Details VARCHAR(10)
)
INSERT TableA VALUES (1, 'Foo', 'Baf')
GO
DECLARE
#sql nvarchar(max),
#cols nvarchar(max),
#conv nvarchar(max)
SET #cols = (SELECT STUFF((SELECT ',' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name='TableA'
ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))
SET #conv = (SELECT STUFF((SELECT ', CONVERT(VARCHAR(50), '
+ column_name + ') AS ' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name='TableA'
ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))
SET #sql = N'SELECT [Key], Val FROM (SELECT ' + #conv + ' FROM TableA) x '
+ 'UNPIVOT ( Val FOR [Key] IN (' + #cols + ')) AS unpiv'
EXEC (#sql)
Perhaps you're making this more complicated than it needs to be. Partly because I couldn't wrap my little brain around the number of PIVOT/UNPIVOT/whatever combinations and a dynamic SQL "sea of red" would be necessary to pull this off. Since you know the table has exactly one row, pulling the value for each column can just be a subquery as part of a set of UNIONed queries.
DECLARE #sql NVARCHAR(MAX) = N'INSERT dbo.B([Key], Value) '
SELECT #sql += CHAR(13) + CHAR(10)
+ ' SELECT [Key] = ''' + REPLACE(name, '''', '''''') + ''',
Value = (SELECT ' + QUOTENAME(name) + ' FROM dbo.A) UNION ALL'
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.A');
SET #sql = LEFT(#sql, LEN(#sql)-9) + ';';
PRINT #sql;
-- EXEC sp_executesql #sql;
Result (I only created 4 columns, but this would work for any number):
INSERT dbo.B([Key], Value)
SELECT [Key] = 'Column1',
Value = (SELECT [Column1] FROM dbo.A) UNION ALL
SELECT [Key] = 'Column2',
Value = (SELECT [Column2] FROM dbo.A) UNION ALL
SELECT [Key] = 'Column3',
Value = (SELECT [Column3] FROM dbo.A) UNION ALL
SELECT [Key] = 'Column4',
Value = (SELECT [Column4] FROM dbo.A);
The most efficient thing in the world? Likely not. But again, for a one-row table, and hopefully a one-off task, I think it will work just fine. Just watch out for column names that contain apostrophes, if you allow those things in your shop...
EDIT sorry, couldn't leave it that way. Now it will handle apostrophes in column names and other sub-optimal naming choices.