SQLCookBook第三章學習日記9

2021-07-24 10:20:35 字數 4735 閱讀 6661

問題:

要返回在部門10中每個員工的姓名,以及部門的工作地點,下面的查詢達到的是錯誤資料:

selelct e.ename,d.loc

from emp e ,dept d

where e.deptno =10

解決方案:在from子句對錶進行連線來返回正確的結果集:

select e.ename,d.loc

from emp e,dept d

where e.deptno =10

and d.deptno = e.emptno

討論:

看錶dept中的資料,可以看出,部門10的工作地點是在new york,所以,在返回值中部門所在地點除了new york以外的任何值都是錯誤的。錯誤查詢得到的行數是from子句後面兩個表基數的積。在原查詢中,對錶emp的篩選條件是部門為10,結果有3行,因為沒有對錶dept進行篩選,表dept的所有四行全部返回,3乘以4得12,所以這個錯誤查詢就返回了12行。一般來說,要避免產生笛卡爾積,需要使用n-1規則,這裡的n為from子句中表的數量,並且n-1是要避免產生笛卡爾積的最小連線數。根據在表中的關鍵字和鏈結列不同,可能需要超過n-1個連線,但是對黨寫查詢來說,n-1是乙個好的開始。

注意:如果笛卡爾積應用適當也很有用。很多查詢都用到了笛卡爾積,常用的場合有轉置(反向轉置)結果集,產生順序值和模擬迴圈等

問題:要在包含多個表的查詢中執行聚集運算,要確保表間連線不能使聚集運算發生錯誤.例如,要查詢在部門10中所有員工的工資合計和獎金合計.由於有些員工的獎金激勵不只一條,在表emp和表emp_bonus之間做連線會導致聚集函式sum算得的值錯誤.

現在,考慮一下下面的返回的在部門10中所有員工的工資和獎金的查詢。表bonus中的type欄位決定獎金額,型別1的獎金為員工工資的10%,型別2為20%,型別3為30%。

select e.empno,

e.ename,

e.sal,

e.deptno,

e.sal*case

when eb.type = 1

then

.1when eb.type = 2

then

.2else

.3end

as bonus

from emp e ,emp_bonus eb

where e.empno = eb.empno

and e.deptno = 10

進行到這,一切正常,然而為了計算獎金總數而跟表emp_bonus做聯接時,錯誤出現了:

select deptno,

sum(sal) as total_sal,

sum(bonus) as total_bonus

from (

select e.empno,e.ename,e.sal,e.deptno,

e.sal*case

when eb.type = 1

then

.1when eb.type = 2

then

.2else

.3end

as bonus

from emp e,emp_bonus eb

where e.empno = eb.empno

and e.deptno = 10

)xgroup

by deptno

儘管total_bonus所返回的值是正確的,total_sal卻是錯誤的。

total _sal為什麼錯了,因為聯接導致sal列存在重複。考慮下面的查詢。該查詢聯接表emp和emp_bonus

select e.ename,e.sal

from emp e,emp_bonus eb

where e.empno = eb.empno

and e.deptno =10

現在可以很容易的看出total_sal為什麼錯了,因為miller的工資被統計了兩次。

解決方案:當處理聚集與聯接混合操作時,一定要小心。如果聯接產生重複行,可以有兩種方法來避免聚集函式計算錯誤,方法之一,只要在呼叫聚集函式時使用關鍵字distinct,這樣每個值只參與計算一次,另一種方法是,在進行連線前先只想聚集操作(在內聯檢視中),這樣,因為聚集計算已經在連線前完成了,所以就可以避免聚集函式計算錯誤,從而可以完全避免產生此問題。下面列出的解決方案使用了distinct關鍵字,而」討論」部分將討論如何在聯結前使用內聯檢視來只想聚集操作。

mysql和postgresql

使用distinct開關鍵字只對不相同的工資求和;

select deptno,

sum(distinct sal) as total_sal,

sum(bonus) as total_bonus

from (

select e.empno,

e.ename,

e.sal,

e.deptno,

e.sal *case

when eb.type = 1.

then

.1when eb.type = 2

then

.2else

.3end

as bonus

from emp e ,emp_bonus eb

where e.empno = eb.empno

and e.deptno = 10

)x group

by deptno

db2,oracle 和sql server

這些平台也支援上面的解決方案,此外他們還支援另一種使用視窗函式 sum over方案:

select

distinct deptno,total_sal,total_bonus

from (

select e.empno,

e.ename,

sum(distinct e.sal) over

(partition by e.deptno) as total_sal,

e.deptno,

sum(e.sal*case

when eb.type = 1

then

.1when eb.type = 2

then

.2else

.3end

)over

(partition by deptno) as total_bonus

from emp e,emp_bonus eb

where e.empno = eb.empno

and e.deptno = 10

)x

討論:

mysql和postgresql

在問題部分的第二個查詢在連線表emp和emp_bonus時,對員工「miller」產生了兩條記錄,這就是導致計算emp.sal的和出錯的原因(其工資加了兩次)。解決方案是只把不同的emp.sal值相加。下面的查詢是另外一種解決方案。首先計算部門10中工資合計,然後將該行跟表emp連線,最後聯接到表emp_bonus下面的查詢可以用於dbms:

select d.deptno,

d.total_sal,

sum(e.sal*case

when eb.type = 1

then

.1when eb.type = 2

then

.2else

.3end) as total_bonus

from emp e,emp_bonus eb,

(select deptno,sum(sal) sass total_sal from emp

where deptno = 10

group

by deptno

) dwhere e.deptno = d.deptno

and e.empno = eb.empno

group

by d.deptno,d.total_sal

db2、oracle 和sql server

另一種解決方案發揮了視窗函式sum over的優勢。

select e.empno,e.ename,

sum(distinct e.sal) over

(partition by e.deptno) as total_sal,

e.deptno,

sum(e.sal *case

when eb.type = 1

then

.1when eb.type = 2

then

.2else .e end) over

(partition by deptno) as total_bonus

from emp e,emp_bonus eb

where e.empno = eb.empno

and e.deptno = 10

在上述查詢中,sum over視窗函式被兩次呼叫,第一次用來計算給定分割槽或組中不同工資合計,本例中,分割槽為deptno為10,部門10不相同工資的總額為8750;第二次呼叫sum over 用來計算同一分割槽的獎金合計。取total_sal dept_no 和total_bonus 的唯一值就得到最終結果集。

SQLCookBook第三章學習日記10

問題 同時返回多個表中丟失的資料。要從表dept中返回emp不存在的行 所有沒有員工的部門 需要做外聯結。考慮下面的查詢。它返回表dept中的deptno和name欄位,以及每個部門中所有員工的姓名。如果該某個部門有員工的話 select d.deptno,d.dname,e.ename from ...

sql cookbook 第三章 操作多個表 記

3.4 從乙個表中查詢另乙個沒有值 問題 要從表dept中查詢在表emp中不存在資料的所有部門 1.沒有deptno為null時 select deptno from dept where deptno not in select deptno from emp 2.當emp表中有deptno為nu...

第三章 Data語意學

1 關於data member的繫結 對於memner function的本體分析,會直到整個class的宣告都出現了才才開始。因此乙個inline member function軀體內的乙個data member的繫結操作,會在整個class宣告之後才發生。但是,對於member function...