【问题标题】:Need help figuring out the cursor需要帮助找出光标
【发布时间】:2022-01-17 17:52:27
【问题描述】:

更新: 谢谢大家的回答。我很感激。 补充几点:

  1. 我将实际的包名替换为“%%”,因为我不想在 NDA 相关的东西上遇到麻烦,所以我选择格外小心。
  2. 我确实使用 select @sql 查看执行的查询,但感谢您注意到 :)
  3. 再次感谢大家的回答。

这是我的第一篇文章,所以可能有点令人印象深刻:D

我编写了这个脚本来帮助我一次删除所有依赖项的数据,但是我有两个问题:

declare @PackageId uniqueidentifier ;
set @PackageId = (select Id from SysPackage where name like '%%');
declare @tableName varchar(max);
declare @Columnname varchar(max);
declare @sql varchar(max);
declare dependent_tables Cursor 
FOR SELECT  
    tab1.name,
    col1.name
FROM sys.foreign_key_columns fkc
INNER JOIN sys.objects obj
    ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
    ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch
    ON tab1.schema_id = sch.schema_id
INNER JOIN sys.columns col1
    ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
    ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.columns col2
    ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
INNER JOIN sys.foreign_keys fk
    ON fk.name = obj.name
where tab2.name ='SysPackage'

Open dependent_tables

Fetch NEXT from dependent_tables INTO
    @tableName, 
    @columnName; 

WHILE @@Fetch_Status = 0 
Begin
    if @tableName is null Break;
    if @Columnname is null Break;
    set @sql=CONCAT('delete from ',@tableName,' WHERE ',@columnName,' = ',@PackageId); 
    exec (@sql); 
END;


Deallocate dependent_tables;

所以我基本上有两个问题。

  1. 循环是无限的,因为光标实际上并不循环遍历记录,而是始终停留在相同的@tableName 和@columName 上

  2. @packageId 也有一个问题,它被插入到没有引号的查询中,所以查询看起来像这样:

    从 Table1 中删除,其中 column1 = A5664658-26D5-4600-862A-86467FD59244

我想为联系人创建一个额外的参数,有点像这样:

set @sql=CONCAT('delete from ',@tableName,' WHERE ',@columnName,' = ', ''',@PackageId, '''); 

但它不会让我逃脱 '. 任何想法如何找出解决它的方法将不胜感激。

谢谢。

【问题讨论】:

  • 你没有获取下一个光标
  • 正如@Nathan_Sav 所指出的,您需要在while 循环结束时,在END 关键字之前“从dependent_tables 中获取NEXT ......”。对于第二点,您需要将其输入为' = ','''' + @PackageId + '''');。最后评论:在开发动态 SQL 查询时,PRINT 语句(例如PRINT @sql)是你最好的朋友。没有它,你就是聋子、哑巴和瞎子。始终“打印”生成的查询并尝试直接在 SSMS 中运行它以捕获琐碎的错误。
  • 同时关闭光标。

标签: sql sql-server database-cursor


【解决方案1】:

只需替换以下代码。您忘记在 WHILE 循环中获取数据。

WHILE @@Fetch_Status = 0 
Begin
    if @tableName is null Break;
    if @Columnname is null Break;
    set @sql=CONCAT('delete from ',@tableName,' WHERE ',@columnName,' = ',@PackageId); 
    exec (@sql); 

    FETCH NEXT FROM dependent_tables INTO 
    @tableName, 
    @columnName;  
END;

【讨论】:

    【解决方案2】:

    这里有一些提示。首先,使用适当的数据类型。一个对象的名称不能超过 20 亿个 ANSI 字符;它的名称存储为sysnamenvarchar(128) NOT NULL 的同义词)。

    对于您的动态语句,不要向它们注入参数parametrise。也不要注入原始对象名称,分隔标识它们(使用QUOTENAME)。

    您还应该始终架构限定您的对象。

    另外,我知道您有一个名为SysPackage 的表。这是一个坏主意; sys 是 Microsoft 已使用的前缀。阅读Does prefixing system or general table names with "Sys" fine in Sql Server?

    我还注意到,您的子查询(SELECT Id FROM dbo.SysPackage WHERE name LIKE '%%') 实际上是WHERE Name IS NOT NULL。我也怀疑该查询返回单个值,所以我怀疑这是您需要解决的另一个问题。

    所有这些都给你这样的东西:

    DECLARE @PackageId uniqueidentifier;
    SET @PackageId = (SELECT Id FROM dbo.SysPackage WHERE name IS NOT NULL); -- LIKE '%%' is odd. Just check if the value isn't NULL
    DECLARE @SchemaName sysname; 
    DECLARE @TableName sysname; --Use an appropriate data type
    DECLARE @ColumnName sysname;  --Use an appropriate data type
    DECLARE @sql nvarchar(MAX); --SQL is an nvarchar, not a varchar
    DECLARE dependent_tables CURSOR FOR
        SELECT sch.[name], --Ensure you schema qualify
               tab1.[name],
               col1.[name]
        FROM sys.foreign_key_columns fkc
             INNER JOIN sys.objects obj ON obj.object_id = fkc.constraint_object_id
             INNER JOIN sys.tables tab1 ON tab1.object_id = fkc.parent_object_id
             INNER JOIN sys.schemas sch ON tab1.schema_id = sch.schema_id
             INNER JOIN sys.columns col1 ON col1.column_id = parent_column_id
                                        AND col1.object_id = tab1.object_id
             INNER JOIN sys.tables tab2 ON tab2.object_id = fkc.referenced_object_id
             INNER JOIN sys.columns col2 ON col2.column_id = referenced_column_id
                                        AND col2.object_id = tab2.object_id
             INNER JOIN sys.foreign_keys fk ON fk.name = obj.name
        WHERE tab2.name = 'SysPackage';
    
    OPEN dependent_tables;
    
    FETCH NEXT FROM dependent_tables
    INTO @SchemaName,
         @tableName,
         @Columnname;
    
    WHILE @@Fetch_Status = 0
    BEGIN
    
        SET @sql = CONCAT(N'DELETE FROM ', QUOTENAME(@SchemaName), N'.', QUOTENAME(@TableName), N' WHERE ', QUOTENAME(@Columnname), N' = @PackageId');
        EXEC sys.sp_executesql @SQL, N'@PackageId uniqueidentifier', @PackageId;
    
        FETCH NEXT FROM dependent_tables
        INTO @SchemaName,
             @TableName,
             @Columnname;
    END;
    
    
    DEALLOCATE dependent_tables;
    CLOSE dependent_tables;
    

    【讨论】:

    • 感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2013-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多