1. 程式人生 > >Oracle查詢優化改寫_3

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. 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;
  1. 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(+);
  1. 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;
  1. 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