寫了兩篇《MySQL入門》以後我發現,寫書的人還是都挺有本事的,起碼人家知道怎麼編排自己想講的知識點,我實在是不知道該先說那裡後說哪裡,那我就想到什麼講什麼吧。 一 寫SQL 其實我是不想寫有關SQL的部分的,因為這個部分其實很簡單,基本上大學只要好好聽聽資料庫概論這門課基本上都能寫滿足功能的SQL
寫了兩篇《MySQL入門》以後我發現,寫書的人還是都挺有本事的,起碼人家知道怎麼編排自己想講的知識點,我實在是不知道該先說那裡後說哪裡,那我就想到什麼講什麼吧。
一 寫SQL
其實我是不想寫有關SQL的部分的,因為這個部分其實很簡單,基本上大學只要好好聽聽資料庫概論這門課基本上都能寫滿足功能的SQL,但是後來想想,SQL其實是人和資料庫交互的一種介面,不會SQL確實是不可以的,寫的不好感覺不出資料庫有多麼的強大,甚至有可能會讓人產生出資料庫慢的壞印象。
MySQL作為most popular的開源資料庫,其實在SQL支持這裡做的不是那麼完美,比如5.6為止還是不支持全外連接(5.7沒試過是不是支持)。如果有人說全外連接這東西不支持就不支持了,沒什麼,但是我想說的是既然RDBMS是基於Codd的理論,那麼全外連接作為關係代數的一部分,不實現是有點說不過去的。相反,Berkeley出身的PostgreSQL就做的很好了,畢竟人家是most advanced開源資料庫。
閑話少敘。其實平時很多程式員寫的最多的SQL是這樣子的:
select t1.a, t1.b, t2.c, t2.d from table1 t1, table2 t2 where t1.m = t2.m and t1.k = ?;
這個寫法一點問題都沒有,特別的自然,翻譯成漢語是這樣的:
我需要從table1和table2中取一些數據出來,這兩張表通過m列關聯起來取交集,其中table1要卡k條件。
這個SQL按下不表,繼續說些技術性的東西。
基礎語法的東西就不說了,說一下連接。使用頻率最高的就是內連接和外連接兩種了。連接在我看來就是一種集合運算,比如內連接就是很自然的取交集運算,以兩張表的內連接說,就是A和B兩個集合做了A∩B運算。
內連接,翻譯成英文就是inner join;外連接,翻譯成英文就是outer join。所以說SQL是很好理解的,甚至接近了自然的語言。
寫一個內連接:
select t1.a, t1.b, t2.c, t2.d from table1 t1 inner join table2 t2 on t1.m = t2.m where t1.k = ?;
這個SQL就是上面那個SQL的等價形式,只不過這是ANSI SQL92寫法,上面那種是ANSI SQL89寫法,SQL是一種語言,更是一種標準,新的標準更好理解,顯然的讓我們知道了這個SQL採用了內連接的形式。
至於連接具體怎麼寫,我很早以前寫了一篇筆記:《Oracle的連接》。我就不重覆發明輪子了,雖然當時採用的是Oracle,但是我說過了,SQL是一種標準,因此放到任何一個RDBMS里,這些都是通用的。
我本來就沒有打算把這個寫成什麼正式的東西,我沒有寫書的夢想,所以基本上都是扯淡,扯淡的中途講點知識。
二 數據類型
SQL也是一種編程語言!SQL也是一種編程語言!SQL也是一種編程語言!
我要把這個強調三遍,因為很多很多,多入牛毛的同行都會覺得寫SQL是個很簡單的事情,SQL就是增刪改查數據,學一個小時就會的東西,是個很low的東西。我見過的很多人都問我SQL到底有什麼寫的,不就些破命令麽?我真的不知道怎麼回答他們。
SQL真的可以做很多事情,而寫好SQL並不太容易,當然,更不難。
MySQL支持的數據類型大致分為:數字類型,時間日期類型,字元型。
以前玩兒Oracle的時候,數字類型通通是number型,好用到沒朋友。但是MySQL不是這樣搞的,MySQL的數字類型分為tinyint, smallint, mediumint, int, bigint。每種類型都會有“有符號”和“無符號”的屬性,他們的範圍如下表:
類型 | 占用空間(位元組) | 最小值 | 最大值 |
tinyint | 1 | -128 | 127 |
tinyint(unsigned) | 1 | 0 | 255 |
smallint | 2 | -32768 | 32767 |
smallint(unsigned) | 2 | 0 | 65535 |
mediumint | 3 | -8388608 | 8388607 |
mediumint(unsigned) | 3 | 0 | 16777215 |
int | 4 | -2147483648 | 2147483647 |
int(unsigned) | 4 | 0 | 4294967295 |
bigint | 8 | -9223372036854775808 | 9223372036854775807 |
bigint(unsigned) | 8 | 0 | 18446744073709551615 |
很難記,但是其實不是很難,占用空間這裡呢,我們都知道1Byte等於8bit,那麼tinyint的取值範圍就應該是[-2^(8-1), 2^(8-1)-1]。如果是無符號類型的取值範圍就能推導出來了,有基本數學知識都能理解。
我第一次玩兒MySQL的時候,發現列類型經常是int(4)這樣的,這個4是什麼呢?從上面的表中明顯可以看出數字型占用的空間總是一定的,那這裡的4就不是char(4)裡面那種意思了,這其實是填充格式用的,要和ZEROFILL屬性一起用的,這樣就能讓欄位被填充滿,不過不影響取值,感覺像是語法糖之類的東西。
後來我覺得MySQL的varchar型有意思,有意思就在於varchar括弧內的長度不是位元組數,而是字元數,與漢字英文無關,就是字元數,這個感覺又是一個語法糖之類的東西,我在設計表的時候就不需要考慮什麼字元集了,直接告訴程式員我給你了一個列,這個列最多存100個字元,你們自己控制。
但是這裡還是有個陷阱。很多書上和網上的資料都會說,InnoDB表的所有varchar欄位長度的總和不得超過65535,就說到了這裡就不說了。實際上不能忽略一個前提,就是字元集應該選擇latin1,而且絕對不是65535,是65532,還有些別的開銷。如果換成UTF8字元集呢?這個理論總和限制就會變成21845,這個數字其實就是65535/3,實際上的限制是21844。所以我說這個是個語法糖,其實還是字元集相關的,只是不再讓上層的程式員去考慮了,而是由DB自己保證。
時間和日期類型MySQL也是很豐富的,而且特別簡單,'2015-12-12 12:31:20'這樣就可以,Oracle的話還得寫個什麼函數去把字元串轉成date型。當然時間和日期類型也是有空間占用的,很多資料上都會說用TIMESTAMP類型取代DATETIME類型,這樣節省空間,確實沒有問題,TIMESTAMP占用的空間僅有DATETIME的一半,但是要註意,TIMESTAMP的範圍不大,1970年到2038年,節省空間是對的,但是要註意應用場景。
當然還有很討厭的BLOB和TEXT這樣的大數據類型(huge size data type),這可不是現在特別火的big data。這部分數據嘛,我覺得能不用就別用了,他們的存儲都是在單獨的地方,效率上我個人覺得堪憂。不過還是那句話,設計的時候要註意應用場景,給恰當的數據類型。
今天就扯到這裡吧。