Oracle SQL — Combining two tables, but taking duplicates from one?

我只是一个虾纸丫 提交于 2019-12-24 17:50:02

问题


I have these tables:

Table A
Num Letter 
1   A      
2   B      
3   C     

Table B
Num Letter
2   C      
3   D   
4   E 

I want to union these two tables, but I only want each number to appear once. If the same number appears in both tables, I want it from Table B instead of table A.

Result
Num Letter
1   A
2   C
3   D
4   E

How could I accomplish this? A union will keep duplicates and an intersect would only catch the same rows -- I consider a row a duplicate when it has the same number, regardless of the letter.


回答1:


Try this: http://www.sqlfiddle.com/#!4/0b796/1

with a as
(
  select Num, 'A' as src, Letter
  from tblA
  union
  select Num, 'B' as src, Letter
  from tblB
)
select 

   Num

   ,case when count(*) > 1 then
       min(case when src = 'B' then Letter end)
   else
       min(Letter)
   end as Letter

from a
group by Num
order by Num;

Output:

| NUM | LETTER |
----------------
|   1 |      A |
|   2 |      C |
|   3 |      D |
|   4 |      E |



回答2:


And another one:

SELECT COALESCE(b.num, a.num) num, COALESCE(b.letter, a.letter) letter
  FROM a FULL JOIN b ON a.num = b.num
 ORDER BY 1;

With your data:

WITH a AS 
(SELECT 1 num, 'A' letter FROM dual
 UNION ALL SELECT 2, 'B' FROM dual
 UNION ALL SELECT 3, 'C' FROM dual),
b AS
(SELECT 2 num, 'C' letter FROM dual
 UNION ALL SELECT 3, 'D' FROM dual
 UNION ALL SELECT 4, 'E' FROM dual)
SELECT COALESCE(b.num, a.num) num, COALESCE(b.letter, a.letter) letter
  FROM a FULL JOIN b ON a.num = b.num
 ORDER BY 1;

       NUM L
---------- -
         1 A
         2 C
         3 D
         4 E



回答3:


The efficiency might be lacking, but it produces the correct answer.

select nums.num, coalesce(b.letter, a.letter)
from
(select num from b
union
select num from a) nums
left outer join b
on (b.num = nums.num)
left outer join a
on (a.num = nums.num);



回答4:


Or you can use Oracle-specific technique to make the code shorter: http://www.sqlfiddle.com/#!4/0b796/11

with a as
(
  select Num, 'A' as src, Letter
  from tblA
  union
  select Num, 'B' as src, Letter
  from tblB
)
select Num, min(Letter) keep(dense_rank first order by src desc) as Letter
from a
group by Num
order by Num;

Output:

| NUM | LETTER |
----------------
|   1 |      A |
|   2 |      C |
|   3 |      D |
|   4 |      E |

The code works regardless of min(letter) or max(letter), it has the same output, it gives the same output. Important is you use keep dense_rank. Another important thing is, the order matter, we use order by src desc to give priority to source table B when keeping a row.


And to really make it shorter, use keep dense_rank last, and omit the desc on order by, asc is the default anyway http://www.sqlfiddle.com/#!4/0b796/12

with a as
(
  select Num, 'A' as src, Letter
  from tblA
  union
  select Num, 'B' as src, Letter
  from tblB
)
select Num, min(Letter) keep(dense_rank last order by src) as Letter
from a
group by Num
order by Num;

Again, using min or max on Letter doesn't matter, as long as your keep dense_rank get the prioritized/preferred row




回答5:


Another option is to combine the UNION and MINUS commands as follows:

SELECT 
    NUM, LETTER
FROM 
    TABLE B 
UNION 
( SELECT 
      NUM, LETTER 
  FROM 
      TABLE A 
  WHERE 
      NUM IN (SELECT
                  NUM
              FROM 
                  TABLE A 
              MINUS 
              SELECT 
                  NUM
              FROM 
                  TABLE B ))



回答6:


SELECT A.*
FROM A
WHERE A.NUM NOT IN
  (SELECT A.NUM
  FROM B
  WHERE A.NUM=B.NUM
  AND B.NUM IS NOT NULL
  AND A.NUM IS NOT NULL
  )
UNION
SELECT * FROM B;


来源:https://stackoverflow.com/questions/11059372/oracle-sql-combining-two-tables-but-taking-duplicates-from-one

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!