問題:
要返回在部門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...