如何在不命名所有列的情况下合并两个表?

Posted

技术标签:

【中文标题】如何在不命名所有列的情况下合并两个表?【英文标题】:How do I merge two tables without naming all columns? 【发布时间】:2012-01-20 16:53:41 【问题描述】:

假设我有这两个具有相同列的表。发挥您的想象力,让它们变得更大:

USER_COUNTERPARTY:
ID  |Name                        |Credit Rating    |Sovereign Risk    |Invoicing Type
----+----------------------------+-----------------+------------------+---------------
1   |Nat Bank of Transnistria    |7                |93                |Automatic
2   |Acme Ltd.                   |25               |12                |Automatic
3   |CowBInd LLP.                |49               |12                |Manual

TEMP:
ID  |Name                        |Credit Rating    |Sovereign Risk    |Invoicing Type
----+----------------------------+-----------------+------------------+---------------
2   |Acacacme Ltd.               |31               |12                |Manual
4   |Disenthralled Nimrod Corp.  |31               |52                |Automatic

我想将它们合并为一个,用第二个替换第一个中具有相同 ID 的任何内容,并插入不存在的任何内容。我可以使用这种说法:

MERGE INTO USER_COUNTERPARTY C
USING TEMP T
ON (C.COUNTER_ID = T.COUNTER_ID)
WHEN MATCHED THEN UPDATE SET
    C.COUNTER_NAME = T.COUNTER_NAME,
    C.COUNTER_CREDIT = T.COUNTER_CREDIT,
    C.COUNTER_SVRN_RISK = T.COUNTER_SVRN_RISK,
    C.COUNTER_INVOICE_TYPE = T.COUNTER_INVOICE_TYPE
WHEN NOT MATCHED THEN INSERT VALUES (
    T.COUNTER_ID,
    T.COUNTER_NAME,
    T.COUNTER_CREDIT,
    T.COUNTER_SVRN_RISK,
    T.COUNTER_INVOICE_TYPE);

这很好,但请注意,我必须为每一列命名。有什么方法可以合并这些表而不必命名所有列? Oracle documentation 坚持我在合并中的“插入”和“设置”之后使用列名,因此可能需要一些其他声明。结果应该是这样的:

ID  |Name                        |Credit Rating    |Sovereign Risk    |Invoicing Type
----+----------------------------+-----------------+------------------+---------------
1   |Nat Bank of Transnistria    |7                |93                |Automatic
2   |Acacacme Ltd.               |31               |12                |Manual
3   |CowBInd LLP.                |49               |12                |Manual
4   |Disenthralled Nimrod Corp.  |31               |52                |Automatic

如果有帮助,我将其粘贴在这里:

CREATE TABLE USER_COUNTERPARTY
( COUNTER_ID             INTEGER       NOT NULL PRIMARY KEY,
  COUNTER_NAME           VARCHAR(38),
  COUNTER_CREDIT         INTEGER,
  COUNTER_SVRN_RISK      INTEGER,
  COUNTER_INVOICE_TYPE   VARCHAR(10) );

INSERT ALL
INTO USER_COUNTERPARTY VALUES (1, ‘Nat Bank of Transnistria’, 7, 93, ‘Automatic’)
INTO USER_COUNTERPARTY VALUES (2, ‘Acme Ltd.’, 25, 12, ‘Manual’)
INTO USER_COUNTERPARTY VALUES (3, ‘CowBInd LLP.’, 49, 12, ‘Manual’)
SELECT * FROM DUAL;

CREATE TABLE TEMP AS SELECT * FROM USER_COUNTERPARTY;
DELETE FROM TEMP;

INSERT ALL
INTO TEMP VALUES (2, ‘Conoco Ltd.’, 25, 12, ‘Automatic’)
INTO TEMP VALUES (4, ‘Disenthralled Nimrod Corp.’, 63, 12, ‘Manual’)
SELECT * FROM DUAL;

【问题讨论】:

可以查询数据字典(如USER_TAB_COLUMNS)生成列名列表。 没错,你可以。但那又如何呢?我可能不得不将它们放入一个集合中,遍历它们,并使用动态插入。对于更新所有列,我认为删除和插入会更好。但我会记住数据字典。 “然后呢?”正如我所说:生成列名列表。例如SELECT ','||column_name FROM user_tab_columns WHERE table_name='MYTABLE' ORDER BY column_id; - 然后将结果复制并粘贴到合并语句中。当然,它并不漂亮,如果架构发生变化,必须重新完成,但它可以工作。当然我不是在谈论运行时代码生成:) 【参考方案1】:

我认为您必须避免使用列名的唯一选择是两个单独的语句:

delete from USER_COUNTERPARTY UC
      where exists
              (select null
                 from TEMP T
                where T.COUNTER_ID = UC.COUNTER_ID);

insert into USER_COUNTERPARTY UC
  select *
    from TEMP T
   where not exists
           (select null
              from USER_COUNTERPARTY UC
             where T.COUNTER_ID = UC.COUNTER_ID);

【讨论】:

是的,我想这就是这样做的方法。 但是你怎么用'select null'而不是'select *'呢?这是为了效率吗?我也应该这样做吗? selectexists 的子查询中只是用来指示是否返回一行,返回什么列并不重要。有的人会用select 1,有的人会用select *,其实没什么区别。 @JohnDoyle 我认为表别名的语法是错误的。对我来说,第一个查询需要根据***.com/questions/11005209/… 更改为“从 USER_COUNTERPARTY UC 中删除 UC”,第二个需要在第一行删除别名,使其“插入 USER_COUNTERPARTY”。 必须考虑的一个很大区别是,DELETE 语句可能存在 ON DELETE CASCADE 约束导致相关表可能“丢失”数据记录。 MERGE 不会发生这种情况。【参考方案2】:

您可以尝试使用这样的包装联合语句:

SELECT (*) FROM
(SELECT * FROM Table1 WHERE ID NOT IN (SELECT ID FROM Table2)
 UNION ALL
 SELECT * FROM Table2)
ORDER BY 1

【讨论】:

【参考方案3】:

我遇到了所描述的问题,并且我解决它的方式技术含量非常低,但我想我会分享它以防它引发人们的其他想法。

我获取了列名(我从 SQL developer 中的 DDL 表中提取它们,但也使用 tab_columns 表中的方法)并将它们插入到 Excel 电子表格中。然后我删除了 Varchar 等语句(使用文本到列 Excel 函数,然后只删除了 varchar、number 等语句结束的列),所以它只留下了字段名称。然后我在下一个 Excel 列中插入了一个公式,="dest."&A2&"=src."&A2&"," 并填写了所有 110 个字段,然后在一个新的 Excel 列中,使用 =A2&"," 并在一个新列,="src."&A2&",",再次填写所有字段。然后在一个 SQL 表中,我输入:

merge into <schema>.<destination_table> dest
  using <schema>.<source_table> src
on (dest.<link> = src.<link>)
when matched then update set
(<copy all of the first column, 
  not including the linking fields and removing the comma at the end>)
when not matched then insert 
(<copy and paste the second column from Excel, and remove the final comma>)
values
(<copy and paste the third column from Excel and remove the final comma>)

我还有一个用于合并具有不同列名的表的版本,但这涉及在 Excel 工作表中映射字段的额外步骤。

我发现我需要使用合并语句来完成我的工作 - 与存在的更新相比,我发现 Merge 可以节省大量时间。

【讨论】:

【参考方案4】:

如果您有列的默认值(并且您希望使用这些默认值),则可以在插入语句中省略这些值,但否则,您必须指定要为其插入或更新值的每一列。

SELECT 没有像 * 这样的简写。

【讨论】:

【参考方案5】:

我遇到了同样的问题,我编写了一个获取所有表列的列表并构建动态 sql 查询以进行更新而不命名所有列的过程。

PROCEDURE update_from_table(
  p_source VARCHAR2,  -- Table to copy all columns from
  p_target VARCHAR2,  -- Table to copy into 
  p_id_name VARCHAR2 -- Primary key name
)
  AS
v_sql VARCHAR2(4096) := 'UPDATE ' || p_target || ' t1 SET (';
v_sql_src VARCHAR2(4096) := ') = (SELECT ';
v_sql_end VARCHAR2(4096) := ' FROM '|| p_source ||' t2 WHERE t1.'||p_id_name||' = t2.'||p_id_name||') 
WHERE EXISTS (
SELECT 1
  FROM '|| p_source ||' t2
 WHERE t1.'||p_id_name||' = t2.'||p_id_name||' )';
v_first BOOLEAN := TRUE;  
BEGIN
FOR col IN
(select column_name from user_tab_columns
  where table_name = p_source
)
LOOP
  IF NOT v_first THEN
    v_sql:= v_sql || ', '; -- adding comma before every arg except first
    v_sql_src := v_sql_src || ', ';
  ELSE
    v_first := FALSE;
  END IF;
  v_sql:= v_sql || col.column_name ;
  v_sql_src:= v_sql_src || col.column_name ;
END LOOP;

v_sql := v_sql || v_sql_src || v_sql_end;
EXECUTE IMMEDIATE v_sql;
END update_from_table;

然后我分两步合并:

-- Insert not matching records
INSERT INTO USER_COUNTERPARTY
            SELECT *
            FROM TEMP WHERE COUNTER_ID NOT IN (
        SELECT USER_COUNTERPARTY.COUNTER_ID 
        FROM USER_COUNTERPARTY 
        JOIN TEMP ON TEMP.COUNTER_ID = USER_COUNTERPARTY.COUNTER_ID);
-- Update matching records
update_from_table('TEMP', 'USER_COUNTERPARTY', 'COUNTER_ID');

【讨论】:

以上是关于如何在不命名所有列的情况下合并两个表?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不丢失 SQL 中的任何行的情况下合并两个表?

如何在不重复列的情况下合并 Pandas 数据框

如何在不命名 DataFrame 列的情况下使用 Seaborn.lmplot 函数?

如何在不缩小列的情况下制作可滚动表?

如何在不丢失任何信息的情况下合并两个 pdf?

如何在不先定义表中的列的情况下将数据加载到 PostgreSQL 中?