隨着時代的發展,人類活動產生的信息越來越多,大家常說,現在這個時代是大數據時代。在這樣一個前提下,數據的存儲成為我們必須要認真對待和研究的問題了。SQL(Structured Query Language)結構化查詢語言,是當今三大主流關系型數據庫(MsSQL,MySQL,Oracle)的通用操作語言,今天就給大家分享一下我對數據庫和 SQL 的一些認識,希望對大家有用。
一 數據庫簡介
1,什么是數據庫
在理解什么是數據庫之前,我們首先要明白另一個問題,即什么是數據?
一個人的身高、體重、年齡;一輛車的品牌、顏色,尺寸;一個運動員在完成比賽期間所用的時間,速度,心率;即使你計算機上的圖片、視頻等都是數據。我的結論就是:用於描述事物或事件的屬性,並且能被人們識別的符號就是數據。
明白了什么是數據,不難推測出:數據庫就是數據存儲的倉庫,各種數據的集合。當然數據庫中的數據都是有序的,有組織的,這是為了能讓管理數據更方便。
2,什么是數據庫管理系統
你可能常常聽到人們常說:MsSQL 數據庫、MySQL 數據庫、Oracle 數據庫等等,其實他們多數指的是不同的數據庫管理系統。SQL server 是微軟旗下的數據庫管理系統,MySQL 和 Oracle 目前都屬於甲骨文公司。
數據庫管理系統本質上是一套軟件,專門用於幫助用戶管理數據庫中的數據。
3,什么是關系型數據庫
根據數據庫組織和存儲數據的方式,數據庫通常分為四類:層次式數據庫、網絡式數據庫、關系型數據庫、非關系型數據庫。
前兩種基本已經成為了歷史,現在主流的是后兩種,但占市場絕對優勢的仍然是關系型數據庫(Relational Database Management System:RDBMS)。顧名思義,關系型數據庫是依照數據與數據之間的關系來組織存儲數據的。
為了方便后面的書寫,這里進行一個聲明:從此之后,文中提到的所有數據庫均指關系型數據庫。
4,數據庫和 SQL 有什么聯系
SQL,指結構化查詢語言,全稱是 Structured Query Language。
SQL 是用於訪問和處理數據庫的標准的計算機語言。
SQL 是一種 ANSI(American National Standards Institute 美國國家標准化組織)標准的計算機語言。
SQL 是一種命令式語言。
當今三大數據庫管理系統均支持標准 SQL。
二 如何存儲
現在,我們已經知道什么是數據庫、數據庫管理系統和 SQL 了。那么,既然數據庫是用來存儲數據的,那么數據庫具體是通過什么方式來進行存儲的呢?它又是怎樣保證數據的有效性、合法性呢?既然說現今社會主流的仍然是關系型數據庫(RDBMS),那么數據庫又是怎么在數據與數據之間確定關系的呢?
1,數據的存儲方式
開門見山的說:數據庫是通過“表”來存儲數據,根據不同的數據庫管理系統,以不同的文件格式最終被保存到物理磁盤上(多是硬盤)。
這里的表類似 Excel 表格,表中的數據由不同的行和列組成,每一列在數據庫中我們稱為一個字段,每一行數據稱為一條記錄或元組,它算是數據在數據庫中的基本單元。
如果用一張表來記錄一類事物,那么每一條記錄可以看成這類事物單獨的個體,而不同的列則是用來記錄事物的每個具體的特性。例如一個Person 表,用來記錄人員信息,每個人員都可以擁有自己的姓名,年齡,性別,身高等信息,那么這個表應該具有姓名,年齡,性別等這些列。
| name | age | sex | height | birthday |
| 張三 | 20 | 男 | 180 | 2000-01-01 |
| 李四 | 18 | 女 | 170 | 2002-01-01 |
| 王五 | 25 | 男 | 165 | 1995-01-01 |
在這個表中,每一行就是一條記錄,代表一個具體的人,每一列都是用來描述人的不同特征的,從表中可以看出,每個人都擁有不同的特性。
2,確保數據的有效性
上面的表格有一個問題:不能確保數據的有效性、合法性。為什么這么說呢?
試想一下,這個世界上人那么多,總會有其他人也叫張三,並且年齡正好也是20,正好也是個身高180的大漢,那么在表中豈不是有兩條一模一樣的記錄?那我們怎么來區分到底誰是誰呢?還有,在不加任何限制的情況下,我可以隨意錄入人員的年齡,比如我錯把王五的年齡記錄成了250,這世上還沒有能活250歲的人吧,顯然這樣的數據是不合常理的。
那么數據庫是怎么解決這個問題的呢?答案是:約束。
約束作用於表的列,約束用於規定表中的數據存儲規則。如果我們在錄入數據時,不符合約束的規定,那么你將不能把該數據錄入數據庫中。
3,約束
數據庫中主要的約束有以下幾種:
NOT NULL - 指示某列不能存儲 NULL 值,即空,什么都沒有。
UNIQUE - 保證每行的某一列必須具有唯一的值,不能重復。
PRIMARY KEY - NOT NULL 和 UNIQUE 的結合。確保某列(或多個列的結合,即聯合主鍵)有唯一標識,有助於更容易更快速地找到表中的一個特定的記錄。
FOREIGN KEY - 保證表中某一列的數據來自另一個表中的某一列。能使用外鍵約束的列,其數據在另一個表中必須是主鍵。數據庫正是通過外鍵來建立表與表之間的聯系。
CHECK - 保證列中的值符合指定的條件,確保數據的合理性。
DEFAULT - 規定沒有給列賦值時的默認值。
本章主要講解數據庫如何存儲數據和如何確保數據的有效性,下一章將詳細介紹如何創建表,如何為表的字段添加約束,以及如何向表中插入和刪除數據等。
三 如何操作
1,基礎語法
前面提到,SQL 是命令式語言,所以它的語法其實非常簡單,每一條命令就是一條語句,每條語句以“;”結束(非必須)。並且 SQL 語句對大小寫不敏感,不過為了方便閱讀和維護代碼,請盡量統一命令的大小寫。
SQL 中的命令總共分為四大類:
A:DDL(Data Definition Language)數據定義語言
主要命令包括:create(創建)、alter(修改)、drop(刪除)
B:DQL(Data Query Language)數據查詢語言
主要命令包括:select
C:DML(Data Manipulation Language)數據操縱語言
主要命令包括:insert(插入)、update(修改),delete(刪除)
很多時候,select 命令也被認為是 DML 語言的一種,所以,如果你在其他地方聽到這種說法時,不必感到詫異。
D:DCL(data Control Language)數據控制語言
主要命令包括:grant(授權)、revoke(回收)、commit(提交)、rollback(回滾)等
數據控制語言主要是針對數據庫安全性方面的操作,可以簡單理解為權限管理,這部分命令 DBA 經常使用,一般數據開發人員用的較少。
2,使用方式
DDL:
1 create database "my_db";--創建一個庫 2 use "my_db";--選擇剛剛穿件的庫 3 create table "my_tb" 4 ( 5 --字段名1 數據類型 約束, 6 --字段名2 數據類型 約束, 7 --...... 8 );--在“my_db”中創建表“my_tb” 9 10 alter table "my_tb" 11 add 字段名 數據類型;--修改表,並向其新增一列 12 alter table "my_tb" 13 alter column 字段名 數據類型;--修改表中某一列的數據類型 14 15 drop table "my_tb";--刪除表 16 drop database "my_db";--刪除庫 17 truncate table 表名;--清空表中的數據
DQL:數據查詢語言是 SQL 的重中之重,將在下一頁單獨講解,這里僅給出 select 命令的基礎用法。
1 /*以上面的 Person 表為例*/ 2 select name ,age from Person;--查詢Person表中所有人員的姓名及年齡 3 select * from Person;--查詢Person表中的所有數據,*是通配符
DML:
1 /*以 Person 表為例*/ 2 insert into Person (name,age,sex,height) 3 values('小明',12,'男',150); 4 --向表中插入一條數據,指定插入的列 5 insert into Person 6 values ('小黑',22,'男',170); 7 --如果需要插入每一列的數據,可以不指定具體的列 8 update Person set sex='女' where name='小明'; 9 --修改小明的性別為女 10 delete from Person where name='小明'; 11 --刪除姓名為小明的記錄
3,創建約束
創建約束有兩種方式,其一:在創建表時同時創建約束,其二:同過 alter 命令向已創建的表添加約束。
創建表時:
1 /*創建表時即添加約束*/ 2 create table tablename 3 ( 4 id int identity(1,1) primary key, 5 name varchar(50) not null, 6 idcard char(18) unique, 7 city varchar(50) foreign key(city) references City(id),--city列的值通過外鍵綁定City表的id列 8 age int check (age>0 and age<150), 9 email varchar(50) not null 10 )
表已創建時:
1 /*通過 alter 命令添加約束*/ 2 alter table tablename 3 add 4 constraint ck_email check (email like '%@%'); 5 --通過 constraint 可以指定約束的名字,創建表時添加約束也可以使用,但它不是必須的
聯合主鍵:
1 --創建表時: 2 create table tablename 3 ( 4 col1 ... , 5 col2 ... , 6 ......, 7 constraint pk_name PRIMARY KEY (col1,col2) 8 ) 9 --表已創建: 10 alter table tablename 11 add 12 constraint pk_name PRIMARY KEY (col1,col2)
4,事務
事務是由一組 SQL 語句組成的執行單元,該執行單元被視為一個不可分割的整體,單元內的語句要么全部執行成功,要么全部失敗,不允許某些成功,而另外一些執行失敗。如果單元中某一條語句執行失敗,則前面所有被成功執行語句將回滾(使其失效),即數據回到那些語句還沒執行時的狀態。如果所有語句被全部成功執行,那么我們就說該事務被順利執行了。事務也是一種保證數據完整性的方式。
總結起來,事務具備以下 ACID 特性:
A(Atomicity 原子性):原子性是指事務是一個不可分割的執行單元。里面的操作要么都成功,要么都失敗。
C(Consistency 一致性):一致性是指事務必須是數據從一個一致性狀態過度到另一個一致性狀態。關於一致性,還是那個經典的轉賬例子:兩個人各有1000元錢,數據庫中存儲的錢總和是2000元,如果一個事務的操作是:他們兩之見轉一次賬,那么執行完事務之后,數據庫中存儲的錢總和還應該是2000元。
I(Isolation 隔離性):隔離性是指在被一個事務操作的數據,不應該再被另一個事務所干擾。迸發執行的各個事務之間不能相互干擾。
D(Durability 持久性):持續性是指一個事務被提交以后不可恢復,它對數據的影響是永久性的,如果需要在該事務提交后再恢復到原始狀態,只能通過另一個事務。
不同的數據庫管理系統對事務的支持存在較大差異。但通常情況下,對於數據的 DML 操作,每一條語句的執行都算一次事務,而且這種事務是隱式的,系統在后台直接開啟,並在執行完后關閉,用戶是不可見的。
對於不同數據庫中事務的具體實現,請持續關注我后續關於三大關系型數據庫的介紹文章。
5,數據類型
不同數據庫支持的數據類型有較大差異,即使相同的數據庫不同的版本也存在一定差異,所以在使用時請盡量以官方文檔為依據,這里僅列出部分常用的、通用的數據類型:
bigint(整型)、varchar(n)(可變長度字符串)、boolean(布爾值)、float(浮點型)、date(日期)、time(時間),timestamp(日期+時間型)、xml(XML型)。
四 如何查詢
相較於其他命令,數據庫開發中用的最多的就是 select 了,沒有之一。
1,普通查詢
通過幾條簡單的查詢語句來說明:
1 insert into Person values('張五',30,男,175,'1990-01-01'); 2 insert into Person (name,sex,height,birthday) 3 values('張六',男,175,'1990-01-01'); 4 --先插入兩條新數據 5 select * from Person as P 6 where P.birthday between '1990-01-01' and '2010-01-01' 7 and P.name like '%張%' 8 and P.age is not null 9 --查詢生日在1990-2010間姓名包含張並且年齡不為空的所有人員信息
通過上面的例子,我要說明查詢語句的一些基本用法。
首先是緊跟在 select 命令之后的信息,它表示需要被查詢的字段,* 星號表示通配符,意為查詢所有表中的字段。
其次是 from 關鍵字,它表示從哪個表中查詢數據,緊跟在其后的是被查詢的表名。
as 關鍵字的作用是給表起一個別名,主要是為了簡化代碼,被查詢的字段,也可以使用 as 起一個更通俗易懂的別名。
where 關鍵字用於指定過濾條件,通過 where 我們可以只查詢我們需要的數據,提高查詢速度。
and 關鍵字用來連接不同的過濾條件。
between...and... 是一個組合范圍關鍵字,如上例所示,它可以用來指定時間范圍,還可以用來指定數字的取值范圍等。
like 用來指定模糊查詢,% 表示零個或多個任意字符,_ 表示任意單個字符,[ ] 表示指定字符中的一個,[^ ] 表示不在指定字符中的一個。如果需要使用 like 查詢包含特殊字符的列,比如 %、_等,那么你需要用到 escape 定義轉意字符。
1 select * from table 2 where col like '%$_%' escape '$'; 3 -- $ 符號被定義成轉意字符,緊跟在 $ 后面的字符會被當成普通字符匹配
一個特別的:在過濾條件中,判斷某列的值是否為空,應該使用 is 或 not is 關鍵字,而不是使用 = 等號或 != 不等號 。
2,分組和排序
SQL 中的分組使用 group by 實現,group by 通常和聚合函數一起使用,單獨使用 group by 分組沒有現實意義。
SQL 中的聚合函數從列的計算中獲取值,一般返回一個單一的值。SQL 中除了聚合函數,還有另一類標量函數,它們基於輸入的值返回一個單一的值。
| 聚合函數 | 含義 | 標量函數 | 含義 |
| AVG() | 返回平均值 | UCASE() | 轉換為大寫 |
| COUNT() | 返回行數 | LCASE() | 轉換為小寫 |
| FIRST() | 返回第一個記錄 | SUBSTRING() | 截取字符串 |
| LAST() | 返回最后一個記錄 | LEN() | 返回字段長度 |
| MAX() | 返回最大值 | ROUND() | 四舍五入 |
| MIN() | 返回最小值 | NOW() | 返回系統時間 |
| SUM() | 返回總和 | FORMAT() | 格式化字符串 |
不同數據庫對函數的實現有一定差異,但常用的函數使用方式都相同:如上黑體字列出的聚合函數。
單獨使用聚合函數:
1 select avg(age) as "平均年齡" from Person;--計算所有人的平均年齡 2 select max(age) as "最大年齡",min(age) as "最小年齡" from Person;--計算最大年齡和最小年齡 3 select count(name) as "人數" from Person;--統計表中的人數 4 --count()通過指定列來統計個數,忽略 NULL 值
聚合函數 + group by:
1 select sex,sum(name) as "人數" from Person 2 group by sex 3 having sum(name)>2;--查詢性別人數之和大於的的性別和人數 4 --group by 后面為聚合(或者叫分組)的字段,查詢中用來分組的字段都必須出現在 group by 之后,having 用來對聚合后的數據再過濾
如果有多個字段需要被用來分組,那么他們的分組順序是從左至右的,並且最右邊的字段將被當做聚合函數計算的最小分組。
使用 order by 排序:
1 select * from Person order by age desc; 2 --按年齡從大到小排序查詢所有信息,desc 表示倒序。默認是asc 表示升序,可以省略。group by 可以和 order by 一起使用,但 order by 永遠在查詢語句的最后
3,連接查詢
連接查詢分為三類:內連接,外連接,全連接。
我們知道,現實世界中的事物都存在各種聯系,通過事物之間的種種聯系,你可以收集到更多在一類事物上不存在的信息。正如著名的七人理論:你最多只需要通過7個人就能和世上任何一個人認識。這也是一種典型的關系模型。
我們的關系型數據庫正是通過各種各樣的外鍵把不同表關聯起來的。只要根據他們的關系,我們就可以在不同的表中查找我們想要的任何數據了。
A:內連接
內連接的用法如下:
1 select * from tableA,tableB;--方式一 2 select * from tableA A 3 join tableB B 4 on 1=1;--方式二(join 是inner join 的縮寫,on 用來指定組合產生新數據連接的條件,這里1=1始終為真,意為沒有任何限制的連接兩個表)
內連接會使用兩個表中的數據產生一個笛卡爾積,簡單的說就是:數據庫把兩個表中的數據認為是多對多的關系,用表 A 的每一條數據去和表 B 中的每一條數據組合成新的數據。
最終的結果是:總記錄的條數是兩個表記錄條數的乘積,字段數是兩個表字段數的總和。
很顯然,通常情況下,笛卡爾積並不是我們需要的數據。方式一可以通過 where 來設置過濾條件,而方式二則是通過 on 指定連接條件。比如如下sta和sal兩個表:
| sta表 | id | name | sal_grade | sal表 | sal_grade | sal |
| 1 | 張三 | 3 | 1 | 8000 | ||
| 2 | 李四 | 2 | 2 | 6000 | ||
| 3 | 王五 | 1 | 3 | 4000 |
1 select * from sta,sal;--會返回 9 條記錄,每條記錄有 5 個字段 2 select * from sta,sal 3 where sta.sal_grade = sal.sal_grade;--只會返回9條記錄中sta.sal_grade = sal.sal_grade的數據,共3條 4 select * from sta join sal 5 on sta.sal_grade = sal.sal_grade;--也只返回 3 條記錄,但他們的原理是有差別的,where 是在產生笛卡爾積后過濾,而 join 方式是在確定連接關系時就開始過濾,最終不會產生笛卡爾積,除非 on 指定的條件為真 6 select sta.id,sta.name,sal.sal from sta join sal 7 on sta.sal_grande = sal.sal_grande;--只查詢需要的字段,而不是兩個表中所有的字段
B:外連接
外連接又分為左外連接和右外連接,左外連接會返回所有左表中的記錄,無論是否滿足連接條件,而右外連接剛好相反,會返回右表中所有的記錄,無論是否滿足連接條件。這里的左和右是指 join 關鍵字的左和右。
外連接最終的輸出結果表現為:總記錄數不能確定,因為可能存在一對多的關系。而字段數仍為兩個表字段數之和。那些滿足連接條件的記錄,每個字段都會有確定的值,而那些不滿足連接條件的記錄,則只有左表或右表的字段有值,另一個表的字段為 NULL(具體取決於是左外連接還是右外連接)。
1 insert into sta values(4,'張五',5); 2 select sta.id,sta.name,sal.sal from sta 3 left join sal 4 on sta.sal_grande = sal.sal_grande;--返回 4 條記錄,但張五的工資字段為 NULL,因為 sal 表中並沒有 sal_grande 為 5 的記錄可以匹配
C:全連接和交叉連接
全連接使用 full join,結果集的數量不確定,字段數是兩個表字段數的總和,但是,依然會返回左右表中不滿足連接條件的記錄,只是另一邊表的字段則均為 NULL。
交叉連接使用 cross join,產生的結果仍是笛卡爾積,等價於內連接。
1 insert into sal values(4,2000); 2 select * from sta 3 full join sal 4 on sta.sal_grande = sal.sal_grande;--返回 5 條記錄,但name為張五的記錄中sal表的字段值均為 NULL,工資等級為 4 的記錄中 sta 表的字段均值為 NULL
4,聯合查詢
連接查詢是把表的字段橫向組合到一起,從而產生新的數據,而聯合查詢是把兩個表的記錄縱向組合到一起,聯合查詢的要求是:兩個表的字段數量和對應字段的數據類型必須相同。
A:union和union all
SQL 通過 union 聯合兩張表,返回兩張表中不同記錄的組合。
1 select col1,col2,col3 from tableA 2 union 3 select col1,col2,col3 from tableB
請注意,union 聯合表后,默認只選取不同的記錄,如果你希望在聯合后的記錄中允許相同的數據存在,請使用 union all。
B:except
SQL 通過 except 聯合兩張表,返回那些只在左表中存在的記錄。
1 select col1,col2,col3 from tableA 2 except 3 select col1,col2,col3 from tableB
C:intersect
SQL 通過 intersect 聯合兩張表,返回那些同事存在於左表和右表的記錄。
1 select col1,col2,col3 from tableA 2 intersect 3 select col1,col2,col3 from tableB
