【问题标题】:Syntax error in Cursor and dynamic SQL游标和动态 SQL 中的语法错误
【发布时间】:2017-08-16 12:45:46
【问题描述】:

我的以下代码出错:(SQL Server 2008R2;在游标中获取两个数据库名称,然后将数据插入两个数据库中的 Table1)

DECLARE @SQL NVARCHAR(max)
DECLARE @DB  VARCHAR(50)

DECLARE CUR_DB CURSOR FAST_FORWARD FOR 
        SELECT NAME FROM MASTER.SYS.DATABASES
        WHERE DATABASE_ID IN ('5', '81')
;

OPEN CUR_DB;
FETCH NEXT FROM CUR_DB INTO @DB;

WHILE @@FETCH_STATUS = 0 
BEGIN
    SELECT @SQL = 'INSERT INTO @DB.dbo.Table1 VALUES (100, ''abc'', def'', 0)'
    EXEC(@SQL)
    FETCH NEXT FROM CUR_DB INTO @DB 
END;

CLOSE CUR_DB;
DEALLOCATE CUR_DB;

错误:消息 102,级别 15,状态 1,第 1 行 '.' 附近的语法不正确。 消息 102,级别 15,状态 1,第 1 行 '.' 附近的语法不正确。

【问题讨论】:

  • 没有名为@DB 的数据库。在执行之前尝试print @Sql
  • 这样做动态SQL的时候,一定要养成注释掉EXEC(@SQL)语句的习惯,换成PRINT @SQL;陈述。然后您可以看到您正在生成的 SQL,并轻松发现任何错误,或者将输出复制并粘贴到查询窗口中并尝试执行它以测试它是否良好。
  • @SqlZim 和 pmbAustin,感谢您的好建议。

标签: sql-server syntax cursor dynamic-sql


【解决方案1】:

您没有在动态 SQL 中连接数据库的名称:

DECLARE @SQL NVARCHAR(max)
DECLARE @DB  VARCHAR(50)

DECLARE CUR_DB CURSOR FAST_FORWARD FOR 
        SELECT NAME FROM MASTER.SYS.DATABASES
        WHERE DATABASE_ID IN ('5', '81')
;

OPEN CUR_DB;
FETCH NEXT FROM CUR_DB INTO @DB;

WHILE @@FETCH_STATUS = 0 
BEGIN
    SELECT @SQL = 'INSERT INTO ' + QUOTENAME(@DB) + '.dbo.Table1 VALUES (100, ''abc'', def'', 0)'
    EXEC(@SQL)
    FETCH NEXT FROM CUR_DB INTO @DB 
END;

CLOSE CUR_DB;
DEALLOCATE CUR_DB;

【讨论】:

    【解决方案2】:

    你为什么要使用光标?您已将此限制为仅两个数据库。为什么不将其编码为两个插入语句,然后忘记所有使用游标的疯狂。整个游标结构就是这么简单。

    INSERT INTO DataBaseNameForDataBaseID5.dbo.Table1 VALUES (100, 'abc', 'def', 0)
    INSERT INTO DataBaseNameForDataBaseID81.dbo.Table1 VALUES (100, 'abc', 'def', 0)
    

    ---编辑---

    既然您说您需要在不止这两个数据库上执行此操作,您仍然不需要为此类事情求助于游标。看看这个。它利用动态 sql 但不需要循环。

    declare @SQL nvarchar(MAX) = N''
    
    select @SQL = @SQL + N'INSERT INTO ' + QuoteName(name) + N'.dbo.Table1 VALUES (100, ''abc'', ''def'', 0);'
    from sys.databases
    where DATABASE_ID IN (5, 81) --No quotes here, these are integers, not strings
    
    exec sp_executesql @SQL
    

    【讨论】:

    • 顺便说一句,我不确定 "DataBaseNameForDataBaseID5" 吗?您的意思是:MASTER.SYS.DATABASESFORDatabaseID5 还是 DatabasenameFORDatabaseID5? FOR和数据库名称之间没有空格?我的意思是语法问题。
    • 这只是数据库的名称。我不知道它们是什么,因为您的原始代码只有数据库标识。 :) 这只是 3 部分命名。 [DatabaseName].[SchemaName].[TableName] 现在有意义吗?
    【解决方案3】:

    请尝试以下脚本

    DECLARE @SQL NVARCHAR(max)
    DECLARE @DB  VARCHAR(50)
    
    DECLARE CUR_DB CURSOR FAST_FORWARD FOR 
        SELECT NAME FROM MASTER.SYS.DATABASES
        WHERE DATABASE_ID IN ('5', '81');
    
    OPEN CUR_DB;
    FETCH NEXT FROM CUR_DB INTO @DB;
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        SELECT @SQL = 'INSERT INTO ['+@DB+'].[dbo].[Table1] VALUES (100,''abc'',''def'', 0)'
        EXEC( @SQL)
        FETCH NEXT FROM CUR_DB INTO @DB 
    END;
    
    CLOSE CUR_DB;
    DEALLOCATE CUR_DB;
    

    脚本中有 2 个问题:

    1. 您在查询字符串中使用了参数名称

      'INSERT INTO **@DB**.dbo.Table1 VALUES (100, ''abc'', def'', 0)'
      

    这将导致错误,因为 EXEC 语句将括号内给出的所有语句作为单独的批处理运行,并且您之前声明的所有参数都不会超出 EXEC 语句的范围。所以改为如下修改

         SELECT @SQL = 'INSERT INTO **['+@DB+']**.[dbo].[Table1] VALUES (100, ''abc'', ''def'', 0)'
    
    1. 在值def之前缺少2个单引号

       VALUES (100, ''abc'', **def''**, 0)'
      

    所以我只是添加了那个。

    希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-25
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      相关资源
      最近更新 更多