【问题标题】:Dynamic update statement with variable column names具有可变列名的动态更新语句
【发布时间】:2012-10-11 19:04:36
【问题描述】:

我们希望在几个 SQL Server 数据库中进行更新,以将某个表中的所有 NULL 值更改为空字符串而不是 NULL。我们可能会在数百个数据库中执行此操作。表名始终相同,但列名根据前端应用程序的配置方式是可变的(不要判断...我没有创建这个系统)。

有没有办法在不提前知道列名的情况下更新所有这些列?

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    可以在动态sql中传递列名:

    declare @sql nvarchar (1000);
    set @sql = N'update table set ' + @column_name + '= ''''';
    
    exec sp_executesql @sql;
    

    【讨论】:

    • always use quotename围绕动态对象名称。
    • @GSerg +1 提及quotename()。我会说它在这里的使用似乎有点多余,因为这只是用于一些内部 DML。如果它是使用来自公共(或地狱,甚至内部)站点的用户输入执行的存储过程,我会全力以赴。
    【解决方案2】:

    您可以查看sys.columns 表并加入表名或object_id。

     DECLARE @OBJ_ID INT
    
     SELECT @OBJ_ID = OBJECT_ID
     FROM SYS.tables
     WHERE name = 'YOURTABLE'
    
     SELECT * FROM SYS.columns
     WHERE OBJECT_ID = @OBJ_ID
    

    您可以使用 sys.columns 查询中的 name 字段作为执行更新的基础。

    【讨论】:

      【解决方案3】:

      假设您只需要 varchar/char 类型的所有列(或将类型过滤器更改为您需要的任何内容):

      DECLARE @tableName varchar(10)
      SET @tableName = 'yourtablenamehere'
      
      DECLARE @sql VARCHAR(MAX)
      SET @sql = ''
      
      SELECT @sql = @sql + 'UPDATE ' + @tableName + ' SET ' + c.name + ' = '''' WHERE ' + c.name + ' IS NULL ;'
      FROM sys.columns c
      INNER JOIN sys.tables t ON c.object_id = t.object_id
      INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
      WHERE t.name = @tableName AND y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
      
      EXEC (@sql)
      

      【讨论】:

        【解决方案4】:

        这可以通过游标来实现。您首先选择@Darren 提到的列名,然后使用这些值设置光标并循环:

        Open oColumnsCursor
        Fetch Next From oColumnscursor
            Into @ColumnName
        
        While @@FETCH_STATUS=0
            Begin
                Set @oQuery = 'Update [DB]..[Table] Set [' + @ColumnName + '] = ''NewValue'' Where [' + @ColumnName + '] = ''OldValue'''
                Execute(@oQuery)
                Fetch Next From oColumnscursor Into @ColumnName
                Set @oCount = @oCount + 1
            End
        
        Close oColumnsCursor;
        Deallocate oColumnsCursor;
        

        【讨论】:

          【解决方案5】:

          当您知道表名时,这将起作用:

          DECLARE @tableName varchar(10)
          SET @tableName = 'Customers'
          
          DECLARE @sql VARCHAR(MAX)
          SET @sql = ''
          
          SELECT @sql = @sql + 'UPDATE ' + @tableName + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
          FROM sys.columns c
          INNER JOIN sys.tables t ON c.object_id = t.object_id
          INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
          WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
          AND t.name = @tableName;
          
          EXEC(@sql);
          

          这将迭代 Db 中的所有表和所有列:

          DECLARE @sql VARCHAR(MAX)
          SET @sql = ''
          
          SELECT @sql = @sql + 'UPDATE ' + t.name + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
          FROM sys.columns c
          INNER JOIN sys.tables t ON c.object_id = t.object_id
          INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
          WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar');
          
          EXEC(@sql); 
          

          【讨论】:

            【解决方案6】:

            下面是过程。

            ALTER PROCEDURE [dbo].[util_db_updateRow]
                @colval_name  NVARCHAR (30), -- column and values e.g. tax='5.50'
                @idf_name  NVARCHAR (300),   -- column name
                @idn_name  NVARCHAR (300),   -- column value
                @tbl_name  NVARCHAR (100)    -- table name
            AS
            BEGIN
                SET NOCOUNT ON;
            
                DECLARE @sql NVARCHAR(MAX)
            
                -- construct SQL
                SET @sql = 'UPDATE ' + @tbl_name + '  SET ' + @colval_name + 
                           ' WHERE ' + @idf_name + '=' + @idn_name;
            
                -- execute the SQL
                EXEC sp_executesql @sql
                SET NOCOUNT OFF  
            
                RETURN
            END
            

            【讨论】:

              【解决方案7】:

              以下是存储过程,您可以在其中传递架构名称、表名称和以逗号分隔的列名列表。它仅适用于 Sql Server 2016 或更高版本。

              CREATE OR ALTER PROCEDURE UpdateData
              (@SchemaName NVARCHAR(Max),@TableName NVARCHAR(MAX),@ColumnNames NVARCHAR(MAX))
              AS
              BEGIN
              DECLARE @DynamicSql NVARCHAR(MAX);
              SET @DynamicSql =  'UPDATE ' +'[' +@SchemaName+'].' + '[' +@TableName+']' +' SET ' + STUFF((SELECT ', [' + C.name + '] = ' + '''NEW_VALUE''' 
              FROM sys.columns C
              INNER JOIN sys.tables T ON T.object_id = C.object_id
              INNER JOIN sys.schemas S ON T.schema_id = S.schema_id
              WHERE 
                  T.name = @TableName
                  AND S.Name = @SchemaName
                  AND [C].[name] in (SELECT VALUE FROM string_split(@ColumnNames,','))
              FOR XML PATH('')), 1,1, '')
              print @DynamicSql;
              EXEC (@DynamicSql);
              END
              

              【讨论】:

                猜你喜欢
                • 2021-11-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-10-22
                • 1970-01-01
                相关资源
                最近更新 更多