【问题标题】:How to UPDATE all columns of a record without having to list every column如何更新记录的所有列而不必列出每一列
【发布时间】:2015-04-28 18:05:15
【问题描述】:

我正在尝试找出一种方法来更新记录,而不必列出需要更新的每个列名。

例如,如果我可以使用类似于以下的内容,那就太好了:

// the parts inside braces are what I am trying to figure out
UPDATE Employee
SET {all columns, without listing each of them} 
WITH {this record with id of '111' from other table}
WHERE employee_id = '100'

如果可以做到这一点,编写这样一个查询的最直接/最有效的方法是什么?

【问题讨论】:

  • 你怎么知道为每一列分配什么值?
  • 我只是想用新记录 (id=111) 中的任何内容覆盖旧记录 (id=100) 中的所有内容。因此,例如,即使没有列出像 first_name、last_name 这样的列名,此查询也会按顺序比较每一列并用不同的值覆盖所有列。这有意义吗?
  • 没有实用的方法。您可以生成代码或执行删除-插入对。在 99% 的情况下,两者都不实用。
  • 您可以删除现有的行条目,然后执行 INSERT 吗?这样做依赖于序数列定位,通常不是最佳做法。
  • @MichaelG - 但是,您仍然需要列出每一列才能使用 INSERT,对吧?或者有没有办法通过只传递唯一的记录 id 来简单地插入记录?

标签: sql sql-update


【解决方案1】:

这是不可能的。

您尝试执行的操作不是 SQL 规范的一部分,并且不受任何数据库供应商的支持。请参阅MySQLPostgresqlMSSQLOracleFirebirdTeradata 的 SQL UPDATE 语句规范。其中每一个仅支持以下语法:

UPDATE table_reference
   SET column1 = {expression} [, column2 = {expression}] ...
[WHERE ...]

【讨论】:

  • 是的,我花了几个小时研究解决方案,然后才在这里发布我的问题,认为可能有一些只有专家知道的核心方法来完成它。
【解决方案2】:

这是不可能的,但是..

你可以做到:

begin tran
delete from table where CONDITION
insert into table select * from EqualDesingTabletoTable where CONDITION
commit tran

小心身份字段。

【讨论】:

    【解决方案3】:

    这是使用 SQL SERVER 执行此操作的核心方法。不过,在尝试之前请仔细考虑安全性和完整性。

    这使用模式来获取所有列的名称,然后组合一个大的更新语句来更新除用于连接表的 ID 列之外的所有列。

    这仅适用于单列键,不适用于复合。

    用法:EXEC UPDATE_ALL 'source_table','destination_table','id_column'

    CREATE PROCEDURE UPDATE_ALL
    
    @SOURCE VARCHAR(100),
    @DEST VARCHAR(100),
    @ID VARCHAR(100)
    
    AS
    
        DECLARE @SQL VARCHAR(MAX) =  
    
        'UPDATE D SET ' +
    
        -- Google 'for xml path stuff' This gets the rows from query results and 
        -- turns into comma separated list.
        STUFF((SELECT ', D.'+ COLUMN_NAME + ' = S.' + COLUMN_NAME
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = @DEST
        AND COLUMN_NAME <> @ID 
        FOR XML PATH('')),1,1,'')
    
        + ' FROM ' + @SOURCE + ' S JOIN ' + @DEST + ' D ON S.' +  @ID + ' = D.' + @ID
    
        --SELECT @SQL
        EXEC (@SQL)
    

    【讨论】:

      【解决方案4】:

      在 Oracle PL/SQL 中,您可以使用以下语法:

      DECLARE
        r my_table%ROWTYPE;
      BEGIN
        r.a := 1;
        r.b := 2;
        ...
      
        UPDATE my_table
        SET ROW = r
        WHERE id = r.id;
      END;
      

      当然,这只是将负担从 UPDATE 语句转移到记录构造,但您可能已经从某个地方获取了记录。

      【讨论】:

        【解决方案5】:

        使用 Merge 怎么样?

        https://technet.microsoft.com/en-us/library/bb522522(v=sql.105).aspx

        它使您能够运行插入、更新和删除。另一条建议是,如果您要使用索引更新大型数据集,并且源子集小于您的目标但两个表都非常大,请先将更改移动到临时表。我试图合并两个表,每个表都有近 200 万行,20 条记录花了 22 分钟。将增量移到临时表后,需要几秒钟。

        【讨论】:

        • MERGE 让您一次完成所有操作,而不是单独进行更新、插入和删除,但对于每个条件,您仍然必须编写所有 SQL,就好像它们是分开的一样。
        【解决方案6】:

        如果你使用Oracle,你可以使用rowtype

        declare 
            var_x  TABLE_A%ROWTYPE;
        Begin
            select * into var_x
            from TABLE_B where rownum = 1;
        
            update TABLE_A set row = var_x
            where ID = var_x.ID;
        end;
        /
        

        鉴于 TABLE_A 和 TABLE_B 具有相同的架构

        【讨论】:

          【解决方案7】:

          可能。就像 npe 说的那样,这不是标准做法。但如果你真的必须:

          1.首先是一个标量函数
          CREATE FUNCTION [dte].[getCleanUpdateQuery] (@pTableName varchar(40), @pQueryFirstPart VARCHAR(200) = '', @pQueryLastPart VARCHAR(200) = '', @pIncludeCurVal BIT = 1)
          RETURNS VARCHAR(8000) AS
          BEGIN
          DECLARE @pQuery VARCHAR(8000);
          WITH cte_Temp
          AS
          (
              SELECT
              C.name
              FROM SYS.COLUMNS AS C
                  INNER JOIN SYS.TABLES AS T ON T.object_id = C.object_id
              WHERE T.name = @pTableName
          )
              SELECT @pQuery = (
              CASE @pIncludeCurVal
              WHEN 0 THEN
              (
              STUFF(
                  (SELECT ', ' + name + ' = ' + @pQueryFirstPart + @pQueryLastPart FROM cte_Temp FOR XML PATH('')), 1, 2, ''
                  )
              )
              ELSE
              (
              STUFF(
                  (SELECT ', ' + name + ' = ' + @pQueryFirstPart + name +  @pQueryLastPart FROM cte_Temp FOR XML PATH('')), 1, 2, ''
                  )
              ) END)
          
              RETURN 'UPDATE ' + @pTableName + ' SET ' + @pQuery
          END
          
          2.这样使用
          DECLARE @pQuery VARCHAR(8000) = dte.getCleanUpdateQuery(<your table name>, <query part before current value>, <query part after current value>, <1 if current value is used. 0 if updating everything to a static value>);
          EXEC (@pQuery)
          
          示例 1:将所有员工列设为“未知”(您需要确保列类型与预期值匹配:
          DECLARE @pQuery VARCHAR(8000) = dte.getCleanUpdateQuery('employee', '', 'Unknown', 0);
          EXEC (@pQuery)
          
          示例 2:删除不需要的文本限定符(例如 #)
          DECLARE @pQuery VARCHAR(8000) = dte.getCleanUpdateQuery('employee', 'REPLACE(', ', ''#'', '''')', 1);
          EXEC (@pQuery)
          

          这个查询可以改进。这只是我保存的,有时我会使用的。你明白了。

          【讨论】:

            【解决方案8】:

            您可以通过删除表中的列并重新添加该列并添加您需要的默认值来实现。然后保存这将需要重建表

            【讨论】:

              【解决方案9】:

              与 upsert 类似,您可以检查该项目是否存在于表中,如果存在,请将其删除并插入新值(从技术上更新它),但如果您的 rowid 很敏感,那么您将丢失您的 rowid案例。

              看哪,更新

              IF NOT EXISTS (SELECT * FROM Employee WHERE ID = @SomeID)
              
                  INSERT INTO Employee VALUES(@SomeID, @Your, @Vals, @Here)
              
              ELSE
              
                  DELETE FROM Employee WHERE ID = @SomeID
                  INSERT INTO Employee VALUES(@SomeID, @Your, @Vals, @Here)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-11-04
                • 1970-01-01
                • 2022-09-27
                • 1970-01-01
                • 1970-01-01
                • 2013-10-19
                • 2017-05-26
                相关资源
                最近更新 更多