Oracle查詢優化改寫_3
對應的第三章”操作多個表“
1.UNION ALL與空字串
UNION ALL 常常用來合併多個結果集。
select ename AS 名稱 from emp where empno = 7788
UNION ALL
select null AS 名稱 from dual
UNION ALL
select '' AS 名稱 from dual;
得到的結果如下:
名稱 |
---|
SCOTT |
null |
null |
可以看到,當一個數據集列不夠時,可以用null來填充該列的值,而空字串在Oracle中常常也相當於null;
為什麼不說空字串等於null呢?看下面的示例:
select sal AS 工資 from emp where empno = 7788
UNION ALL
select '' AS 工資 from dual;
--報錯:表示式必須具有與對應表示式相同的資料型別【sal型別:"NUMBER" ,''型別:"varchar2"】
可以看到,空字串本身是 varchar2型別,這區別於null可以是任何型別,因此,它們是不等價的。
2.UNION和OR
UNION ALL和UNION的區別
1. UNION ALL合併結果集的時候,資料可能會重複。
2. UNION 合併結果集的時候,會對資料進行去重操作。
在使用UNION和OR時,要特別注意,防止由於去重導致查詢結果錯誤。
SQL1:
select empno,deptno from emp where mgr = 7698 order by 1;
SQL2:
select empno,deptno from emp where job = 'SALESMAN' order by 1;
兩個sql得到的結果如下:
SQL1:
empno | deptno |
---|---|
7944 | 30 |
7521 | 30 |
7654 | 30 |
7844 | 30 |
7900 | 30 |
SQL2:
empno | deptno |
---|---|
7944 | 30 |
7521 | 30 |
7654 | 30 |
7844 | 30 |
1)如果使用OR條件,查詢結果將是5條
select deptno from emp where mgr = 7698 or job = 'SALESMAN' order by 1;
deptno |
---|
30 |
30 |
30 |
30 |
30 |
2)使用UNION來合併兩個條件的查詢結果集
select deptno from emp where mgr = 7698
UNION
select deptno from emp where job = 'SALESMAN';
得到的結果如下
deptno |
---|
30 |
在這種情況下,我們在使用UNION來去重合並結果集時,一定要在查詢的時候,加一個可以唯一標識各行的列,例如 empno。
除了可以使用唯一列,主鍵外,也可以用rowid。
3.inner join/left join/right join/full join 區別
- 1)inner join:內連線,返回左右表中,相匹配的資料。
select l.str AS left_str,r.str AS right_str from left_table l
inner join right_table r on l.v = r.v;
--使用where條件
select l.str AS left_str,r.str AS right_str from left_table l ,right_table r
where l.v = r.v;
- 2)left join:左(外)連線,左表為主表,左表返回所有的資料,右表只返回與左表相匹配的資料。右表的欄位,查詢結果可能為空
select l.str AS left_str,r.str AS right_str from left_table l
left join right_table r on l.v = r.v;
--加(+)後的寫法
select l.str AS left_str,r.str AS right_str from left_table l ,right_table r
where l.v = r.v(+);
- 3)right join:右(外)連線,右表為主表,右表返回所有的資料,左表只返回與右表相匹配的資料。左表的欄位,查詢結果可能為空
select l.str AS left_str,r.str AS right_str from left_table l
right join right_table r on l.v = r.v;
--加(+)後的寫法
select l.str AS left_str,r.str AS right_str from left_table l ,right_table r
where l.v(+) = r.v;
- 4)full join:完全連線,返回左表和右表中的全部資料,左右表相匹配的資料在同一行,非匹配的資料,只顯示左表或右表的資料。左表,右表的欄位,查詢結果都可能為空,但不同時為空。
select l.str AS left_str,r.str AS right_str from left_table l
full join right_table r on l.v = r.v;
--full join 沒有(+)的寫法
4.外連線中,條件不要亂放
select l.str AS left_str,r.str AS right_str,r.status from left_table l
left join right_table r on l.v = r.v order by 1, 2;
left_str | right_str | status |
---|---|---|
left_1 | ||
left_2 | ||
left_3 | right_3 | 1 |
left_4 | right_4 | 0 |
此時,只想要right_table表顯示 status=1的部分資料,即要求顯示結果如下所示:
left_str | right_str | status |
---|---|---|
left_1 | ||
left_2 | ||
left_3 | right_3 | 1 |
left_4 |
4.1)修改sql如下:
left join 用法:
select l.str AS left_str,r.str AS right_str,r.status from left_table l
left join right_table r on l.v = r.v where r.status = 1 order by 1,2;
(+)用法:
select l.str AS left_str,r.str AS right_str,r.status from left_table l, right_table r
where l.v = r.v(+) and r.status = 1 order by 1,2;
此時得到的結果如下:
left_str | right_str | status |
---|---|---|
left_3 | right_3 | 1 |
查詢結果錯誤。
4.2)正確的sql如下:
left join 用法:
select l.str AS left_str,r.str AS right_str,r.status from left_table l
left join right_table r on l.v = r.v and r.status = 1 order by 1,2;
(+)用法:
select l.str AS left_str,r.str AS right_str,r.status from left_table l, right_table r
where l.v = r.v(+) and r.status(+) = 1 order by 1,2;
上述sql,也可以像下面這樣寫
select l.str AS left_str,r.str AS right_str,r.status from left_table l
left join (select * from right_table where status = 1) r on l.v = r.v order by 1,2;
5.比較兩張表中資料是否相同嗎,以及相同資料的條數是否相同
5.1)比較兩個表資料是否相同
select
a.empno,a.empname,b.empno,b.empname
from
emp_a a full join emp_b b
on
a.empno = b.empno
where
(a.empno IS NULl or b.empno IS NULL);
此時可以比較兩張表中資料是否相同,但是無法比較兩張表中相同資料的條數,是否相同。
5.2)表兩張表中資料,以及相同資料的條數是否相同
select
a.empno,a.empname,a.num,b.empno,b.empname,b.num
from
(select empno,empname,count(*) as num from emp_a group by empno,empname) a
full join
(select empno,empname,count(*) as num from emp_b group by empno,empname) b
on
(a.empno = b.empno and a.num = b.num)
where
(a.empno IS NULl or b.empno IS NULL);
6.空值處理
6.1)如果null參與算術運算[+,-,*,],則該算術表示式的值為null。
6.2)如果null參與比較運算[:>=,<=,<>],則結果可視為false。要使用 IS NULL,IS NOT NULL
6.3)使用count(*),count(1),計數會包含NULL值和重複項;使用count(欄位1),計數時不包含,欄位1為null的資料。
6.4)Oracle中,如果子查詢包含空值null,則 NOT IN(該子查詢)返回結果為空。
select 12 as a from dual WHERE 1 IN (1,,NULL);
A
---
12
select 12 as b from dual WHERE 1 NOT IN (2,3,,NULL);
B
---
null