Declare temp table and insert into in query - sql

I try to call declare temp table in my query but it say
"Must declare the table variable "#MDLTable"."
Here my coding:
DECLARE #dbname AS NVARCHAR(50);
SELECT #dbname = DB_NAME();
PRINT #dbname;
DECLARE #sql NVARCHAR(1000) ;
DECLARE #MDLTable AS TABLE(MDLID BIGINT, MDLRLVR INT)
SET #sql = 'INSERT INTO ' + #MDLTable + ' (MDLID, MDLRLVR)
SELECT DISTINCT a.MDLID, a.MDLRLVR FROM ' + #dbname + '.dbo.EUSYSRSRRL a
INNER JOIN ' + #dbname + '.dbo.EUSYSIROE b ON a.MDLID = b.MDLID
INNER JOIN ' + #dbname + '.dbo.EUSYSEAPML c ON b.EAPMLID = c.EAPMLID
WHERE c.REEID = ' + CONVERT(nvarchar(50),6) + ''
exec (#sql);

You can do this by using insert . . . exec:
SET #sql = '
SELECT DISTINCT a.MDLID, a.MDLRLVR
FROM ' + #dbname + '.dbo.EUSYSRSRRL a JOIN
' + #dbname + '.dbo.EUSYSIROE b
ON a.MDLID = b.MDLID JOIN
' + #dbname + '.dbo.EUSYSEAPML c
ON b.EAPMLID = c.EAPMLID
WHERE c.REEID = ' + CONVERT(nvarchar(50), 6) + '';
INSERT INTO #MDLTable (MDLID, MDLRLVR)
exec(#sql);

Related

SQL Server Dynamic insert script JSON For

43/5000
First of all I apologize for my bad english...
I am trying to write the insert procedures of all tables with a single procedure. But I have a problem like this.
DECLARE #jsondata varchar(MAX)
Set #jsondata='[{"RecordId":1,"CreatedUser":0,"CreatedDate":"2020-03-26T14:49:21.210","UpdatedDate":"2020-03-26T14:57:33.420","UpdatedUser":0,"Status":true,"IsDeleted":false,"Name":"Oyun Konsolları","Icon":"videogame_asset","Description":"Oyun Konsolları","Order":1}]';
DECLARE #cn nvarchar(50)
DECLARE #dt nvarchar(50)
DECLARE #ml nvarchar(50)
DECLARE #inserttext varchar(MAX)
DECLARE #selecttext varchar(MAX)
DECLARE #jsoncol varchar(MAX)
DECLARE #tablename varchar(50)
SET #tablename = 'Categories'
SET #inserttext = ' INSERT INTO '+#tablename+' ( ';
SET #selecttext = ' SELECT ';
SET #jsoncol = ') WITH (';
DECLARE #schema nvarchar(max) = N''
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT
c.name 'Column Name',
t.Name 'Data type',
c.max_length 'Max Length'
FROM
sys.columns c
INNER JOIN
sys.types t ON c.user_type_id = t.user_type_id
WHERE
c.object_id = OBJECT_ID(#tablename)
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #cn,#dt,#ml
WHILE ##FETCH_STATUS = 0
BEGIN
IF(#cn NOT IN('CreatedUser','CreatedDate','UpdatedDate','UpdatedUser','Status','IsDeleted','RecordId','Status','IsDeleted'))
BEGIN
--Do something with Id here
SET #inserttext = #inserttext + '['+#cn + '], ';
SET #selecttext = #selecttext + '['+ #cn + '], ';
IF(#dt = 'varchar' OR #dt='nvarchar' )
BEGIN
SET #jsoncol = #jsoncol + '['+#cn + '] ' + #dt + ' (' +#ml + '), '
END
ELSE
BEGIN
SET #jsoncol = #jsoncol + '['+#cn + '] ' + #dt +', '
END
END
FETCH NEXT FROM MY_CURSOR INTO #cn,#dt,#ml
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
SET #jsoncol = LEFT(#jsoncol, LEN(#jsoncol) - 1)
SET #inserttext = LEFT(#inserttext, LEN(#inserttext) - 1)
SET #selecttext = LEFT(#selecttext, LEN(#selecttext) - 1)
SET #inserttext =#inserttext + ' )';
SET #jsoncol = #jsoncol + ' )';
EXEC( #inserttext + ' '+ #selecttext + ' ' +' FROM OPENJSON('+#jsondata+ #jsoncol);
Error i get after running :
Msg 103, Level 15, State 4, Line 1 The identifier that
starts with
'{"RecordId":1,"CreatedUser":0,"CreatedDate":"2020-03-26T14:49:21.210","UpdatedDate":"2020-03-26T14:57:33.420","UpdatedUser":0,"S'
is too long. Maximum length is 128.
Completion time: 2020-09-25T10:42:41.2474477+03:00
29/5000
Is it possible ?
in short, is it possible to insert into tables from json as dynamic exec
declare #sql nvarchar(max) = #inserttext + ' '+ #selecttext + ' ' +' FROM OPENJSON(#thejsondata'+ #jsoncol;
exec sp_executesql #stmt=#sql, #params=N'#thejsondata nvarchar(max)', #thejsondata = #jsondata;

Can this dynamic SQL be replaced with the regular SQL to improve performance?

I am using the following dynamic query, but see that the performance is slow. I am not a big fan of dynamic SQL, and am looking for, if possible, a good clean and fast SQL alternative for the following. Thanks a million ton in advance! Here are some details:
In the following code, the final table missingfields_xxxx lists out the rows where we have a missing rule field.
table_name has the column rule that holds the column name of the table trans_modelname (this table can be found in the dynamic part of the sql)
DECLARE #rule NVARCHAR(MAX)
DECLARE #PeriodNumber INT = 1
DECLARE #SelectList NVARCHAR(MAX)
DECLARE #WhereList NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #ModelName as NVARCHAR(MAX) = 'modelname'
--DECLARE #MaxPeriods INT = 8
DECLARE #MaxPeriods INT
SELECT #MaxPeriods = count (*)
FROM
(
SELECT [rule]
FROM table_name
WHERE ModelName = #ModelName) ab
DECLARE db_cursor3 CURSOR FOR
SELECT * FROM
(
SELECT [rule]
FROM table_name
WHERE ModelName = #ModelName) cd
OPEN db_cursor3
FETCH NEXT FROM db_cursor3 INTO #rule
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN
SELECT #SelectList = COALESCE(#SelectList + ', ', '') + '' + #rule + ' AS [GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + ']'
SELECT #SelectList as 'Selectlist'
IF #PeriodNumber < #MaxPeriods
BEGIN
SELECT #WhereList = COALESCE(#WhereList, '') + '(isnull([GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + '],'''') = '''' ) OR '
SELECT #WhereList as 'Wherelist where periodnumber < maxperiods'
END
ELSE IF #PeriodNumber = #MaxPeriods
BEGIN
SELECT #WhereList = COALESCE(#WhereList, '') + '(isnull([GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + '], '''') = '''' )'
SELECT #WhereList as 'Wherelist where periodnumber = maxperiods'
END
SET #PeriodNumber = #PeriodNumber + 1
END
FETCH NEXT FROM db_cursor3 INTO #rule
END
CLOSE db_cursor3
DEALLOCATE db_cursor3
-- build dynamic query
SET #SQL =
'SELECT * into missingfields_' + #ModelName + ' from trans_' + #ModelName + '
WHERE id in
(
SELECT id from
(
SELECT id, ' + #SelectList + '
FROM trans_' + #ModelName + ')A
WHERE ' + #WhereList + '
);
SELECT * from missingfields_' + #ModelName
PRINT #SQL
print 'missingfields_' + #ModelName
EXEC sp_executesql #SQL

Dynamic SQL - parametrized query

I use dynamic sql with parameters and have problem :)
In block of code it works, but when I call procedure I have error that I need to declare scalar variable #name_dummy
So:
DECLARE #START_TIME DATETIME = GETDATE()
DECLARE #TMP_NAMES TABLE (
SCHEMA_N VARCHAR(500),
TABLE_N VARCHAR(500),
CONDITION VARCHAR(MAX),
TARGETDB VARCHAR(500),
SOURCEDB VARCHAR(500),
COLUMN_LIST VARCHAR(MAX)
)
DECLARE #SCHEMA_N VARCHAR(500)
DECLARE #TABLE_N VARCHAR(500)
DECLARE #CONDITION VARCHAR(MAX)
DECLARE #COLUMN_LIST VARCHAR(MAX)
DECLARE #TARGETDB VARCHAR(500)
DECLARE #SOURCEDB VARCHAR(500)
INSERT INTO #TMP_NAMES
SELECT
o.SCHEMA_N
,o.TABLE_N
,o.Condition
,o.TargetDB
,o.SourceDB
,STUFF((SELECT
', ' + c.name
FROM sys.columns c
WHERE c.object_id = o.object_id
AND c.system_type_id <> 189 /* Pomijam timestamp*/
ORDER BY c.column_id
FOR XML PATH (''))
, 1, 1, '')
FROM Table_A o
ORDER BY 1,
2
SELECT
SCHEMA_N
,TABLE_N
,CONDITION
,TARGETDB
,SOURCEDB
,COLUMN_LIST
FROM #TMP_NAMES
DECLARE KUR CURSOR
FOR
SELECT
SCHEMA_N
,TABLE_N
,CONDITION
,TARGETDB
,SOURCEDB
,COLUMN_LIST
FROM #TMP_NAMES
OPEN KUR
FETCH NEXT
FROM KUR
INTO #SCHEMA_N,
#TABLE_N,
#CONDITION,
#TARGETDB,
#SOURCEDB,
#COLUMN_LIST
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #TOP VARCHAR(100)
SET #TOP = ( SELECT CAST(Wartosc AS VARCHAR) FROM Table_B WHERE Id = 3 )
IF #TOP < 40000
SET #TOP = 40000
WHILE ( 1 = 1 )
BEGIN
DECLARE #CHECK BIT = 0
DECLARE #sqlCMD NVARCHAR(MAX) = 'SELECT TOP 1 #check_exists = 1 FROM '
+ #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' WHERE ' + #CONDITION
exec sp_executesql #sqlCMD, N'#check_exists bit OUTPUT, #START_TIME DATE', #check_exists = #CHECK OUTPUT, #START_TIME = #START_TIME;
SELECT #sqlCMD , #CHECK
END
END
FETCH NEXT
FROM KUR
INTO #SCHEMA_N,
#TABLE_N,
#CONDITION,
#TARGETDB,
#SOURCEDB,
#COLUMN_LIST
CLOSE KUR;
DEALLOCATE KUR
And when I execute this it's compiling and works ( returns values ) for example sql which I want to execute.
SELECT TOP 1 #check_exists = 1 FROM MSSF15_WarstwaPosrednia.KatalogZdarzen.MSSF_RaportZdarzenPLKB2CS2K WHERE StatusPrzetwarzania = 4 AND DataWpisu < DATEADD(MONTH, -3, #START_TIME)
Parameters work here ( #check_exists which is output and #START_TIME condition param ) , but when I execute this in procedure I'm getting error
Must declare the scalar variable "#START_TIME". 137
SP definition:
USE xxxx
/****** Object: StoredProcedure dbo.dummy Script Date: 06/09/2018 13:40:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE dbo.dummy
AS
BEGIN
/*
PRZENIESIENIE DANYCH
*/
DECLARE #START_TIME DATETIME = GETDATE()
DECLARE #TMP_NAMES TABLE (
SCHEMA_N VARCHAR(500),
TABLE_N VARCHAR(500),
CONDITION VARCHAR(MAX),
TARGETDB VARCHAR(500),
SOURCEDB VARCHAR(500),
COLUMN_LIST VARCHAR(MAX)
)
DECLARE #SCHEMA_N VARCHAR(500)
DECLARE #TABLE_N VARCHAR(500)
DECLARE #CONDITION VARCHAR(MAX)
DECLARE #COLUMN_LIST VARCHAR(MAX)
DECLARE #TARGETDB VARCHAR(500)
DECLARE #SOURCEDB VARCHAR(500)
INSERT INTO #TMP_NAMES
SELECT o.SCHEMA_N,
o.TABLE_N,
o.Condition,
o.TargetDB,
o.SourceDB,
STUFF((
SELECT ', ' + c.NAME
FROM sys.columns c
WHERE c.object_id = o.object_id
AND c.system_type_id <> 189 /* Pomijam timestamp*/
ORDER BY c.column_id
FOR XML PATH('')
), 1, 1, '')
FROM dbo.Table_A o
ORDER BY 1,
2
SELECT SCHEMA_N,
TABLE_N,
CONDITION,
TARGETDB,
SOURCEDB,
COLUMN_LIST
FROM #TMP_NAMES
DECLARE KUR CURSOR
FOR
SELECT SCHEMA_N,
TABLE_N,
CONDITION,
TARGETDB,
SOURCEDB,
COLUMN_LIST
FROM #TMP_NAMES
OPEN KUR
FETCH NEXT
FROM KUR
INTO #SCHEMA_N,
#TABLE_N,
#CONDITION,
#TARGETDB,
#SOURCEDB,
#COLUMN_LIST
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #TOP VARCHAR(100)
SET #TOP = (
SELECT CAST(Wartosc AS VARCHAR)
FROM dbo.Table_B
WHERE Id = 3
)
IF #TOP < 40000
SET #TOP = 40000
WHILE (1 = 1)
BEGIN
BEGIN TRY
BEGIN TRANSACTION T_TEST
DECLARE #CHECK BIT = 0
DECLARE #START_TIME_P DATE = GETDATE()
DECLARE #sqlCMD NVARCHAR(MAX) = 'SELECT TOP 1 #check_exists = 1 FROM ' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' WHERE ' + #CONDITION
SELECT #CHECK,
#START_TIME_P,
#sqlCMD
EXEC sp_executesql #sqlCMD,
N'#check_exists bit OUTPUT, #START_TIME DATE',
#check_exists = #CHECK OUTPUT,
#START_TIME = #START_TIME_P;
IF OBJECT_ID('tempdb..#TMP_ID') IS NOT NULL
DROP TABLE #TMP_ID
CREATE TABLE #TMP_ID (Id INT)
EXEC (
'INSERT INTO #TMP_ID
SELECT TOP ' + #TOP + ' Id
FROM ' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' WHERE ' + #CONDITION
)
DECLARE #sqlCMD_Ins NVARCHAR(MAX)
SET #sqlCMD_Ins = 'INSERT INTO ' + #TARGETDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' '
SET #sqlCMD_Ins = #sqlCMD_Ins + 'SELECT ' + #COLUMN_LIST + ' FROM ' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' WHERE Id IN ( SELECT Id FROM #TMP_ID )'
IF #sqlCMD_Ins IS NULL
RAISERROR (
'Brak polecenia insert #sqlCMD_Ins',
16,
1
)
EXEC (#sqlCMD_Ins)
DECLARE #sqlCMD_Del NVARCHAR(MAX)
SET #sqlCMD_Del = 'DELETE FROM ' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' WHERE Id IN ( SELECT Id FROM #TMP_ID )'
IF #sqlCMD_Del IS NULL
RAISERROR (
'Brak polecenia insert #sqlCMD_Ins',
16,
1
)
EXEC (#sqlCMD_Del)
DROP TABLE #TMP_ID
COMMIT TRANSACTION T_TEST
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION T_TEST
PRINT 'Wycofanie zmian'
PRINT ERROR_MESSAGE() + ' ' + CAST(ERROR_NUMBER() AS VARCHAR(256))
END
END CATCH
IF #CHECK = 0
BREAK
END
EXEC ('SELECT ''' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ''' AS DB ,COUNT(1) AS ILOSC FROM ' + #SOURCEDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ' UNION ALL ' + 'SELECT ''' + #TARGETDB + '.' + #SCHEMA_N + '.' + #TABLE_N + ''' AS DB ,COUNT(1) AS ILOSC FROM ' + #TARGETDB + '.' + #SCHEMA_N + '.' + #TABLE_N)
FETCH NEXT
FROM KUR
INTO #SCHEMA_N,
#TABLE_N,
#CONDITION,
#TARGETDB,
#SOURCEDB,
#COLUMN_LIST
END
CLOSE KUR;
DEALLOCATE KUR;
DECLARE #END_TIME DATETIME = GETDATE()
SELECT 'Daty przetworzenia',
#START_TIME_P AS 'START',
#END_TIME AS 'END'
SELECT 'Czas (min)',
DATEDIFF(MINUTE, #START_TIME_P, #END_TIME)
END
Guys, maybe u have similar situation and can help.
Error msg
Wycofanie zmian
Must declare the scalar variable "#START_TIME". 137
Thanks
Instead of
exec sp_executesql #sqlCMD, N'#check_exists bit OUTPUT, #START_TIME DATE', #check_exists = #CHECK OUTPUT, #START_TIME = #START_TIME;
try
declare #date_in DATE = GETDATE()
exec sp_executesql #sqlCMD, N'#check_exists bit OUTPUT, #START_TIME DATE', #check_exists = #CHECK OUTPUT, #START_TIME = #date_in;

Dynamic SQL - Insert a variables via INSERT INTO x SELECT statement

I would like to ask how can I insert a variables into a table using INSERT INTO x SELECT statement via dynamic SQL.
I have following table:
|-------------------|-----------------|--------------|-----------------|
| TableName | ColName | Value | SQL_Statement |
|-------------------|-----------------|--------------|-----------------|
I get a content for Value column by this query:
INSERT INTO #ReturnTable(Value) SELECT TreeHolder FROM prm.Schm_Root WHERE ParentTreeHolderId = 'DD040D31-4591-4658-A02E-A6ED00AB64F2';
But I need to fill whole table. Please consider that other values are variables, not SQL queries.
SELECT #TableSchema = TableSchema FROM #TableNames WHERE Id = #Counter;
SELECT #TableName = TableName FROM #TableNames WHERE Id = #Counter;
SELECT #ColName = ColName FROM #TableNames WHERE Id = #Counter;
SET #SQL_Statement = 'SELECT ' + #ColName + ' FROM ' + #TableSchema + '.' + #TableName + ' WHERE ' + #ColName + ' = ' + '''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''' + ';';
Now I have this query that fills a table:
SET #SQL_String = N'INSERT INTO #ReturnTable SELECT
''' + #TableName + ''',
''' + #ColName + ''',
''' + #SQL_Statement + ''',
'' + Value + '',
(SELECT ' +
#ColName + '
FROM ' +
#TableSchema + '.' + #TableName + '
WHERE ' +
#ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) + '
'')';
EXECUTE sp_executesql #SQL_String
PRINT #SQL_String;
The thing I need is to rewrite this query from INSERT INTO ? VALUE to INSERT INTO ? SELECT format.
If I understand correctly, you want to insert SQL execute syntax string and it results in ReturnTable table.
I would let subquery SQL execute syntax save in a variable. because the will be more clear what you need to do.
Declare a new variable #SQL_excuteStatement variable to save your execute syntax.
the #SQL_Statement to carry the original SQL string.
set #SQL_Statement = 'SELECT ' + #ColName +
' FROM ' + #TableSchema + '.' + #TableName +
' WHERE ' + #ColName + ' = '+'''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''';
and use select ... from table instead of subquery in select
There is a sample for you.
DECLARE #SQL_String NVARCHAR(MAX)
DECLARE #TableSchema NVARCHAR(MAX)
DECLARE #TableName NVARCHAR(MAX)
DECLARE #ColName NVARCHAR(MAX)
DECLARE #Counter int = 1
DECLARE #SQL_Statement NVARCHAR(MAX)
DECLARE #GuidArgument INT = 1
CREATE TABLE TableNames(
ID INT,
TableSchema NVARCHAR(100),
TableName NVARCHAR(100),
ColName NVARCHAR(100)
);
CREATE TABLE ReturnTable(
TableName NVARCHAR(100),
ColName NVARCHAR(100),
SQL_Statement NVARCHAR(max),
value nvarchar(max)
);
INSERT INTO TableNames VALUES (1,'dbo','T','val');
CREATE TABLE T(val INT);
INSERT INTO T VALUES (1)
SELECT #TableSchema = TableSchema FROM TableNames WHERE Id = #Counter;
SELECT #TableName = TableName FROM TableNames WHERE Id = #Counter;
SELECT #ColName = ColName FROM TableNames WHERE Id = #Counter;
set #SQL_Statement = 'SELECT ' + #ColName +
' FROM ' + #TableSchema + '.' + #TableName +
' WHERE ' + #ColName + ' = '+ '''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''';
SET #SQL_String = N'INSERT INTO ReturnTable (TableName,ColName,SQL_Statement,value)
SELECT '''+ #TableName + ''','''+ #ColName + ''','''+ #SQL_Statement + '''' + ',' + QUOTENAME(#ColName) +
' FROM ' + QUOTENAME(#TableSchema) + '.' + QUOTENAME(#TableName) + '' +
' WHERE ' + #ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''';
EXECUTE sp_executesql #SQL_String
sqlfiddle
Note
I would suggest you use clear column after insert into
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;

Generate Delete Statement From Foreign Key Relationships in SQL 2008?

Is it possible via script/tool to generate a delete statement based on the tables fk relations.
i.e. I have the table: DelMe(ID) and there are 30 tables with fk references to its ID that I need to delete first, is there some tool/script that I can run that will generate the 30 delete statements based on the FK relations for me ?
(btw I know about cascade delete on the relations, I can't use it in this existing db)
I'm using Microsoft SQL Server 2008
Here is a script for cascading delete by Aasim Abdullah, works for me on MS SQL Server 2008:
IF OBJECT_ID('dbo.udfGetFullQualName') IS NOT NULL
DROP FUNCTION dbo.udfGetFullQualName;
GO
CREATE FUNCTION dbo.udfGetFullQualName
(#ObjectId INT)
RETURNS VARCHAR (300)
AS
BEGIN
DECLARE #schema_id AS BIGINT;
SELECT #schema_id = schema_id
FROM sys.tables
WHERE object_id = #ObjectId;
RETURN '[' + SCHEMA_NAME(#schema_id) + '].[' + OBJECT_NAME(#ObjectId) + ']';
END
GO
--============ Supporting Function dbo.udfGetOnJoinClause
IF OBJECT_ID('dbo.udfGetOnJoinClause') IS NOT NULL
DROP FUNCTION dbo.udfGetOnJoinClause;
GO
CREATE FUNCTION dbo.udfGetOnJoinClause
(#fkNameId INT)
RETURNS VARCHAR (1000)
AS
BEGIN
DECLARE #OnClauseTemplate AS VARCHAR (1000);
SET #OnClauseTemplate = '[<#pTable>].[<#pCol>] = [<#cTable>].[<#cCol>] AND ';
DECLARE #str AS VARCHAR (1000);
SET #str = '';
SELECT #str = #str + REPLACE(REPLACE(REPLACE(REPLACE(#OnClauseTemplate, '<#pTable>', OBJECT_NAME(rkeyid)), '<#pCol>', COL_NAME(rkeyid, rkey)), '<#cTable>', OBJECT_NAME(fkeyid)), '<#cCol>', COL_NAME(fkeyid, fkey))
FROM dbo.sysforeignkeys AS fk
WHERE fk.constid = #fkNameId; --OBJECT_ID('FK_ProductArrearsMe_ProductArrears')
RETURN LEFT(#str, LEN(#str) - LEN(' AND '));
END
GO
--=========== CASECADE DELETE STORED PROCEDURE dbo.uspCascadeDelete
IF OBJECT_ID('dbo.uspCascadeDelete') IS NOT NULL
DROP PROCEDURE dbo.uspCascadeDelete;
GO
CREATE PROCEDURE dbo.uspCascadeDelete
#ParentTableId VARCHAR (300), #WhereClause VARCHAR (2000), #ExecuteDelete CHAR (1)='N', --'N' IF YOU NEED DELETE SCRIPT
#FromClause VARCHAR (8000)='', #Level INT=0 -- TABLE NAME OR OBJECT (TABLE) ID (Production.Location) WHERE CLAUSE (Location.LocationID = 7) 'Y' IF WANT TO DELETE DIRECTLY FROM SP, IF LEVEL 0, THEN KEEP DEFAULT
AS -- writen by Daniel Crowther 16 Dec 2004 - handles composite primary keys
SET NOCOUNT ON;
/* Set up debug */
DECLARE #DebugMsg AS VARCHAR (4000),
#DebugIndent AS VARCHAR (50);
SET #DebugIndent = REPLICATE('---', ##NESTLEVEL) + '> ';
IF ISNUMERIC(#ParentTableId) = 0
BEGIN -- assume owner is dbo and calculate id
IF CHARINDEX('.', #ParentTableId) = 0
SET #ParentTableId = OBJECT_ID('[dbo].[' + #ParentTableId + ']');
ELSE
SET #ParentTableId = OBJECT_ID(#ParentTableId);
END
IF #Level = 0
BEGIN
PRINT #DebugIndent + ' **************************************************************************';
PRINT #DebugIndent + ' *** Cascade delete ALL data from ' + dbo.udfGetFullQualName(#ParentTableId);
IF #ExecuteDelete = 'Y'
PRINT #DebugIndent + ' *** #ExecuteDelete = Y *** deleting data...';
ELSE
PRINT #DebugIndent + ' *** Cut and paste output into another window and execute ***';
END
DECLARE #CRLF AS CHAR (2);
SET #CRLF = CHAR(13) + CHAR(10);
DECLARE #strSQL AS VARCHAR (4000);
IF #Level = 0
SET #strSQL = 'SET NOCOUNT ON' + #CRLF;
ELSE
SET #strSQL = '';
SET #strSQL = #strSQL + 'PRINT ''' + #DebugIndent + dbo.udfGetFullQualName(#ParentTableId) + ' Level=' + CAST (##NESTLEVEL AS VARCHAR) + '''';
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
DECLARE curs_children CURSOR LOCAL FORWARD_ONLY
FOR SELECT DISTINCT constid AS fkNameId, -- constraint name
fkeyid AS cTableId
FROM dbo.sysforeignkeys AS fk
WHERE fk.rkeyid <> fk.fkeyid -- WE DO NOT HANDLE self referencing tables!!!
AND fk.rkeyid = #ParentTableId;
OPEN curs_children;
DECLARE #fkNameId AS INT,
#cTableId AS INT,
#cColId AS INT,
#pTableId AS INT,
#pColId AS INT;
FETCH NEXT FROM curs_children INTO #fkNameId, #cTableId; --, #cColId, #pTableId, #pColId
DECLARE #strFromClause AS VARCHAR (1000);
DECLARE #nLevel AS INT;
IF #Level = 0
BEGIN
SET #FromClause = 'FROM ' + dbo.udfGetFullQualName(#ParentTableId);
END
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #strFromClause = #FromClause + #CRLF + ' INNER JOIN ' + dbo.udfGetFullQualName(#cTableId) + #CRLF + ' ON ' + dbo.udfGetOnJoinClause(#fkNameId);
SET #nLevel = #Level + 1;
EXECUTE dbo.uspCascadeDelete #ParentTableId = #cTableId, #WhereClause = #WhereClause, #ExecuteDelete = #ExecuteDelete, #FromClause = #strFromClause, #Level = #nLevel;
SET #strSQL = 'DELETE FROM ' + dbo.udfGetFullQualName(#cTableId) + #CRLF + #strFromClause + #CRLF + 'WHERE ' + #WhereClause + #CRLF;
SET #strSQL = #strSQL + 'PRINT ''---' + #DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(#cTableId) + ' Rows Deleted: '' + CAST(##ROWCOUNT AS VARCHAR)' + #CRLF + #CRLF;
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
FETCH NEXT FROM curs_children INTO #fkNameId, #cTableId;
--, #cColId, #pTableId, #pColId
END
IF #Level = 0
BEGIN
SET #strSQL = #CRLF + 'PRINT ''' + #DebugIndent + dbo.udfGetFullQualName(#ParentTableId) + ' Level=' + CAST (##NESTLEVEL AS VARCHAR) + ' TOP LEVEL PARENT TABLE''' + #CRLF;
SET #strSQL = #strSQL + 'DELETE FROM ' + dbo.udfGetFullQualName(#ParentTableId) + ' WHERE ' + #WhereClause + #CRLF;
SET #strSQL = #strSQL + 'PRINT ''' + #DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(#ParentTableId) + ' Rows Deleted: '' + CAST(##ROWCOUNT AS VARCHAR)' + #CRLF;
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
END
CLOSE curs_children;
DEALLOCATE curs_children;
Usage example 1
Note the use of the fully qualified column name in the example. It's subtle but you must specify the table name for the generated SQL to execute properly.
EXEC uspCascadeDelete
#ParentTableId = 'Production.Location',
#WhereClause = 'Location.LocationID = 2'
Usage example 2
EXEC uspCascadeDelete
#ParentTableId = 'dbo.brand',
#WhereClause = 'brand.brand_name <> ''Apple'''
Usage example 3
exec uspCascadeDelete
#ParentTableId = 'dbo.product_type',
#WhereClause = 'product_type.product_type_id NOT IN
(SELECT bpt.product_type_id FROM dbo.brand_product_type bpt)'
DELETE statements generated for use in SP with parameter, and as ON DELETE triggers:
(this variant supports single column FKs only)
SELECT 'DELETE '+detail.name+' WHERE '+dcolumn.name+' = #'+mcolumn.name AS stmt,
'DELETE ' + detail.name + ' FROM ' + detail.name + ' INNER JOIN deleted ON ' +
detail.name + '.' + dcolumn.name + ' = deleted.' + mcolumn.name AS trg
FROM sys.columns AS mcolumn
INNER JOIN sys.foreign_key_columns ON mcolumn.object_id =
sys.foreign_key_columns.referenced_object_id
AND mcolumn.column_id = sys.foreign_key_columns.referenced_column_id
INNER JOIN sys.tables AS master ON mcolumn.object_id = master.object_id
INNER JOIN sys.columns AS dcolumn
ON sys.foreign_key_columns.parent_object_id = dcolumn.object_id
AND sys.foreign_key_columns.parent_column_id = dcolumn.column_id
INNER JOIN sys.tables AS detail ON dcolumn.object_id = detail.object_id
WHERE (master.name = N'MyTableName')
I'm pretty sure I posted code here on Stack Overflow which does this automatically using INFORMATION_SCHEMA to generate dynamic SQL, but I can't find it. Let me see if I can regenerate it.
You might need to check this out a bit, I couldn't find my original code, so I modified some code I had which builds flattend views for star-schemas automatically.
DECLARE #COLUMN_NAME AS sysname
DECLARE #TABLE_NAME AS sysname
DECLARE #IDValue AS int
SET #COLUMN_NAME = '<Your COLUMN_NAME here>'
SET #TABLE_NAME = '<Your TABLE_NAME here>'
SET #IDValue = 123456789
DECLARE #sql AS varchar(max) ;
WITH RELATED_COLUMNS
AS (
SELECT QUOTENAME(c.TABLE_SCHEMA) + '.'
+ QUOTENAME(c.TABLE_NAME) AS [OBJECT_NAME]
,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS AS c WITH (NOLOCK)
INNER JOIN INFORMATION_SCHEMA.TABLES AS t WITH (NOLOCK)
ON c.TABLE_CATALOG = t.TABLE_CATALOG
AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
AND c.TABLE_NAME = t.TABLE_NAME
AND t.TABLE_TYPE = 'BASE TABLE'
INNER JOIN (
SELECT rc.CONSTRAINT_CATALOG
,rc.CONSTRAINT_SCHEMA
,lkc.TABLE_NAME
,lkc.COLUMN_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
WITH (NOLOCK)
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE lkc
WITH (NOLOCK)
ON lkc.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
AND lkc.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
AND lkc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
WITH (NOLOCK)
ON rc.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
AND rc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
AND rc.UNIQUE_CONSTRAINT_NAME = tc.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE rkc
WITH (NOLOCK)
ON rkc.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
AND rkc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
AND rkc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE rkc.COLUMN_NAME = #COLUMN_NAME
AND rkc.TABLE_NAME = #TABLE_NAME
) AS j
ON j.CONSTRAINT_CATALOG = c.TABLE_CATALOG
AND j.CONSTRAINT_SCHEMA = c.TABLE_SCHEMA
AND j.TABLE_NAME = c.TABLE_NAME
AND j.COLUMN_NAME = c.COLUMN_NAME
)
SELECT #sql = COALESCE(#sql, '') + 'DELETE FROM ' + [OBJECT_NAME]
+ ' WHERE ' + [COLUMN_NAME] + ' = ' + CONVERT(varchar, #IDValue)
+ CHAR(13) + CHAR(10)
FROM RELATED_COLUMNS
PRINT #sql
Another technique is to use a code generator to create the Sql. I'm pretty sure the MyGeneration (no connection) has existing templates to do this. Using that tool and the right template you can create a sql script that deletes the relevant stuff with no pain.
Unfortunately, I think cascading is the tool you're asking for. I understand not being able to use it, but that fact that it exists as a built-in part of the db has pretty much killed the need for an alternative.
You can create all fk columns with a same name like 'row_id'
Then write the code below:
create procedure dbo.deleteRow
#row_id int
as
begin
set nocount on
begin transaction delete_row
declare #mainTableName varchar(50) = 'MyMainTableName'
begin try
declare #OBJECT_ID_mainTable int
select #OBJECT_ID_mainTable = OBJECT_ID from sys.tables where name = #mainTableName
create table #ids ( object_id int , table_name varchar (50) , referenced_object_id int , r_index int )
--1) select all tables are has fk to main table
insert into #ids select t.object id , t.name , fk.referenced object id ,
row_number () over ( order by
--how many tables are depends on me
(select COUNT ( * ) from sys . foreign_key_columns a fk where a_fk.referenced_object_id = fk.parent_object_id ) desc ) r_index
from sys.foreign_key_columns fk
join sys.tables t on t.object_id- fk.parent_object_id
where fk.referenced_object_id = #OBJECT_ID_mainTable
declare #i int = ( select max ( r_index ) from #ids )
declare #sqlBuilder nvarchar ( max )
--2) delete from fk tables in dependet order
while #i > 0
begin
--all fk column are called 'row_id'
set #sqlBuilder = concat ('delete from dbo.[' + ( select table_name from #ids where r_index = #i ) + ']' +
'where row_id = ', #row_id )
exec(#sqlBuilder)
set #i=#i-1
end
--3) delete from main table
delete from <MyMainTableName> where id = #row_id
commit transaction delete_row
end try
begin catch
rollback transaction delete_row
throw
end catch
end