自动匹配 INSERT INTO ... SELECT ... FROM 中的列
Posted
技术标签:
【中文标题】自动匹配 INSERT INTO ... SELECT ... FROM 中的列【英文标题】:Automatically match columns in 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 行
列名或提供的值的数量与表定义不匹配。
【问题讨论】:
【参考方案1】:为什么不简单
INSERT INTO t1
SELECT * FROM T2
【讨论】:
我需要跳过一列(身份主键)【参考方案2】:是的,您可以省略插入到的表的字段名称,并且可以使用 select * 从表中获取所有字段,但我不推荐这种方法。
如果您省略字段名称,则字段将按位置匹配,而不是按名称匹配。如果字段的顺序不完全相同,它们将被混淆。通常,您应该避免依赖表的确切布局,以最大限度地降低表中的更改破坏查询的风险。
【讨论】:
有没有办法在不指定所有列名的情况下插入除identity key
之外的所有列?我使用的表包含数十列,每次都列出它们真的很无聊。
也许你可以创建一个包含所有你想要的列的视图并从中选择 *。
@Konstantin:不,没有简单的方法可以指定除身份字段之外的所有字段。要么全有,要么全无。
只要列完全相同就可以使用【参考方案3】:
如果T1
和T2
完全匹配,您有两个选择。您可以为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(以及任何其他具有隐式转换的松散类型语言)的经验法则是尽可能具体,如果您不希望在运行时出现静默错误,并且在有人添加新功能时不希望出现错误。
【讨论】:
以上是关于自动匹配 INSERT INTO ... SELECT ... FROM 中的列的主要内容,如果未能解决你的问题,请参考以下文章