我想要实现的目标如下。 当我删除一条记录时,我想检查是否存在任何FK关系,并且需要递归。这样我就可以显示与你要删除的记录相关的所有记录的列表。
这是嵌套链接的一个小例子 项目1 - >阶段1 - >块1 - > ..
因此,当我尝试删除项目1时,我需要先获取您需要删除的项目列表: 阶段1 块1 ....
我想用一个带有ID和表名的存储过程(格式[chema]。[tablename])来查找所有这些链接记录。
我遇到的问题是递归部分。 到目前为止,这是我的代码:
ALTER PROCEDURE core.usp_CanBeDeleted
@entityId int,
@entityName nvarchar(250)
AS
BEGIN
DECLARE @NumberRecords int, @RowCount int
DECLARE @childId int
DECLARE @query nvarchar(max)
DECLARE @eName nvarchar(250) , @keyName nvarchar(250)
DECLARE @columnName nvarchar(250)
DECLARE @keys TABLE(
RowID int IDENTITY(1, 1),
name nvarchar(250),
entityName nvarchar(250),
columnName nvarchar(250)
)
if not exists (select * from sysobjects where name='partialResults' and xtype='U')
BEGIN
CREATE TABLE partialResults(
RowID int IDENTITY(1, 1),
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
END
DECLARE @recusiveResults TABLE(
RowID int,
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
DECLARE @results TABLE(
RowID int,
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
SET @RowCount = 1
-- get all FK's of the entity
INSERT INTO @keys
SELECT name, '[' + OBJECT_SCHEMA_NAME(parent_object_id) + '].[' + OBJECT_NAME(parent_object_id)+ ']',cu.column_name
from sys.foreign_keys k
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
ON k.name = CU.CONSTRAINT_NAME
where k.referenced_object_id = OBJECT_ID(@entityName)
-- set variable to number of records in temp table
SET @NumberRecords = @@ROWCOUNT
-- loop through the FK's an get all linked entities
WHILE(@RowCount <= @NumberRecords)
BEGIN
SELECT @keyName = name, @eName = entityName, @columnName = columnName
FROM @keys
WHERE RowId = @RowCount
-- get all FK information
SET @query = 'INSERT INTO partialResults(FK_name, name, id, parentId)'
+ ' SELECT ''' + @keyName + ''','''+ @eName + ''',' + 'id,' + cast(@entityId as varchar(25)) + ' as parentid'
+ ' FROM ' +@eName
+ ' WHERE id in '
+ ' (SELECT ' + @columnName
+ ' FROM ' + @entityName
+ ' WHERE id = ' + cast(@entityId as varchar(25))
+ ' )'
--print @query
EXEC (@query)
SET @RowCount = @RowCount + 1
END
-- rest number of records
SET @RowCount = 1
SELECT @NumberRecords = count(id)
FROM partialResults
-- save partialResults
INSERT INTO @results--(FK_name, name, id, parentId)
SELECT *--FK_name, name, id, parentId
FROM partialResults
DELETE FROM partialResults
WHILE(@RowCount <= @NumberRecords)
BEGIN
-- select next row
SELECT @childId = id, @eName = name
FROM @results
WHERE RowId = @RowCount
INSERT INTO @recusiveResults
EXEC core.usp_CanBeDeleted @childId, @eName
SET @RowCount = @RowCount + 1
END
INSERT INTO @results
SELECT *
FROM @recusiveResults
if exists (select * from sysobjects where name='partialResults' and xtype='U')
BEGIN
-- remove temp tables
DROP TABLE partialResults
END
-- return results
SELECT *
FROM @results
END
GO
问题出在这里:
INSERT INTO @recusiveResults
EXEC core.usp_CanBeDeleted @childId, @eName
显然你不能嵌套插入exec。 但我真的没有看到任何其他方法来做到这一点。 我已经尝试将其转换为函数,但还有其他问题,如动态查询。
任何帮助都会有很大帮助。
答案 0 :(得分:1)
将过程拆分为外部和内部过程。
在外部过程中创建一个#results temp-table,然后调用内部过程。
在内部过程中放入包括递归在内的所有逻辑,但不是在结尾处选择结果而是将结果插入到已存在的#results表中。
这样你就可以节省很多时间,因为你不必移动数据。您也不必再嵌套INSERT...EXEC
了。
您也不再需要dbo.PartialResults
表,因为您可以直接写入动态语句中的#results表。如果您仍然需要它,为了使递归工作用您在内部过程中创建的#partialResults临时表替换它(不要检查是否存在,只需创建新的。请参阅http://sqlity.net/en/1109/temp-tables-scoping-eclipsing/以获取解释临时表范围)。这样每次执行都会创建自己的临时表,而不必处理清理。与使用真实表格相比,这也稍微不那么重了。
最后,所有表变量也可以。
在内部程序结束时,您可以执行简单的SELECT * FROM #results;
输出所有收集的结果。