1. 程式人生 > >10個簡單步驟,完全理解SQL

10個簡單步驟,完全理解SQL

轉載自:http://blog.jobbole.com/55086/

1 sql是一種宣告式語言 
sql語言是為計算機聲明瞭一個你想從原始資料中獲得什麼樣的結果的一個範例,而不是告訴計算機如何能夠得到結果。 
2sql的語法並不是按照語法順序執行 
select [distinct] 
from 
where 
group by 
having 
union 
order by 
a, T he first thing that happens is loading data from the disk into memory ,in order to operate on such data. 
b,select 是在大部分語句執行了之後才執行的,嚴格的說是在from 和group by之後執行的。理解這一點是很重要的,這就是你不能再where 中使用在select 中設定別名的欄位作為判斷條件的原因 
select是最後執行的語句 
重寫別名: 
select a.x +a.y as z from a where(a.x+ a.y) = 10

c 無論在語法上還是執行順序上,union總是排在order by之前,很多人認為每個union段都能使用order by 排序,但是根據sql語言標準和各個資料庫sql的執行差異來看,這並不是真的,儘管某些資料允許sql語句對子查詢subqueries 或者派生表derived tables 進行排序,但是這並不說明這個排序在union操作過後仍保持排序後的順序

3sql語言的核心是對錶的引用 tablereferences 
from a,b 
上面這句table語句的輸出是一張聯合表,聯合了表a和表b,如果a表有三個欄位,b表有5個欄位,輸出是8個欄位。一張聯合表,來自於所有引用的表在某一個維度上的聯合, 
這個聯合表中的資料時a*b,即a和b的笛卡爾積,換句話說,也就是a表中的每一條資料都要跟b中的每一條資料配對,如果a表有3條資料,b表有5條資料,那摩聯合表就會有15條資料。

from 輸出的結果被where語句篩選後要經過group by語句處理,從而形成新的輸出結果。 
如果我們從集合論關係代數的角度來看,一張資料庫的表就是一組資料元的關係,而每個sql語句會改變一種或者數種關係,從而產生新的資料元的關係(即產生新的表)

4靈活引用表能使sql語句變的更強大 
靈活引用表能使sql語句標的更加強大,一個簡單的例子就是join的使用,join語句並非是select中的一部分,而是一種特殊的表引用語句,sql語言標準中表的連線定義如下: 
::= 


| joined table>
from a,b 
a1 join a2 on a1.id = a2.id 
將它放到之前的例子中就變成了: 
from a1 join a2 on a1.id =a2.id,b 
儘管講一個連線表用逗號跟另一張表聯合在一起並不是常用做法,但是你的確可以這麼做。結果就是,最終輸出的表就有了a1 +a2 +b個欄位了

思考問題時,要從表引用的角度出發,這樣就很容易理解資料時怎麼樣被sql語句處理的,並且能幫助你理解哪些複雜的表引用是做什麼的 
更重要的是,要理解join是構建連線表的關鍵詞,並不是select語句的一部分,有些資料庫允許在insert,update,delete中使用join

5sql語句中推薦使用表連線

from a,b 
高階sql程式設計師也許會給你忠告,儘量不要使用都好來代替join進行表的連線,這樣會提高你的sql語句的可讀性,並且可以避免一些錯誤 
記著要儘量的使用join進行表的連線,永遠不要在from後面使用逗號連線表

6sql語句中不同的連線操作 
sql語句中,表連線的方式從根本上分為5種: 
equi join 
semi join 
anti join 
cross join 
division 
EQUI JOIN 
這是一種最普通的join操作,它包含兩種連線方式: 
inner join (join) 
outer join(left,right,full outer join) 
this table reference contains authers and their books 
there is one record for each book and its author 
authors without books are NOT included 
authors JOIN book ON author.id =book.author.id

this table reference contains authors and their books 
there is one record for each book and its author 
or there is an “empty” record for authors without book 
(“empty” meaning that all book columns are null) 
author LEFT OUTER JOIN book ON author.id =book.author.id

SEMI JOIN 
這種連線關係在sql中有兩種表現方式:使用IN,或者使用EXISTS. 
“SEMI”在拉丁文中是”半“的意思,這種連線方式是隻連線目標表的一部分。我們不需要作者和書名的這樣的組合,只是需要哪些在書名錶中的書的作者資訊。那我們 
就能這麼寫: 
using IN 
from author 
where author.id IN (select book.author_id from book) 
using EXISTS 
FROM AUTHOR 
WHERE EXISTS (SELECT 1 FROM BOOK WHERE BOOK.AUTHOR_ID = AUTHOR.ID

儘管沒有嚴格的規定說明你何時應該使用IN,何時應該使用EXISTS,但是這些事情你還是應該知道的 
IN比EXISTS 的可讀性更好 
EXISTS 比IN 的表達性更好(更適合複雜的語句) 
二者之間效能沒有差異(但對於某些資料庫來說效能差異會非常大)

因為使用INNER JOIN也能得到書名中書對應的作者資訊,所以很多初學者認為可以通過DISTINCT進行去重,然後將SEMI JOIN IN 語句寫成這樣 
—-find only those authors who also have books 
select distinct frist_name,last_name 
from author 
JOIN book ON author.id =book.author_id 
這是一種很糟糕的寫法,原因如下: 
sql語句效能底下:因為去重操作(distinct)需要資料庫重複從硬碟中讀取資料到記憶體中。(distinct的確是一種很耗費資源的操作,但是每種資料庫對於distinct的操作方式可能不同) 
這麼寫並非完全正確:儘管也許現在這麼寫不會出現問題,但是隨著sql語句變得越來越複雜,你想要去重得到正確的結果就變得時分困難。 
http://blog.jooq.org/2013/07/30/10-common-mistakes-java-developers-make-when-writing-sql/(distinct的危害)

ANTI JOIN 
這種連線的關係跟SEMI JOIN剛好相反, 在IN 或者EXISTS 前加一個NOT 關鍵字就能使用這種連線。舉個例子來說,我們列出書名錶裡沒有書的作者:

using IN 
from author where author.id NOT IN (select book.author_id from book)

using EXISTS 
from author where NOT EXISTS (select 1 from book where book.author_id=author.id 
關於效能。可讀性,表達性等特性也完全可以參考SEMI JOIN 
http://blog.jooq.org/2012/01/27/sql-incompatibilities-not-in-and-null-values/)。(not in遇到null時應該怎麼辦)

cross join 
這個連線過程就是兩個連線的表的乘積:即將第一張表的每一天資料分別對應第二張表的每條資料。我們之前見過,這就是逗號在from語句中的用法 
在實際應用中,很少有地方能用到cross join,但是一旦用了,你就可以用這樣的sql語句表達 
combine every author with every book 
author cross join book

division 
division的確是一個怪胎,簡而言之,如果join是一個乘法運算,那摩division就是join的逆過程。division的關係很難用sql表達出來,結餘一個新手指南,解釋division已經超過了我們的目的。(http://blog.jooq.org/2012/03/30/advanced-sql-relational-division-in-jooq/) 
(http://en.wikipedia.org/wiki/Relational_algebra#Division) 
(https://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/)。(相關文件,可自行查閱) 
推薦閱讀 →_→ 《畫圖解釋SQL聯合語句》http://blog.jobbole.com/40443/

我們學到了什麼? 
sql是對錶的引用,join則是一種引用表的複雜方式。但是sql語言的表達方式和實際我們所需要的邏輯關係之間是有區別的,並非所有的邏輯關係都能找到對應的join操作 
所以這就要我們在平時多積累和學習關鍵邏輯,這樣就能在以後編寫sql語句中選擇適當的join操作了

7 sql中如同變數的派生表 
sql是一種宣告性的語言,並且sql語句中不能包含變數,但是你能寫出類似於變數的語句,這些就是叫做派生表: 
說白了,所謂的派生表就是在括號中的子查詢:

—A DERIVED TABLE 
from (select * from author)

需要注意的是有些時候我們可以給派生表定義一個相關名,及我們所說的別名。 
—-A derived table with an alias 
from (select * from author ) a 
派生表可以有效的避免由於sql邏輯而產生的問題。如果想重用一個select和where語句查詢出的結果,就可以寫成這樣:以oracle為例:

–get authors` frist and last name ,and their age in days 
select frist_name,last_name,age 
from ( 
select first_name,last_name,current_date - date_of_birth 
from author 

—-if the age is greater than 10000 days 
where age >10000 
需要我們注意的是:在有些資料庫,以及sql:1990標準中,派生表被歸為下一級–通用表語句(common table experssion)。 
這就允許你在一個select 語句中對派生表多次重用,上面的例子就是等價於下面的語句: 
with a as( 
    select first_name,last_name,current_date -date_of_birth  
from author 
) 
select *  
from a 
where age >10000

當然你可以給a建立一個單獨的檢視,這樣你就可以在更廣泛的範圍內重用這個派生表了。 
(http://en.wikipedia.org/wiki/View_%28SQL%29)。 
sql語句就是對錶的引用,而非對欄位的引用,要好好利用這一點,不要害怕使用派生表或者其他更復雜的語句

8 sql語句中group by 是對錶的引用進行操作 
from a,b 
group by A.x,A.Y,B.z 
上面語句的結果就是產生出了一個包含三個欄位的新表的引用,當你應用group by的時候,。select後沒有使用聚合函式的列,都要出現在group by 後面。即你能對其進行下一級邏輯操作的列會減少,包括在select中的列。

需要注意的是:其他欄位能使用聚合函式: 
select a.x,a.y.sum(a.z) from a group by a.x,a.y 
還有一點值得留意的是,mysql並不堅持這個標準,這的確是令人困惑的地方。

group by ,再次強調一次,是在表的引用上進行了操作,將其轉換為一種新的引用方式。 
9sql語句中的select實質上是對關係的對映 
一旦建立起來了表的作用,進過修改,變形,你能夠一步一步的將其對映到另一個模型中。select 語句就像一個“投影儀”,我們可以將其理解成一個將源表中的資料按照一定的邏輯轉換成目標表資料的函式 
通過select語句,你能對每一個欄位進行操作,通過複雜的表示式生成所需要的資料 
select語句有很多特殊的規則,至少你應該熟悉以下幾條: 
1 你僅能夠使用那些能通過表引用而得來的欄位 
2如果你有group by 語句,你只能夠使用group by 語句後面的欄位或者聚合函式; 
3當你的語句中沒有group by 的時候,可以使用開窗函式代替聚合函式, 
4當你的語句沒有group by 的時候,你不能同時使用聚合函式和其他函式 
5有一些方法可以將普通函式封裝在聚合函式中

為什麼不能再一個沒有group by的select語句中同時使用普通函式和聚合函式? 
1,憑直覺,這種做法從邏輯上就講不通 
2如果直覺不能夠說服你,那麼語法肯定能。sql;1990標準引入了grouping sets,sql;2003標準引入了group sets:group by() 
無論什麼時候,只要你的語句中出現了聚合函式,而且沒有明確的group by語句,這時一個不明確的,空的grouping set 就會被應用到這段sql中,因此,原始的邏輯順序的規則就被打破了,對映關係首先會影響到邏輯關係,其次語句關係

這段話原文就比較艱澀,可以簡單理解如下:在既有聚合函式又有普通函式的 SQL 
語句中,如果沒有 GROUP BY 進行分組,SQL 語句預設視整張表為一個分組,當聚合函式對某一欄位進行聚合統計的時候,引用的表中的每一條 
record 就失去了意義,全部的資料都聚合為一個統計值,你此時對每一條 record 使用其它函式是沒有意義的 
select 語句可能是sql語句中最難得部分了,儘管他看上去很簡單,其他語句的作用其實就是對錶的不同形式的引用,而select語句則是把這些引用整合到了一起,通過邏輯規則將源表對映到目標表,而且這個過程是可逆的,我們可以清楚的知道目標表的資料是怎麼來的 
想要學習好sql語言,就要在使用射了select語句之前弄懂其他的語句,雖然select是語法結構中的第一個關鍵詞,但是他應該是最後一個掌握的

10,sql語句中的幾個簡單的關鍵詞:distinct,union ,order by 和offset 
集合運算:distinct,union 
排序運算 order by ,offset。。。fetch 
集合運算:set operation 
集合運算主要是在於集合上,事實上指的就是對錶的一種操作。從概念上來說,他們很好理解: 
distinct 在對映之後對資料進行去重 
union將兩個子查詢拼接起來並去重 
union all將兩個子查詢拼接起來但不去重 
except 將第二個字查詢中的結果從第一個子查詢中去掉 
intersect 保留兩個子查詢中都有的結果並去重

排序運算 ordering operating 
排序運算跟邏輯關係無關,這是一個sql特有的功能,排序運算不僅在sql語句的最後,而且在sql語句執行的過程中也是最後執行的。使用order by he offset…fetch 是保證資料能夠按照順序排列的兒最有效的方式。其他所有的排序方式都有一定隨機性,儘管他們得到的排序結果是可重提的。

offset。。。set是一個沒有同意確定語法的語句,不同的資料庫有不同的表達方式,如mysql和postgresql的limit。。。sffset,sql server和sybase的top。。。start at等。 
(http://www.jooq.org/doc/3.1/manual/sql-building/sql-statements/select-statement/limit-clause/)。

10 Common Mistakes Java Developers Make when Writing SQL

10 More Common Mistakes Java Developers Make when Writing SQL
--------------------- 
作者:讓認真成為一種性格 
來源:CSDN 
原文:https://blog.csdn.net/liuyuzhu111/article/details/50514586 
版權宣告:本文為博主原創文章,轉載請附上博文連結!