I was looking at the MERGE command which seems cool but still it requires the columns to be specified. I'm looking for something like:
MERGE INTO target AS t
USING source AS s
WHEN MATCHED THEN
UPDATE SET
[all t.fields = s.fields]
WHEN NOT MATCHED THEN
INSERT ([all fields])
VALUES ([all s.fields])
Is it possible?
I'm lazy... this is a cheap proc I wrote that will spit out a general MERGE command for a table. It queries information_schema.columns for column names. I ripped out my source database name - so, you have to update the proc to work with your database (look for #SourceDB... I said it was cheap.) Anyway, I know others could write it much better - it served my purpose well. (It makes a couple assumptions that you could put logic in to handle - namely turning IDENTITY_INSERT OFF - even when a table doesn't have identity columns.) It updates the table in your current context. It was written against sql server 2008 to sync up some tables. Use at your own risk, of course.
CREATE PROCEDURE [dbo].[GenerateMergeSQL]
#TableName varchar(100)
AS
BEGIN
SET NOCOUNT ON
declare #sql varchar(5000),#SourceInsertColumns varchar(5000),#DestInsertColumns varchar(5000),#UpdateClause varchar(5000)
declare #ColumnName varchar(100), #identityColName varchar(100)
declare #IsIdentity int,#IsComputed int, #Data_Type varchar(50)
declare #SourceDB as varchar(200)
-- source/dest i.e. 'instance.catalog.owner.' - table names will be appended to this
-- the destination is your current db context
set #SourceDB = '[mylinkedserver].catalog.myDBOwner.'
set #sql = ''
set #SourceInsertColumns = ''
set #DestInsertColumns = ''
set #UpdateClause = ''
set #ColumnName = ''
set #isIdentity = 0
set #IsComputed = 0
set #identityColName = ''
set #Data_Type = ''
DECLARE #ColNames CURSOR
SET #ColNames = CURSOR FOR
select column_name, COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as IsIdentity ,
COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsComputed') as IsComputed , DATA_TYPE
from information_schema.columns where table_name = #TableName order by ordinal_position
OPEN #ColNames
FETCH NEXT FROM #ColNames INTO #ColumnName, #isIdentity, #IsComputed, #DATA_TYPE
WHILE ##FETCH_STATUS = 0
BEGIN
if #IsComputed = 0 and #DATA_TYPE <> 'timestamp'
BEGIN
set #SourceInsertColumns = #SourceInsertColumns +
case when #SourceInsertColumns = '' THEN '' ELSE ',' end +
'S.' + #ColumnName
set #DestInsertColumns = #DestInsertColumns +
case when #DestInsertColumns = '' THEN '' ELSE ',' end +
#ColumnName
if #isIdentity = 0
BEGIN
set #UpdateClause = #UpdateClause +
case when #UpdateClause = '' THEN '' ELSE ',' end
+ #ColumnName + ' = ' + 'S.' + #ColumnName + char(10)
END
if #isIdentity = 1 set #identityColName = #ColumnName
END
FETCH NEXT FROM #ColNames INTO #ColumnName, #isIdentity, #IsComputed, #DATA_TYPE
END
CLOSE #ColNames
DEALLOCATE #ColNames
SET #sql = 'SET IDENTITY_INSERT ' + #TableName + ' ON;
MERGE ' + #TableName + ' AS D
USING ' + #SourceDB + #TableName + ' AS S
ON (D.' + #identityColName + ' = S.' + #identityColName + ')
WHEN NOT MATCHED BY TARGET
THEN INSERT(' + #DestInsertColumns + ')
VALUES(' + #SourceInsertColumns + ')
WHEN MATCHED
THEN UPDATE SET
' + #UpdateClause + '
WHEN NOT MATCHED BY SOURCE
THEN DELETE
OUTPUT $action, Inserted.*, Deleted.*;
SET IDENTITY_INSERT ' + #TableName + ' OFF'
Print #SQL
END
Not everything you wanted, but partially:
WHEN NOT MATCHED THEN
INSERT([all fields])
VALUES (field1, field2, ...)
(The values list has to be complete, and match the order of the fields in your table's definition.)
Simple alternative to merge without naming any fields or having to update statement whenever table design changes. This is uni-directional from source to target, but it can be made bi-directional. Only acts on changed records, so it is very fast even with linked servers on slower connection.
--Two statement run as transaction batch
DELETE
C
FROM
productschina C
JOIN
(select * from productschina c except select * from productsus) z
on c.productid=z.productid
INSERT into productschina select * from productsus except select * from productschina
Here is code to setup tables to test above:
--Create a target table
--drop table ProductsUS
CREATE TABLE ProductsUS
(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100),
Rate MONEY
)
GO
--Insert records into target table
INSERT INTO ProductsUS
VALUES
(1, 'Tea', 10.00),
(2, 'Coffee', 20.00),
(3, 'Muffin', 30.00),
(4, 'Biscuit', 40.00)
GO
--Create source table
--drop table productschina
CREATE TABLE ProductsChina
(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100),
Rate MONEY
)
GO
--Insert records into source table
INSERT INTO ProductsChina
VALUES
(1, 'Tea', 10.00),
(2, 'Coffee', 25.00),
(3, 'Muffin', 35.00),
(5, 'Pizza', 60.00)
GO
SELECT * FROM ProductsUS
SELECT * FROM ProductsChina
GO
I think this answer deserves a little more love. It's simple, elegant and works. However, depending on the tables in question, it may be a little bit slow because the except clause is evaluating every column.
I suspect you can save a little bit of time by just joining on the primary key and the last modified date (if one exists).
DELETE
C
FROM
productschina C
JOIN
(select primary_key, last_mod_date from productschina c except select primary_key, last_mod_date from productsus) z
on c.productid=z.productid
INSERT into productschina select * from productsus except select * from productschina
Related
I am trying to log data changes in MS SQL with trigger. I want to create a new History table in every month. After I found the answer how to change table name Dynamically I can't access the DELETED and INSERTED tables anymore. It says invalid object name.
ALTER TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare #hist nvarchar(40)
set #hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
declare #DynamicSql1 nvarchar(2000)
declare #DynamicSql2 nvarchar(2000)
set #DynamicSql1 = N'IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N''[History][dbo].[#hist]'')
AND OBJECTPROPERTY(id, N''IsUserTable'') = 1)
CREATE TABLE [History].[dbo].[#hist] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql #DynamicSql1, N'#hist nvarchar(40)', #hist=#hist
set #DynamicSql2 = N'INSERT INTO [History].[dbo].[#hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM DELETED
INSERT INTO [History].[dbo].[#hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM INSERTED'
Exec sp_executesql #DynamicSql2, N'#hist nvarchar(40)', #hist=#hist
Thanks for the answers in advance.
Dynamic sql is executed in his own scope, so you can't acces inserted/deleted objects.
You could write a SQLCLR trigger in C# look this example SQLCLR Trigger
but I think the easiest way is to use a temp table to write changes to, so the dynamic part is fixed.
Take a look:
DROP TRIGGER [test_history]
GO
CREATE TRIGGER [test_history] ON [test_table]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
declare #date datetime = getdate()
declare #guid uniqueidentifier = newid()
declare #hist nvarchar(40)= 'test_history_' + CAST(YEAR(#date ) as VARCHAR(4))+ '_' + right('0' + CAST(Month(#date) as VARCHAR(2)), 2)
DECLARE #T1 BIT = 0
SELECT top 1 #T1 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = 'test_history_9999_99'
IF #T1 = 1 TRUNCATE table test_history_9999_99
DECLARE #T2 BIT = 0
SELECT top 1 #T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = #hist
IF #T1=0 BEGIN
SELECT ID, [desc], #date DATE_TIME, cast('delete' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
INTO test_history_9999_99
FROM DELETED
END else begin
INSERT INTO test_history_9999_99
SELECT ID, [desc], #date, cast('delete' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
FROM DELETED
end
INSERT INTO test_history_9999_99
SELECT ID, [desc], #date, cast('insert' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
FROM inserted
IF #T2 = 0 BEGIN
EXEC sp_rename 'test_history_9999_99', #hist
END ELSE BEGIN
declare #DynamicSql nvarchar(2000)
SET #DynamicSql = 'INSERT INTO ' + #hist + ' SELECT * FROM test_history_9999_99;'
Exec sp_executesql #DynamicSql
END
END
My test_table contains only two columns ID and [Desc].
In the history tables I have added a DATETIME column with change date and a UNIQUEIDENTIFIER column so you can group all changes in a batch if you INSERT/UPDATE many records with a single operation
Tanks for the answer #MtwStark. Now it works, I can check if the table exists, and create it if not. And have eaccess to the DELETED and INSERTED tables.
I'm not sure, if in your solution I have to create the test_history_9999_99 table in advance. Because when I used your trigger I've got an error about column insertion (I didn't understand the error completly).
Now my code looks like this. I'm not sure if it can handle INSERT/UPDATE many records with a single operation. Probably I still need to insert this code for it? CAST(#guid AS varchar(64)) BATCH . I'm not sure what it really does, I have to look into it deeper.
CREATE TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare #hist nvarchar(40)
set #hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
select * into #ins from inserted
select * into #del from deleted
declare #DynamicSql nvarchar(2000)
DECLARE #T2 BIT = 0
SELECT top 1 #T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = #hist
if #T2=0 begin
set #DynamicSql = N'CREATE TABLE [' + #hist + '] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql #DynamicSql
end
set #DynamicSql = N'INSERT INTO ' + #hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM #del
INSERT INTO ' + #hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM #ins'
Exec sp_executesql #DynamicSql
Try refreshing intellisense. Ctrl+Shift+R see if that might help. Or do a database table refresh.
If you have SQL Server enterprise (check your version) Then better way will be to enable CDC.
https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx
I have a master table which contains the table names and columns corresponding to that table.
I want to write a procedure which iterates through all the records of tables and gets all the data and returns it as a single result set.
You need to use Dynamic Query
DECLARE #sql VARCHAR(max)=''
SET #sql = (SELECT #sql + 'select ' + column_name + ' from '
+ table_name + ' union all '
FROM master_table
FOR xml path(''))
SELECT #sql = LEFT(#sql, Len(#sql) - 9)
EXEC (#sql)
Note : The datatype of all the columns should be same. If it is not the case then you may have to do explicit conversion to varchar
SET #sql = (SELECT #sql + 'select cast(' + column_name + ' as varchar(4000)) from '
+ table_name
+ ' union all '
FROM Master_table
FOR xml path(''))
Assuming that all tables listed in your Master table is having same columns with same order and data types. Then it will be as follows:
create table ##a
(
Value int
)
create table ##b
(
Value int
)
create table ##c
(
Value int
)
declare #all table
(
Value int
)
declare #master table
(
TableName varchar(10)
)
declare #TableName varchar(10)
insert ##a values (1), (2), (3)
insert ##b values (4), (5), (6)
insert ##c values (7), (8), (9)
insert #master values ('##a'), ('##b'),('##c')
declare looper cursor local static forward_only read_only for
select TableName from #master
open looper
fetch next from looper into #TableName
while ##fetch_status = 0
begin
insert #all exec('select Value from ' + #TableName)
fetch next from looper into #TableName
end
close looper
deallocate looper
select * from #all
drop table ##a
drop table ##b
drop table ##c
If the tables are of different structures, please visit Stored procedures and multiple result sets in T-SQL. It will squeeze the content of each table into a single XML cell. The article also explains how to read them back.
I assume that you are using many tables with different columns in your master table. You should loop your master table. Try like this,
DECLARE #sql NVARCHAR(max) = ''
DECLARE #start INT = 1
,#end INT = 0
,#tablename VARCHAR(100) = ''
DECLARE #TableList TABLE (
id INT identity(1, 1)
,tablename VARCHAR(128)
)
INSERT INTO #TableList (tablename)
SELECT DISTINCT table_name
FROM YourMasterTableName
WHERE TABLE_NAME = 'productss'
SET #end = ##ROWCOUNT
WHILE (#start <= #end)
BEGIN
SET #tablename = (
SELECT tablename
FROM #TableList
WHERE id = #start
)
SET #sql = (
SELECT ',[' + column_name + ']'
FROM YourMasterTableName M
WHERE TABLE_NAME = #tablename
FOR XML path('')
)
SET #sql = 'SELECT ' + stuff(#sql, 1, 1, '') + ' FROM ' + #tablename
EXEC sp_executesql #sql
SET #start = #start + 1
END
I have a SQL Server database and whenever I want to make a backup of the database, first generate a Drop and Create Script of the DB. Because by this way I can make my database in every version of SQL Server. Regardless of the version and maintenance problems of SQL.
In the Generate and Publish Scripts window of SQL Server there is an Advanced Scripting Option, as shown below:
Now, I would like a script from this window to reproduce script generator advanced options. In the other words, I want a script to make script generator of my database by my selected Advanced Options.
How can I do it?
You can use SQL Server Profiler to retrieve executing sqls in the specified instance.
[
Please refer to this How to generate SQL Table Script through query
Is this what you want?
declare #table varchar(100)
set #table = 'MyTable' -- set table name here
declare #sql table(s varchar(1000), id int identity)
-- create statement
insert into #sql(s) values ('create table [' + #table + '] (')
-- column list
insert into #sql(s)
select
' ['+column_name+'] ' +
data_type + coalesce('('+cast(character_maximum_length as varchar)+')','') + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=#table
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(#table) as varchar) + ',' +
cast(ident_incr(#table) as varchar) + ')'
else ''
end + ' ' +
( case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
coalesce('DEFAULT '+COLUMN_DEFAULT,'') + ','
from information_schema.columns where table_name = #table
order by ordinal_position
-- primary key
declare #pkname varchar(100)
select #pkname = constraint_name from information_schema.table_constraints
where table_name = #table and constraint_type='PRIMARY KEY'
if ( #pkname is not null ) begin
insert into #sql(s) values(' PRIMARY KEY (')
insert into #sql(s)
select ' ['+COLUMN_NAME+'],' from information_schema.key_column_usage
where constraint_name = #pkname
order by ordinal_position
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
insert into #sql(s) values (' )')
end
else begin
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
end
-- closing bracket
insert into #sql(s) values( ')' )
-- result!
select s from #sql order by id
In DB I have #temp tables with data and I need to generate insert Scripts (for all data).
How it can be done ? I right clicked on tempDB and selected > Tasks > Generate Scripts but I can't select #temp tables to generate script (they are not avaialble to select).
how I can geneate Insert script from #temp tables I m using SQL Server 2008 R2.
You can insert your query results into sql table (temporary table , it will be created automatically ):
SELECT * INTO myTempTable FROM (query results)
e.g : SELECT * INTO myTempTable FROM user where condition
A table named myTempTable will be created inside schema dbo
Then click on database click :
Tasks > Generate Scripts
and you choose the table myTempTable
Another best way to do it just bit faster but longer.
Use SqlPubWiz
Just go:
C:\Program Files (x86)\Microsoft SQL Server\90\Tools\Publishing\1.4
and run and fill required information regarding log in into database and choose your database and get your whole database script and find the table's insert script that you wanted in your saved script file.
You can use the following query batch for generating scripts for temp tables and you can select the rows based on the conditions. I have got this from here. In the original source, the Author created Stored Procedure for generating scripts. I have formatted and modified for declaring TableName with Condition. You need to run this query batch in tempdb with your #temp table name. Thanks to Neeraj Prasad Sharma.
DECLARE #QUERY VARCHAR(MAX) = 'Dbo.#Temp where 1 = 1'
SET NOCOUNT ON
DECLARE #WithStrINdex AS INT
DECLARE #WhereStrINdex AS INT
DECLARE #INDExtouse AS INT
DECLARE #SchemaAndTAble VARCHAR(270)
DECLARE #Schema_name VARCHAR(30)
DECLARE #Table_name VARCHAR(240)
DECLARE #Condition VARCHAR(MAX)
SELECT #WithStrINdex = 0
SELECT #WithStrINdex = CHARINDEX('WITH', #Query), #WhereStrINdex = CHARINDEX('WHERE', #Query)
IF(#WithStrINdex != 0)
SELECT #INDExtouse = #WithStrINdex
ELSE
SELECT #INDExtouse = #WhereStrINdex
SELECT #SchemaAndTAble = LEFT(#Query, #INDExtouse - 1)
SELECT #SchemaAndTAble = LTRIM(RTRIM(#SchemaAndTAble))
SELECT #Schema_name = LEFT(#SchemaAndTAble, CHARINDEX('.', #SchemaAndTAble ) - 1)
,#Table_name = SUBSTRING(#SchemaAndTAble, CHARINDEX('.', #SchemaAndTAble ) + 1, LEN(#SchemaAndTAble))
,#CONDITION = SUBSTRING(#Query, #WhereStrINdex + 6, LEN(#Query))--27+6
DECLARE #COLUMNS TABLE([Row_number] SMALLINT, Column_Name VARCHAR(MAX))
DECLARE #CONDITIONS AS VARCHAR(MAX)
DECLARE #Total_Rows AS SMALLINT
DECLARE #Counter AS SMALLINT
DECLARE #ComaCol AS VARCHAR(MAX)
SELECT #ComaCol = '', #Counter = 1, #CONDITIONS = ''
print #Schema_name
print #Table_name
INSERT INTO #COLUMNS
SELECT ROW_NUMBER() OVER(ORDER BY ORDINAL_POSITION) [Count] ,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = #Schema_name
AND TABLE_NAME = #Table_name
AND COLUMN_NAME NOT IN ('SYNCDESTINATION','PENDINGSYNCDESTINATION' ,'SKUID','SALECREDITEDTO')
SELECT #Total_Rows = COUNT(*) FROM #COLUMNS
SELECT #Table_name = '['+#Table_name+']'
SELECT #Schema_name = '['+#Schema_name+']'
WHILE (#Counter< = #Total_Rows )
BEGIN
SELECT #ComaCol = #ComaCol + ' ['+Column_Name+'],' FROM #COLUMNS
Where [Row_number] = #Counter
SELECT #CONDITIONS = #CONDITIONS+ ' + CASE WHEN ['+Column_Name+'] IS NULL THEN ''NULL'' ELSE '''''''' +
REPLACE( CONVERT(VARCHAR(MAX),['+Column_Name+']) ,'''''''','''')
+'''''''' END +'+''','''
FROM #COLUMNS WHERE [Row_number] = #Counter
SET #Counter = #Counter + 1
END
SELECT #CONDITIONS = RIGHT(#CONDITIONS, LEN(#CONDITIONS) -2)
SELECT #CONDITIONS = LEFT(#CONDITIONS, LEN(#CONDITIONS) -4)
SELECT #ComaCol = SUBSTRING (#ComaCol, 0, LEN(#ComaCol))
SELECT #CONDITIONS = '''INSERT INTO ' + #Schema_name + '.' + #Table_name + '(' + #ComaCol + ')' +' VALUES( '+'''' + '+' + #CONDITIONS
SELECT #CONDITIONS = #CONDITIONS + '+' + ''')'''
SELECT #CONDITIONS = 'SELECT' + #CONDITIONS + 'FROM' + #Schema_name + '.' + #Table_name + ' WITH(NOLOCK) ' + ' WHERE ' + #Condition
PRINT(#CONDITIONS)
EXEC(#CONDITIONS)
You have to right click on database and click on
Tasks -> Generate Scripts
now you are having a popup screen go to next click
Select specific database objects
now select your table from Tables option and click next now you have Advanced button there click on it.
You will have another small popup screen available choose
Types of data to script and select
Data only
click OK and don't forget to see the path as file name where your script save carefully.
Now click Next and again Next your script is ready with the data.
Does anyone know how to check a a variable against all database table with columns storing the same type of information? I have a poorly designed database that stores ssn in over 60 tables within one database. some of the variations of columns in the various tables include:
app_ssn
ca_ssn
cand_ssn
crl_ssn
cu_ssn
emtaddr_ssn
re_ssn
sfcart_ssn
sfordr_ssn
socsecno
ssn
Ssn
SSN
I want to create a stored procedure that will accept a value and check it against every table that has 'ssn' in the name.Does anyone have idea as to how to do this?
-- I assume that table/column names don't need to be surrounded by square braces. You may want to save matches in a table - I just select them. I also assume ssn is a char.
alter proc proc1
#search1 varchar(500)
as
begin
set nocount on
declare #strsql varchar(500)
declare #curtable sysname
declare #prevtable sysname
declare #column sysname
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
order by table_schema+'.'+table_name +column_name
-- make sure that at least one column has ssn in the column name
if #curtable is not null
begin
while (1=1)
begin
set #strsql = 'select * from ' +#curtable +' where '+''''+#search1+''''+ ' = '+#column
print #strsql
-- any matches for passed in ssn will match here...
exec (#strsql)
set #prevtable = #curtable+#column
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
and table_schema+'.'+table_name +column_name> #prevtable
order by table_schema+'.'+table_name +column_name
-- when we run out of columns that contain ssn we are done...
if ##ROWCOUNT = 0
break
end
end
end
What you will need to do is some research. But here is where you can start;
SELECT tbl.NAME AS TableName
,cl.NAME AS ColumnName
,IDENTITY(INT, 1, 1) AS ID
INTO #ColumnsToLoop
FROM sys.tables tbl
JOIN sys.columns cl ON cl.object_id = tbl.object_id
This will give you the table / column relation then you can simply build a dynamic SQL string based on each row in the query above (basically loop it) and use EXEC or sp_execsql. So basically;
DECLARE #Loop int = (select min(ID) From #ColumnsToLoop),#MX int = (Select MAX(ID) From #ColumnsToLoop)
WHILE(#Loop<=#MX)
BEGIN
DECLARE #SQL nvarchar(MAX) = 'SQL String'
//Construct the dynamic SQL String
EXEC(#SQL);
SET #Loop += 1
END
Perhaps I went a little too crazy with this one, but let me know. I thought it would best the primary key of the search results with the table name so you could join it to your tables. I also managed to do it without a single cursor or loop.
DECLARE #SSN VARCHAR(25) = '%99%',
#SQL VARCHAR(MAX);
WITH CTE_PrimaryKeys
AS
(
SELECT TABLE_CATALOG,
TABLE_SCHEMA,
TABLE_NAME,
column_name
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE D
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
),
CTE_Columns
AS
(
SELECT A.*,
CONCAT(A.TABLE_CATALOG,'.',A.TABLE_SCHEMA,'.',A.TABLE_NAME) AS FullTableName,
CASE WHEN B.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IsPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS A
LEFT JOIN CTE_PrimaryKeys B
ON A.TABLE_CATALOG = B.TABLE_CATALOG
AND A.TABLE_SCHEMA = B.TABLE_SCHEMA
AND A.TABLE_NAME = B.TABLE_NAME
AND A.COLUMN_NAME = B.COLUMN_NAME
),
CTE_Select
AS
(
SELECT
'SELECT ' +
--This returns the pk_col casted as Varchar and the table name in another columns
STUFF((SELECT ',CAST(' + COLUMN_NAME + ' AS VARCHAR(MAX)) AS pk_col,''' + B.TABLE_NAME + ''' AS Table_Name'
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
AND B.IsPrimaryKey = 1
FOR XML PATH ('')),1,1,'')
+ ' FROM ' + fullTableName +
--This is where I list the columns where LIKE desired SSN
' WHERE ' +
STUFF((SELECT COLUMN_NAME + ' LIKE ''' + #SSN + ''' OR '
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
--This is where I filter so I only get desired columns
AND (
--Uncomment the Collate if your database is case sensitive
COLUMN_NAME /*COLLATE SQL_Latin1_General_CP1_CI_AS*/ LIKE '%ssn%'
--list your column Names that don't have ssn in them
--OR COLUMN_NAME IN ('col1','col2')
)
FOR XML PATH ('')),1,0,'') AS Selects
FROM CTE_Columns A
GROUP BY A.FullTableName,A.TABLE_NAME
)
--Unioning them all together and getting rid of last trailing "OR "
SELECT #SQL = COALESCE(#sql,'') + SUBSTRING(selects,1,LEN(selects) - 3) + ' UNION ALL ' + CHAR(13) --new line for easier debugging
FROM CTE_Select
WHERE selects IS NOT NULL
--Look at your code
SELECT SUBSTRING(#sql,1,LEN(#sql) - 11)