如何在不命名所有列的情况下合并两个表?
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 *'呢?这是为了效率吗?我也应该这样做吗?select
在exists
的子查询中只是用来指示是否返回一行,返回什么列并不重要。有的人会用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');
【讨论】:
以上是关于如何在不命名所有列的情况下合并两个表?的主要内容,如果未能解决你的问题,请参考以下文章