问题
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