【问题标题】:Automatically match columns in INSERT INTO ... SELECT ... FROM自动匹配 INSERT INTO ... SELECT ... FROM 中的列
【发布时间】:2010-12-19 17:55:41
【问题描述】:

SQL Server 问题。 做的时候

INSERT INTO T1 SELECT (C1, C2) FROM T2

我不想指定T1 的列名,因为它们与T2 中的相同

有可能吗?

目前我遇到了错误

消息 213,第 16 级,状态 1,第 1 行

列名或提供的值的数量与表定义不匹配。

【问题讨论】:

    标签: sql sql-server tsql select insert


    【解决方案1】:

    为什么不简单

    INSERT INTO t1
    SELECT * FROM T2
    

    【讨论】:

    • 我需要跳过一列(身份主键)
    【解决方案2】:

    是的,您可以省略插入到的表的字段名称,并且可以使用 select * 从表中获取所有字段,但我不推荐这种方法。

    如果您省略字段名称,则字段将按位置匹配,而不是按名称匹配。如果字段的顺序不完全相同,它们将被混淆。通常,您应该避免依赖表的确切布局,以最大限度地降低表中的更改破坏查询的风险。

    【讨论】:

    • 有没有办法在不指定所有列名的情况下插入除identity key 之外的所有列?我使用的表包含数十列,每次都列出它们真的很无聊。
    • 也许你可以创建一个包含所有你想要的列的视图并从中选择 *。
    • @Konstantin:不,没有简单的方法可以指定除身份字段之外的所有字段。要么全有,要么全无。
    • 只要列完全相同就可以使用
    【解决方案3】:

    如果T1T2 完全匹配,您有两个选择。您可以为insert into T1 提供select 来自T2 的所有列,也可以为insert 语句提供列列表。

    即使当您执行select MSSQL 提供列标题时,insert 语句不会使用该信息来匹配列。

    【讨论】:

    • 此语句的第二部分不正确 - MSSQL 与列名匹配。
    • 请记住,当您评论一个 12 年前的答案时,中间版本可能已经发生了一些变化。 :)
    【解决方案4】:

    始终在 INSERT 和 SELECT 投影中使用显式列。即使你不想,你也应该:

    INSERT INTO T1 (C1, c2)
    SELECT C1, C2 FROM T2
    

    【讨论】:

    • 这是我想摆脱的代码重复,转而采用约定驱动的方法(具有相同名称的列应该相互映射)
    • SQL 通常是一种冗长的语言。例如,group by 子句在聚合中是多余的,INTO 是多余的。我认为你只需要接受这一点。
    • 这是一个很好的做法,因为数据库系统无法跟踪客户端代码中的依赖关系。可以向 T2 添加一列,也可以重组 T2 以更改列顺序,也可以删除一列,所有操作都会导致代码中断。
    • 这不是代码重复。代码重复将是INSERT INTO T1 (C1, C2) SELECT C1 C1, C2 C2 FROM T2(列别名)。我还认为 SQL 会尝试根据我定义的列别名来匹配列,但它只关心SELECT 中列的顺序。您在 INTO (destCol1, destCol2) 部分中进行绑定。
    • @cindi 聚合中的 GROUP BY 子句不一定是多余的。 GROUP BY 可以包含根本不在选择列表中的列。如果我有一个包含 FirstName、LastName、State 的表,我可以进行以下查询:SELECT FirstName, COUNT(*) FROM MyTable GROUP BY FirstName, State
    【解决方案5】:

    如果您担心列名,您可以随时给它们取别名:

    INSERT INTO T1 (C1, c2)
    SELECT C1 AS C1_ALIAS, C2 AS C2_ALIAS FROM T2
    

    或者,更简洁:

    INSERT INTO T1 (C1, c2)
    SELECT C1 C1_ALIAS, C2 C2_ALIAS FROM T2
    

    虽然我真的不知道为什么要在这样一个简单的例子中这样做

    【讨论】:

    • 是否可以为多个列设置别名?例如,insert into T1 ALL_COLUMNS_ALIAS select (C1, C2, C3) as ALL_COLUMNS_ALIAS from T2?
    【解决方案6】:

    引用如下:

    INSERT INTO NEWTABLENAME COL1[,COL2,..COLN]
    SELECT COL1[,COL2,..COLN] FROM THE EXISTINGTABLENAME
    

    【讨论】:

      【解决方案7】:

      首先选择此 sql,从 sql 结果中选择您的表行并更改目标或源表名。如果表具有相同的列(不需要相同的顺序),它将起作用。

      xparams 为 ( select (select user from dual) "OWNER", '' "ADDSTRTOFROMTABLENAME" from dual ) ,t1 as ( SELECT dbat.table_name from dba_tables dbat, xparams where dbat.owner = xparams.OWNER ) ,t1c1 as ( SELECT utcs.table_name , LISTAGG(utcs.column_name,',') 组内 (order by utcs.column_name) "COLS" from USER_TAB_COLUMNS utcs, t1 where utcs.table_name = t1.table_name group by utcs.table_name ) ,res1 as ( SELECT 'insert into '|| t1c1.table_name || ' ('|| t1c1.COLS ||') select '|| t1c1.COLS || ' from ' || t1c1.table_name||xparams.ADDSTRTOFROMTABLENAME ||';' "RES" 来自 t1c1, xparams order by t1c1.table_name ) 从 res1 中选择 *

      【讨论】:

        【解决方案8】:

        其他答案很好,但没有解释为什么不好用:

        INSERT INTO T1
        SELECT * FROM T2
        

        在评论中,OP 谈到了在使用更安全的方法时指定列时的代码重复:

        INSERT INTO T1 (C1, c2)
        SELECT C1, C2 FROM T2
        

        但是,如果您不具体说明,则依赖于始终匹配的列数以及列的顺序是否符合您的期望。如果其中一个表被更改为添加一个 柱子。

        您也可能会遇到无声错误的麻烦。如果您使用列数相同但位置不同的表:

        
        CREATE TABLE tab1 (col1 int, col2 string);
        
        CREATE TABLE tab2 (col1 string, col2 int);
        
        INSERT INTO tab1 values(1, 'aaa');
        
        INSERT INTO TABLE tab2 select * FROM tab1;
        

        那么您可能希望您制作的副本使得 tab1 和 tab2 是相同的。我想要的是:

        +-------------------+-------------------+
        | tab2.col1         | tab2.col2         |
        +-------------------+-------------------+
        | 1                 | aaa               |
        +-------------------+-------------------+
        
        

        但它会根据列位置加载并转换数据,所以我得到的是:

        +-------------------+-------------------+
        | tab2.col1         | tab2.col2         |
        +-------------------+-------------------+
        | 1                 | NULL              |
        +-------------------+-------------------+
        

        发生的事情是它无法将字符串转换为 int,因此将其设置为 NULL。它可以将 int 转换为不再是数字类型的 '1' 的字符串。

        即使列匹配任何人都可以做到:

        ALTER TABLE tab1 ADD COLUMNS (col3 string COMMENT 'a new column');
        

        之后,不指定列的查询将中断,说两个表中的列数不匹配。它将不再能够将数据移动到 tab2。

        这意味着安全的做法是使用 SQL 显式:

        INSERT INTO T1 (C1, c2)
        SELECT C1, C2 FROM T2
        

        如果有人只是想快速复制一个表,那么一些 SQL 引擎支持

        CREATE TABLE tab3 AS SELECT * FROM tab1;
        

        在这种情况下,确保键入列是浪费时间,如果有人在克隆之前将列添加到 tab1,那么显式将无法克隆新列。反例表明,编程中没有绝对的规则,只有经验法则。 SQL(以及任何其他具有隐式转换的松散类型语言)的经验法则是尽可能具体,如果您不希望在运行时出现静默错误,并且在有人添加新功能时不希望出现错误。

        【讨论】:

          猜你喜欢
          • 2013-12-31
          • 2022-08-02
          • 1970-01-01
          • 2020-07-13
          • 2011-01-29
          • 1970-01-01
          • 1970-01-01
          • 2016-08-12
          • 2016-06-05
          相关资源
          最近更新 更多