入門心法:要練此功,先廢其功。(先忘記已學(xué)的其他語言,用T-SQL來思考。)
所需代碼:https://github.com/956159241/Microsoft-SQL-Server-2008-T-SQL-Fundamentals
目錄
第一章 T-SQL查詢和編程基礎(chǔ)
1.1 理論背景
1.1.1 SQL
1.1.2 集合論
1.1.3 謂詞邏輯(Predicate Logic)
1.1.4 關(guān)系模型(Relational Model)
1.1.5 數(shù)據(jù)生命周期
1.2 SQL Server 體系結(jié)構(gòu)
1.2.1 SQL Server實(shí)例
1.2.2 數(shù)據(jù)庫
1.2.3 架構(gòu)(Schema)和對(duì)象
1.3 創(chuàng)建表和定義數(shù)據(jù)完整性
1.3.1 創(chuàng)建表
1.3.2 定義數(shù)據(jù)完整性
第二章 單表查詢
2.1 SELECT 語句的元素
2.1.1 FROM 子句
2.1.2 WHERE 子句
2.1.3 GROUP BY 子句
2.1.4 HAVING 子句
2.1.5 SELECT 子句
2.1.6 ORDER BY 子句
2.1.7 TOP 選項(xiàng)
2.1.8 OVER子句
2.2 謂詞和運(yùn)算符
2.3 CASE 表達(dá)式
2.4 NULL值
2.5 同時(shí)操作(All-At-Once Operation)
2.6 處理字符數(shù)據(jù)
2.6.1 數(shù)據(jù)類型
2.6.2 排序規(guī)則(Collation)
2.6.3 運(yùn)算符和函數(shù)
2.7處理日期和時(shí)間數(shù)據(jù)
第三章 聯(lián)接查詢
3.1 交叉聯(lián)接
3.1.1 ANSI SQL-92語法
3.1.2 ANSI SQL-89語法(不支持使用)
3.1.3 自交叉聯(lián)接
3.1.4 生成數(shù)字表
3.2 內(nèi)聯(lián)接
3.3 特殊的聯(lián)接實(shí)例
3.3.1 組合聯(lián)接
3.3.2 不等聯(lián)接
3.3.3 多表聯(lián)接
3.4 外聯(lián)接
3.4.1 外聯(lián)接基礎(chǔ)
第一章 T-SQL查詢和編程基礎(chǔ)
1.1 理論背景
SQL——Structured Query Language,它是為查詢和管理關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDMS)中的數(shù)據(jù)而專門設(shè)計(jì)的一種標(biāo)準(zhǔn)語言。
??語言的獨(dú)立性——關(guān)系模型是獨(dú)立于語言的,例如C#的類模型。
1.1.1 SQL
SQL 是基于關(guān)系模型的ANSI和ISO標(biāo)準(zhǔn)語言,專門為查詢和管理RDMS中的數(shù)據(jù)而專門設(shè)計(jì)的一種標(biāo)準(zhǔn)語言。
名詞區(qū)分:
- ISO —— 國際標(biāo)準(zhǔn)化組織(International Organization for Standardization)簡(jiǎn)稱ISO,是一個(gè)全球性的非政府組織,是國際標(biāo)準(zhǔn)化領(lǐng)域中一個(gè)十分重要的組織。(.iso系統(tǒng)鏡像文件后綴)。
- IOS —— 系統(tǒng)。
- OSI —— 是Open System Interconnect的縮寫(七層)。
理解:
??SQL類似英語,告訴它想要得到什么,然后由DBMS對(duì)語言進(jìn)行處理得出結(jié)果。
1.1.2 集合論
關(guān)系模型的數(shù)學(xué)基礎(chǔ)——集合論。
??所謂“集合”是把我們直觀或思維中確定的、相互間有明確卻別的那些對(duì)象m視為一個(gè)整體M,這一整體M就稱為集合(稱m為集合M的元素)。
1.1.3 謂詞邏輯(Predicate Logic)
關(guān)系模型的數(shù)學(xué)基礎(chǔ)——謂詞邏輯。
??不嚴(yán)格的說,謂詞就是用來刻畫事物是否具有某種性質(zhì)或滿足某種表達(dá)式條件的一個(gè)詞項(xiàng)用于維護(hù)數(shù)據(jù)的邏輯完整性和定義它的結(jié)構(gòu),謂詞也可以用于對(duì)數(shù)據(jù)進(jìn)行過濾以定義其子集等多種應(yīng)用場(chǎng)合。
1.1.4 關(guān)系模型(Relational Model)
關(guān)系模型是一個(gè)用于表示數(shù)據(jù)的語義模型,其理論基礎(chǔ)是集合論和謂詞邏輯。
??關(guān)系模型的目標(biāo)是要用最少的或完全無冗余地支持完整數(shù)據(jù)的持久化表示,而且還要將數(shù)據(jù)完整性(強(qiáng)制的數(shù)據(jù)一致性)定義為模型的一部分。
關(guān)系模型幾個(gè)相關(guān)的概念:
- 命題 —— proposition
- 域 —— domain
- n元關(guān)系 —— n-ary relaiton
- n重元組 —— n-tuple
- 序偶 —— ordered pair
在關(guān)系模型中,關(guān)系在數(shù)據(jù)庫的實(shí)現(xiàn)中就表現(xiàn)為數(shù)據(jù)表。對(duì)關(guān)系進(jìn)行操作的結(jié)果得到的還是一個(gè)關(guān)系。
??一個(gè)域就是一個(gè)屬性可能的(或有效的)** 一組取值 的集合。
約束(Constraint)
??關(guān)系模型最大的優(yōu)點(diǎn)之一就是將數(shù)據(jù)完整性定義為模型的一部分,完整性是通過規(guī)則(或約束)來實(shí)施的。
約束的例子:
??提供實(shí)體完整性(entity integrity)的候選鍵(candidate key)**——指在關(guān)系中能夠防止同一元組(數(shù)據(jù)行)多次出現(xiàn)的屬性集(一個(gè)或多個(gè)屬性)。
??外鍵(foreign key)——用于實(shí)施引用完整性,外鍵是在關(guān)系(稱為引用關(guān)系,referencing relation)中的一個(gè)或多個(gè)屬性上定義,通過它來應(yīng)用另一個(gè)(或者,也可能是同一個(gè))關(guān)系中的候選鍵。
規(guī)范化(Normalization)
??關(guān)系模型同時(shí)也定義了規(guī)范化規(guī)則(也稱為范式)。規(guī)范化是一種形式化的數(shù)學(xué)處理過程,以確保每一個(gè)實(shí)體只由一個(gè)關(guān)系來表示。
第一范式(1NF)無重復(fù)的列:
??第一范式要求表中的行必須是唯一的屬性應(yīng)該是原子的(atomic)——即列不能再拆分。即一個(gè)表表示一個(gè)關(guān)系。
??數(shù)據(jù)庫表中的字段都是單一屬性的,不可再分。這個(gè)單一屬性由基本類型構(gòu)成,包括整型、實(shí)數(shù)、字符型、邏輯型、日期型等。很顯然,在當(dāng)前的任何關(guān)系數(shù)據(jù)庫管理系統(tǒng)(DBMS)中,傻瓜也不可能做出不符合第一范式的數(shù)據(jù)庫,因?yàn)檫@些DBMS不允許你把數(shù)據(jù)庫表的一列再分成二列或多列。因此,你想在現(xiàn)有的DBMS中設(shè)計(jì)出不符合第一范式的數(shù)據(jù)庫都是不可能的。
第二范式(2NF)屬性完全依賴于主鍵 [ 消除部分子函數(shù)依賴 ]:
??第二范式(2NF)是在第一范式(1NF)的基礎(chǔ)上建立起來的,第二范式包括兩條規(guī)則,首先數(shù)據(jù)必須滿足第一范式,其次要求非鍵屬性(nonkey attribute)和候選鍵屬性之間必須滿足一定的條件。非正式地說,如果要獲得任何非鍵屬性值,就必須提供同一行中某個(gè)候選鍵的所有屬性值。
??例如員工信息表中加上了員工編號(hào)(emp_id)列,因?yàn)槊總€(gè)員工的員工編號(hào)是惟一的,因此每個(gè)員工可以被惟一區(qū)分。
另外,所有單關(guān)鍵字的數(shù)據(jù)庫表都符合第二范式,因?yàn)椴豢赡艽嬖诮M合關(guān)鍵字。
第三范式(3NF)屬性不依賴于其它非主屬性 [ 消除傳遞依賴 ]:
??第三范式(3NF)要求一個(gè)數(shù)據(jù)庫表中不包含已在其它表中已包含的非主關(guān)鍵字信息。要求數(shù)據(jù)必須滿足第二范式。其次,所有非鍵屬性必須非傳遞依賴于候選鍵。所有非鍵屬性都必須互相獨(dú)立。換句話說,一個(gè)非鍵屬性不能依賴于其他非鍵屬性。
參考書本和http://www.open-open.com/lib/view/open1404791721950.html
1.1.5 數(shù)據(jù)生命周期
數(shù)據(jù)首先進(jìn)入聯(lián)機(jī)事務(wù)處理(OLTP,Online Transactional Processing )然后進(jìn)入數(shù)據(jù)倉庫(Data Warehouse)再進(jìn)入聯(lián)機(jī)分析處理(OLAP,OnLine Analytical Processing)最后進(jìn)入DM(Data mining )。
1.2 SQL Server 體系結(jié)構(gòu)
1.2.1 SQL Server實(shí)例
SQL Server實(shí)例是指安裝的一個(gè)SQL Server數(shù)據(jù)庫引擎/服務(wù)。在同一臺(tái)計(jì)算機(jī)上可以安裝多個(gè)實(shí)例(補(bǔ)充:但是需要不同的實(shí)例名)。
1.2.2 數(shù)據(jù)庫
可以將數(shù)據(jù)庫認(rèn)為是各種對(duì)象的容器,這些對(duì)象可以是表(table)、視圖(view)、存儲(chǔ)過程(stored procedure)等等。每個(gè)實(shí)例包含多個(gè)數(shù)據(jù)庫。數(shù)據(jù)庫在物理上由數(shù)據(jù)文件和事物日志文件組成。
1.2.3 架構(gòu)(Schema)和對(duì)象
一個(gè)數(shù)據(jù)庫包含多個(gè)架構(gòu),而每個(gè)架構(gòu)則又包含多個(gè)對(duì)象。
實(shí)例 包含 數(shù)據(jù)庫 包含 架構(gòu) 包含 多個(gè)對(duì)象
1.3 創(chuàng)建表和定義數(shù)據(jù)完整性
使用的架構(gòu)名為dbo,數(shù)據(jù)庫名testdb
創(chuàng)建數(shù)據(jù)庫語句:
IF DB_ID('testdb') IS NULL
CREATE DATABASE testdb;
執(zhí)行之后刷新也不出現(xiàn)數(shù)據(jù)庫名,然后我重啟了下SQL Server就出現(xiàn)了……
??DB_ID函數(shù)接受一個(gè)數(shù)據(jù)庫名稱作為輸入,返回它的內(nèi)部數(shù)據(jù)庫ID
1.3.1 創(chuàng)建表
創(chuàng)建表語句:
--創(chuàng)建表,表名:Employees(empid:雇員ID mgrid:經(jīng)理ID ssn:社會(huì)保險(xiǎn)號(hào)(social securuty number))
USE testdb;
IF OBJECT_ID('dbo.Employees','U') IS NOT NULL
DROP TABLE dbo.Employees;
CREATE TABLE dbo.Employees
(
empid INT NOT NULL,
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
hiredate DATE NOT NULL,
mgrid INT NULL,
ssn VARCHAR(20) NOT NULL,
salary MONEY NOT NULL
);
這里,類型‘U’代表用戶表。
1.3.2 定義數(shù)據(jù)完整性
作為模型的一部分而實(shí)施的數(shù)據(jù)完整性(也就是說,作為定義的一部分)稱為聲明式(declarative)數(shù)據(jù)完整性。用代碼來實(shí)施的數(shù)據(jù)完整性(例如,用存儲(chǔ)過程或觸發(fā)器)稱為過程式(procedural)數(shù)據(jù)完整性。
??本節(jié)主要介紹聲明式約束的一些例子,包括主鍵、唯一約束(UNIQUE)、外鍵、檢查約束(CHECK)以及DEFAULT約束。
在添加約束之前,先了解一下如何解除約束:
alter table 表名 drop contrainst 約束鍵名
主鍵約束(Primary Key Constraints)
??每個(gè)表只能定義一個(gè)主鍵。
添加主鍵約束:
--為表添加主鍵約束Primary Key Constraints
ALTER TABLE dbo.Employees
ADD CONSTRAINT PK_Employees
PRIMARY KEY(empid);
唯一約束(Unique Constraints)
--為表添加唯一約束Unique Constraints 添加在ssn列上
ALTER TABLE dbo.Employees
ADD CONSTRAINT UNQ_Employees_ssn
UNIQUE(ssn);
這地地方在管理器上貌似并不能直觀的看到,或許只有插入數(shù)據(jù)的時(shí)候才能知道吧,試試看:
外鍵約束
??先創(chuàng)建一個(gè)Orders表,然后設(shè)置自己的empid為外鍵指向被引用表(referenced table)中的一組候選鍵(主鍵或唯一約束)。注意:引用表和被引用表可能是同一個(gè)表。
--添加一個(gè)Orders表
IF OBJECT_ID('dbo.Orders','U') IS NOT NULL
DROP TABLE dbo.Orders;
CREATE TABLE dbo.Orders
(
orderid INT NOT NULL,
empid INT NOT NULL,
custid VARCHAR(10) NOT NULL,
orderts DATETIME NOT NULL,
qty INT NOT NULL,
CONSTRAINT PK_Orders
PRIMARY KEY(orderid)
);
--添加外鍵約束
ALTER TABLE dbo.Orders
ADD CONSTRAINT FK_Orders_Employees
FOREIGN KEY(empid)
REFERENCES dbo.Employees(empid);
--自己給自己添加外鍵約束
ALTER TABLE dbo.Employees
ADD CONSTRAINT FK_Employees_Employees
FOREIGN KEY(mgrid)
REFERENCES Employees(empid);
檢查約束(Check)
??檢查約束用于定義在表中輸入或修改一行數(shù)據(jù)之前必須滿足的一個(gè)謂詞。
--添加檢查約束(check)
ALTER TABLE dbo.Employees
ADD CONSTRAINT CHK_Employees_salary
CHECK(salary > 0);
默認(rèn)約束(Default)
--默認(rèn)設(shè)置,由它返回當(dāng)前的日期和時(shí)間值
ALTER TABLE dbo.Orders
ADD CONSTRAINT DFT_Orders_orderts
DEFAULT(CURRENT_TIMESTAMP) FOR orderts;
第二章 單表查詢
2.1 SELECT 語句的元素
SELECT 語句的目的是對(duì)表進(jìn)行查詢、應(yīng)用一定的邏輯處理,并返回結(jié)果。
??Microsoft SQL Server引擎并不教條地嚴(yán)格遵守邏輯查詢處理;相反,在物理地處理一個(gè)查詢時(shí),它可以自由地調(diào)整處理階段的順序,只要最終的結(jié)果能夠和邏輯查詢處理的規(guī)定保持一致。SQL SERVER 可以(事實(shí)上經(jīng)常)在查詢的物理處理中采用很多快捷方式。
創(chuàng)建一條查詢語句:
--開始用一個(gè)USE語句來設(shè)置會(huì)話(session)的數(shù)據(jù)庫上下文,如果會(huì)話已經(jīng)位于需要查詢的數(shù)據(jù)庫上下文中,則
--不需要再使用USE語句
USE InsideTSQL2008;
SELECT empid,YEAR(orderdate) AS orderyear,COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*) > 1
ORDER BY empid,orderyear;
解釋(從select開始):
- 1.從Sales.Orders表中查詢數(shù)據(jù)行
- 2.對(duì)訂單數(shù)據(jù)進(jìn)行過濾,值保留客戶ID等于71的記錄
- 3.按雇員ID和訂單年份對(duì)訂單數(shù)據(jù)進(jìn)行分組
- 4.對(duì)分組數(shù)據(jù)(雇員ID和訂單年份)進(jìn)行過濾,只保留具有多個(gè)訂單的分組
- 5.選擇(返回)每個(gè)分組的雇員ID、訂單年份,以及訂單數(shù)量。
- 6.按照雇員ID和訂單年份對(duì)輸出結(jié)果進(jìn)行排序。
在邏輯上,應(yīng)該是按以下順序來處理它的各個(gè)子句:
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*) > 1
SELECT empid,YEAR(orderdate) AS orderyear,COUNT(*) AS numorders
ORDER BY empid,orderyear;
我們看看C#里提供的LINQ語法:
//創(chuàng)建查詢
var query = from CustomerInfo ci in customers
where ci.Age >= 80
select ci;
2.1.1 FROM 子句
1.如何修改架構(gòu)名
??首先確定該架構(gòu)已經(jīng)存在。
架構(gòu)名更改方式:
批量修改:
EXEC sp_MSforeachtable 'exec sp_changeobjectowner ''?'',''dbo'' '
單個(gè)修改:
EXEC sp_changeobjectowner '要改的表名','dbo'
如果想修改存儲(chǔ)過程Owner:
先把--and xtype='p' 反注釋,再創(chuàng)建sp_changeobjectowner 存儲(chǔ)過程。
然后執(zhí)行:
EXEC sp_changeobjectowner 'CurrentOwner','dbo'
2.創(chuàng)建一個(gè)簡(jiǎn)單的查詢
--簡(jiǎn)單的查詢
SELECT orderid,custid,empid,orderdate,freight
FROM Sales.Orders;
查詢結(jié)果:
??看起來似乎是以特定的順序返回(以orderid的升序排列)的,但不能保證絕對(duì)這樣。
如果名稱中嵌入了空格或其他特殊字符,就必須分隔這樣的標(biāo)識(shí)符。
- ANSI SQL標(biāo)準(zhǔn):“Order Details”
- SQL Server的特殊格式:[Order Details]
2.1.2 WHERE 子句
在WHERE子句中,可以指定一個(gè)謂詞或邏輯表達(dá)式,從而過濾由FROM階段返回的行。
練習(xí):
--WHERE 子句練習(xí)
SELECT orderid,empid,orderdate,freight
FROM Sales.Orders
WHERE custid = 71;
要時(shí)刻記住T-SQL使用的是三值謂詞邏輯,所以邏輯表達(dá)式的結(jié)果可以為TRUE、FALSE、或者UNKNOW。
2.1.3 GROUP BY 子句
GROUP BY 階段可以將前面邏輯謂詞處理階段返回的行按“組”進(jìn)行組合。
練習(xí):
--GROUP BY 子句練習(xí)
SELECT empid,YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate);
因?yàn)榫酆虾瘮?shù)只為每個(gè)組返回一個(gè)值,所以一個(gè)元素如果不在GROUP BY列表中出現(xiàn),就只能作為聚合函數(shù)(COUNT、SUM、AVG、MIN、MAX)的輸入。
例如:
正確的操作:
--GROUP BY 的正確操作
SELECT
empid,
YEAR(orderdate) AS orderyear,
SUM(freight) AS totalfreight,
COUNT (*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate);
表達(dá)式SUM(freight)返回每組中所有運(yùn)費(fèi)值的總和,而函數(shù)COUNT(*)則返回每組中行的個(gè)數(shù)。
所有的聚合函數(shù)都會(huì)忽略NULL,只有一個(gè)例外——COUNT(*)。
如果只想處理不重復(fù)的已知值,可以在聚合函數(shù)的圓括號(hào)中指定DISTINCT關(guān)鍵字。
例如:
--DISTINCT的使用
SELECT
empid,
YEAR(orderdate) AS numcusts,
COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY empid,YEAR(orderdate)
2.1.4 HAVING 子句
HAVING 子句用于指定對(duì)組進(jìn)行過濾的謂詞或邏輯表達(dá)式,這與WHERE階段對(duì)單獨(dú)的行進(jìn)行過濾相對(duì)應(yīng)。
??因?yàn)镠AVING子句是對(duì)行進(jìn)行分組后處理的,所以可以在邏輯表達(dá)式中引用聚合函數(shù)。
--HAVING 子句練習(xí)
SELECT
empid,
YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*) > 1
2.1.5 SELECT 子句
T-SQL允許查詢返回沒有名稱的結(jié)果集列,但關(guān)系模型不允許這樣。
注意:選擇Sales.Orders表的orderid和orderdate列,結(jié)果不小心,忘記了在兩個(gè)列名之間加一個(gè)逗號(hào),如下:
select orderid orderdate
from Sales.Orders;
這一查詢?cè)谡Z法上是有效的,意思是說你想將orderid列的別名定義為orderdate,得到輸出只有一列包含訂單ID的列,其別名為orderdate。要查出這樣的bug可能是很難的
??SELECT子句是在FROM、WHERE、GROUP BY以及HAVING子句后處理的。這意味著對(duì)于SELECT子句之前處理的那些子句,在SELECT子句中為表達(dá)式分配的別名并不存在。
??為了確保select語句執(zhí)行的結(jié)果中行的唯一性,SQL提供的方法就是使用DISDINCT子句來刪除重復(fù)的行。
2.1.6 ORDER BY 子句
ORDER BY 子句用于展示數(shù)據(jù)時(shí)對(duì)輸出結(jié)果中的行進(jìn)行排序。從邏輯查詢處理來看,ORDER BY是最后處理的一個(gè)子句。
--ORDER BY 子句練習(xí)
SELECT empid,YEAR(orderdate) AS orderyear,COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*) > 1
ORDER BY empid,orderyear;
理解SQL最重要的一點(diǎn)就是要明白表不保證是有序的,因?yàn)楸硎緸榱舜硪粋€(gè)集合(如果有重復(fù)項(xiàng),則是多集),而集合是無序的。
??事實(shí)上,ORDER BY 是唯一能夠引用SELECT 處理階段創(chuàng)建的別名列的階段,因?yàn)樗俏ㄒ灰粋€(gè)在SELECT階段之后被處理的階段。
T-SQL支持在ORDER BY子句中指定沒有在SELECT子句中出現(xiàn)過得元素,也就是說,排序依據(jù)的列并不一定必須要在輸出返回的列中選取。
??但是,當(dāng)指定了DISTINCT以后,ORDER BY 子句就被限制為只能選取在SELECT列表中出現(xiàn)的那些元素。
2.1.7 TOP 選項(xiàng)
TOP選項(xiàng)是T-SQL特有的,用于限制查詢返回的行數(shù)或百分比。
--Top選項(xiàng)練習(xí)
SELECT TOP(5) orderid,orderdate,custid,empid
FROM Sales.Orders
ORDER BY orderdate DESC;
注意:當(dāng)在查詢中指定了TOP以后,ORDER BY子句就會(huì)起到雙重作用。首先,作為select處理階段一部分的top選項(xiàng)要依靠order by子句先為各個(gè)行定義他們的邏輯優(yōu)先順序,在這種優(yōu)先順序的基礎(chǔ)上再去過濾其他請(qǐng)求。其次,作為select處理階段之后的order by階段,與為了展示數(shù)據(jù)而對(duì)行進(jìn)行排序的order by子句完全一樣。
個(gè)人理解:
由于表是無序的,所以前 n 條是不確定的,用order by 先根據(jù)某一列進(jìn)行排序,然后 top n 選出 n 行,最后order by再執(zhí)行一次功能,展示數(shù)據(jù)而對(duì)行進(jìn)行排序。
在TOP選項(xiàng)中可以使用PERCENT關(guān)鍵字。例如:TOP (1)PERCENT----1% 。
再來看看一個(gè)功能:WITH TIES
注意:即便指定了TOP(5),輸出的還是包含了8行。SQL SERVER 先按照orderdate desc的順序,返回top(5)行;再從表中返回orderdate值和已經(jīng)訪問過得前5行中的最后一行相同的其他所有行。
2.1.8 OVER子句
在group by子句中,在對(duì)數(shù)據(jù)進(jìn)行分組以后,查詢?yōu)槊總€(gè)組只返回一行;因此,也就要限制所有的表達(dá)式為每個(gè)組只能返回一個(gè)值。
??聚合開窗函數(shù)使用OVER子句提供窗口作為上下文,對(duì)窗口中的一組值進(jìn)行操作,而不是使用group by子句提供上下文。
??注意:只有在select和order by處理階段才允許使用over子句。
例如:如果在對(duì)OrderValues視圖進(jìn)行查詢的SELECT子句中指定了SUM(val) OVER表達(dá)式,這個(gè)函數(shù)就會(huì)對(duì)SELECT階段操作的所有行計(jì)算其總價(jià)格。
??如果想對(duì)行限制或分區(qū),則可以使用PARTITION BY子句。
OVER 子句的一個(gè)優(yōu)點(diǎn)就是能夠在返回基本列的同時(shí),在同一行對(duì)它們進(jìn)行聚合,也可以在表達(dá)式中混合使用基本列和聚合值列。例如,以下查詢?yōu)镺rderValues的每一行計(jì)算當(dāng)前價(jià)格占總價(jià)格的百分比,以及當(dāng)前價(jià)格占客戶總價(jià)格的百分比:
--over()練習(xí),所占百分比
SELECT orderid,custid,val,
100.*val / SUM(val) OVER() AS pctall,
100.*val / SUM(val) OVER(PARTITION BY custid) AS pctcust
FROM Sales.OrderValues;
注意:在表達(dá)式中使用的是十進(jìn)制數(shù)100.(100后面加個(gè)點(diǎn)),而不是直接使用證書100,因?yàn)檫@樣可以隱式地將整數(shù)值val和SUM(val)轉(zhuǎn)換成十進(jìn)制實(shí)數(shù)值。否則,表達(dá)式的除法將是“整數(shù)除法”,會(huì)截取小數(shù)部分。
OVER 子句也支持四種排名函數(shù):ROW_NUMBER(行號(hào))、RANK(排名)、DENSE_RANK(密集排名)以及NTILE。
再看一個(gè)在內(nèi)部為子集分配行號(hào):
2.2 謂詞和運(yùn)算符
T-SQL 有幾種不同的語言元素可以指定邏輯表達(dá)式,例如,查詢過濾器(WHERE 和 HAVING)、CHECK約束,等等。在邏輯表達(dá)式中可以使用各種謂詞,T-SQL支持的謂詞包括IN、BETWEEN以及LIKE等。
擴(kuò)展:
- 執(zhí)行where子句查找符合條件的數(shù)據(jù);
- 使用group by 子句對(duì)數(shù)據(jù)進(jìn)行分組;對(duì)group by 子句形成的組運(yùn)行聚集函數(shù)計(jì)算每一組的值;最后having 子句去掉不符合條件的組。
- having 子句中的每一個(gè)元素也必須出現(xiàn)在select列表中。有些數(shù)據(jù)庫例外,如oracle.
- having子句和where子句都可以用來設(shè)定限制條件以使查詢結(jié)果滿足一定的條件限制。
- having子句限制的是組,而不是行。where子句中不能使用聚集函數(shù),而having子句中可以。
IN這個(gè)謂詞用于檢查一個(gè)值(或標(biāo)量表達(dá)式)是否與一組元素中的至少一個(gè)相等。
--IN謂詞
SELECT orderid,empid,orderdate
FROM Sales.Orders
WHERE orderid IN (10248,10249,10250);
BETWEEN 這個(gè)謂詞用于檢查一個(gè)值是否在指定的范圍內(nèi),包括兩個(gè)指定的邊界值。
--BETWEEN謂詞
SELECT empid,firstname,lastname
FROM HR.Employees
WHERE lastname LIKE N'%D%';
LIKE這個(gè)謂詞用于檢查一個(gè)字符串值是否與指定的模式匹配(如上代碼)
??在'%D%'前加N,代表國際化(National)用于表示字符串是Unicode數(shù)據(jù)類型(NCHAR或NVARCHAR)
運(yùn)算符
??由上兩圖很明顯看出AND的優(yōu)先級(jí)高于OR。
2.3 CASE 表達(dá)式
CASE表達(dá)式是一個(gè)標(biāo)量表達(dá)式,它基于條件邏輯來返回一個(gè)值。
CASE表達(dá)式有兩種格式:簡(jiǎn)單表達(dá)式和所搜表達(dá)式。CASE簡(jiǎn)單格式將一個(gè)值(或一個(gè)標(biāo)量表達(dá)式)與一組可能的取值進(jìn)行比較,并返回第一個(gè)匹配的結(jié)果。
例一:
--CASE表達(dá)式
SELECT productid,productname,categoryid,
CASE categoryid
WHEN 1 THEN '1Bevagrages'
WHEN 2 THEN '2COmmo'
WHEN 3 THEN '3HHHH'
WHEN 4 THEN '4Bagrages'
WHEN 5 THEN '5Bevagages'
WHEN 6 THEN '6agrages'
WHEN 7 THEN '7grages'
WHEN 8 THEN '8ages'
ELSE 'unkown'
END AS categoryname
FROM Production.Products;
例二:
--CASE表達(dá)式2
SELECT orderid,custid,val,
CASE NTILE(3) OVER(ORDER BY val)
WHEN 1 THEN 'LOW'
WHEN 2 THEN 'Medium'
WHEN 3 THEN 'High'
END AS titledesc
FROM Sales.OrderValues
ORDER BY val;
NTILE()函數(shù)把記錄結(jié)果集分成N部分
NTILE(3)將查詢結(jié)果分成三部分。
CASE搜索表達(dá)式返回結(jié)果為TRUE的第一個(gè)WHEN邏輯表達(dá)式所關(guān)聯(lián)的THEN子句中指定的值:
SELECT orderid,custid,val,
CASE
WHEN val < 1000.00 THEN 'Less then 1000'
WHEN val BETWEEN 1000.00 AND 3000.00 THEN 'Between 1000 and 3000'
WHEN val > 3000.00 THEN 'More Than 3000'
ELSE 'unknown'
END AS valuecategory
FROM Sales.OrderValues;
2.4 NULL值
SQL支持用NULL符號(hào)來表示缺少的值,它使用的三值謂詞邏輯。這意味著謂詞的計(jì)算結(jié)果可以是TRUE、FALSE或UNKNOWN。
??UNKNOWN的一個(gè)微妙之處是當(dāng)對(duì)它取反(negate)時(shí),結(jié)果仍然是UNKNOWN。
??對(duì)兩個(gè)NULL值進(jìn)行比較的表達(dá)式(NULL = NULL),其計(jì)算結(jié)果竟然也為UNKNOWN。
??查詢過濾條件“接受TRUE”意味著它既會(huì)拒絕讓邏輯表達(dá)式計(jì)算結(jié)果為FALSE的行,也會(huì)拒絕讓表達(dá)式計(jì)算結(jié)果為UNKNOWN的那些行。對(duì)于FALSE也是如此。如若需要查詢NULL值:
--查詢NULL值
SELECT custid,country,region,city
FROM Sales.Customers
WHERE region IS NULL
??在用于比較和排序目的的不同語言元素中,SQL處理NULL的方式也有所不同。一些元素認(rèn)為兩個(gè)NULL值彼此相等,而另一些則認(rèn)為它們不相等。
??當(dāng)進(jìn)行分組和排序時(shí),認(rèn)為兩個(gè)NULL值是相等的。也就是說,GROUP BY子句會(huì)在每個(gè)組中重新組織所有的NULL值就像有具體值得列一樣。
??ORDER BY子句也會(huì)對(duì)所有的NULL值進(jìn)行排序。
??ANSI SQL 有兩種UNIQUE約束:一種將多個(gè)NULL值視為相等的(只允許有一個(gè)NULL值),另一種則將多個(gè)NULL值視為不同的(允許有多個(gè)NULL值)。SQL Server只實(shí)現(xiàn)了前者。
2.5 同時(shí)操作(All-At-Once Operation)
SQL支持一種所謂的同時(shí)操作的概念,其含義是認(rèn)為在同一邏輯查詢處理階段中出現(xiàn)的所有表達(dá)式都是同時(shí)進(jìn)行計(jì)算的。
??從邏輯上來說,SELECT列表中各表達(dá)式的計(jì)算是沒有順序的——它們只是一組表達(dá)式。在邏輯上SELECT列表中的所有表達(dá)式都是在同一時(shí)刻進(jìn)行計(jì)算的。
SELECT col1,col2
FROM dbo.T1
WHERE col1<>0 AND col2/col1>2;
如果表達(dá)式col1<>0的結(jié)果為FALSE,SQL Server將會(huì)按照“短路(short-circuit)求值”的原則,停止計(jì)算這個(gè)表達(dá)式。
??在ANSI SQL 中有“同時(shí)操作的這么個(gè)概念,所以SQL Server可以按它喜歡的任意順序來自由地處理WHERE子句的表達(dá)式。
2.6 處理字符數(shù)據(jù)
2.6.1 數(shù)據(jù)類型
SQL Server支持兩種字符數(shù)據(jù)類型——普通字符和Unicode字符。普通字符數(shù)據(jù)類型包括CHAR和VARCHAR,Unicode字符數(shù)據(jù)類型包括NCHAR和NVARCHAR。
2.6.2 排序規(guī)則(Collation)
排序規(guī)則是字符數(shù)據(jù)的一個(gè)屬性,封裝了幾個(gè)方面的特征,包括多語言支持(和Unicode類型有關(guān),因?yàn)樗С炙姓Z言)、排序規(guī)則、區(qū)分大小寫、區(qū)分重音,等等。
此時(shí),大小寫并不匹配。
--排序規(guī)則,區(qū)分大小寫
SELECT empid,firstname,lastname
FROM HR.Employees
WHERE lastname COLLATE Latin1_General_CS_AS = N'davis';
當(dāng)區(qū)分大小寫時(shí),沒有匹配的數(shù)據(jù),故沒有數(shù)據(jù)被查出來。
2.6.3 運(yùn)算符和函數(shù)
注意:
使用+運(yùn)算符:
--運(yùn)算符和函數(shù)
SELECT empid,firstname + N' ' + lastname AS fullname
FROM HR.Employees;
通過將一個(gè)名為CONCAT_NULL_YIELDS_NULL的會(huì)話選項(xiàng)設(shè)置為OFF,就可以改變SQL Server處理串聯(lián)的方式。這時(shí),SQL Server將把NULL值作為空字符串來進(jìn)行串聯(lián)。
SET CONCAT_NULL_YIELDS_NULL OFF;
常用函數(shù)
2.6.3.1 SUBSTRING函數(shù)
SUBSTRING函數(shù)用于從字符串中提取子串。
語法:
SUBSTRING(string,start,length)
2.6.3.2 LEFT 和 RIGHT函數(shù)
LEFT和RIGHT函數(shù)是SUBSTRING函數(shù)的簡(jiǎn)略形式,它們分別返回輸入字符串中從左邊或右邊開始制定個(gè)數(shù)的字符。
LEFT(string,n),RIGHT(string,n)
2.6.3.3 LEN和DATALENGTH函數(shù)
LEN函數(shù)返回輸入字符串中的字符數(shù)。
語法:
LEN(string);
普通字符字節(jié)數(shù)與字符數(shù)是相同的;而對(duì)于Unicode字符,每個(gè)字符需要兩個(gè)字節(jié)的存儲(chǔ)空間,因此,字符串的字符數(shù)是字節(jié)數(shù)的一半。如果要得到字節(jié)數(shù),則應(yīng)該使用DATALENGTH函數(shù);
??LEN和DATALENGTH函數(shù)的另一個(gè)區(qū)別是:前者不包含尾隨空格,而后者包含尾隨空格。
2.6.3.4 CHARINDEX函數(shù)
CHARINDEX函數(shù)返回字符串中某個(gè)子串第一次出現(xiàn)的起始位置。
語法:
CHARINDEX(substring,string[,start_pos])
例子:
SELECT CHARINDEX(' ','IT BEN');
2.6.3.5 PATINDEX函數(shù)
PATINDEX函數(shù)返回字符串中某個(gè)模式第一次出現(xiàn)的起始位置。
語法:
PATINDEX(patten,string)
例子:
SELECT PATINDEX('%[0-9]%','afd123afadsf')
2.6.3.6 REPLACE函數(shù)
REPLACE 函數(shù)將字符串中出現(xiàn)的所有某個(gè)子串替換為另一個(gè)子串。
語法:
REPLACE(string,substring1,substring2)
2.6.3.7 REPLICATE函數(shù)
REPLICATE函數(shù)以指定的次數(shù)復(fù)制字符串值。
語法:
REPLICATE(string,n)
2.6.3.8 STUFF函數(shù)
STUFF函數(shù)可以刪除字符串中的一個(gè)子串,再插入一個(gè)新的字符串作為替換。
語法:
STUFF(string,pos,delete_length,insertstring)
2.6.3.9 UPPER和LOWER函數(shù)
UPPER和LOWER函數(shù)將輸入字符串中的所有字符都轉(zhuǎn)換為大寫或小寫字符。
語法:
UPPER(string),LOWER(string)
2.6.3.10 RTRIM和LTRIM函數(shù)
RTRIM和LTRIM函數(shù)用于刪除輸入字符串中的尾隨空格或前導(dǎo)空格。
語法:
RTRIM(string),LTRIM(string)
2.7 處理日期和時(shí)間數(shù)據(jù)
2.7.1 日期和時(shí)間數(shù)據(jù)類型
具體使用方式可參照:https://msdn.microsoft.com/zh-cn/library/ms186724(v=sql.120).aspx
第三章 聯(lián)接查詢
3.1 交叉聯(lián)接
在邏輯上,交叉聯(lián)接是一種最簡(jiǎn)單的聯(lián)接。交叉聯(lián)接只實(shí)現(xiàn)一個(gè)邏輯查詢步驟(笛卡爾積)。
例如:
3.1.1 ANSI SQL-92語法
SELECT C.custid,E.empid
FROM Sales.Customers AS C
CROSS JOIN HR.Employees AS E;
執(zhí)行結(jié)果:
3.1.2 ANSI SQL-89語法(不支持使用)
跟上面的代碼相似,就是把Cross Join改成了逗號(hào)聯(lián)接。結(jié)果是一樣的。
3.1.3 自交叉聯(lián)接
對(duì)同一個(gè)表的多個(gè)實(shí)例也可以進(jìn)行聯(lián)接,這種功能就是所謂的自聯(lián)接(self-join),所有基本聯(lián)接類型(交叉聯(lián)接、內(nèi)聯(lián)接,以及外聯(lián)接)都支持自聯(lián)接。
--3.1.3 自交叉聯(lián)接
SELECT
E1.empid,E1.firstname,E1.lastname,
E2.empid,E2.firstname,E2.lastname
FROM HR.Employees AS E1
CROSS JOIN HR.Employees AS E2;
執(zhí)行結(jié)果:
3.1.4 生成數(shù)字表
--3.1.4 生成數(shù)字表,先創(chuàng)建一個(gè)數(shù)據(jù)庫TestDB,然后創(chuàng)建一張表
USE tempdb;
IF OBJECT_ID('dbo.Digits','U') IS NOT NULL DROP TABLE dbo.Digits;
CREATE TABLE dbo.Digits(digit INT NOT NULL PRIMARY KEY);
INSERT INTO dbo.Digits(digit)
VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
--顯示tempdb里的Digits
SELECT * FROM Digits;
相比于以前的插入這種數(shù)字表就要簡(jiǎn)單的多……
INSERT INTO dbo.Digits(digit) VALUES(0);
INSERT INTO dbo.Digits(digit) VALUES(1);
INSERT INTO dbo.Digits(digit) VALUES(2);
INSERT INTO dbo.Digits(digit) VALUES(3);
……
3.2 內(nèi)聯(lián)接
兩個(gè)輸入表進(jìn)行笛卡爾積運(yùn)算,然后根據(jù)用戶指定的謂詞對(duì)結(jié)果進(jìn)行過濾。
注意:
INNER JOIN 等價(jià)于 JOIN
你可以理解為
JOIN 是 INNER JOIN 的縮寫。
LEFT JOIN 等價(jià)于 LEFT OUTER JOIN
RIGHT JOIN 等價(jià)于 RIGHT OUTER JOIN
因?yàn)锳NSI SQL-92語法相對(duì)更加安全,且值得推薦使用的語法,故此不再介紹——89語法。
USE InsideTSQL2008;
SELECT E.empid,E.firstname,E.lastname,O.orderid
FROM HR.Employees AS E
JOIN Sales.Orders AS O
ON E.empid = O.empid;
3.3 特殊的聯(lián)接實(shí)例
3.3.1 組合聯(lián)接
???組合查詢跟內(nèi)聯(lián)接相似,不同之處是加了多條件。
ON
AND
WHERE
3.3.2 不等聯(lián)接
SELECT E1.firstname,E1.lastname,E2.firstname,E2.lastname
FROM HR.Employees AS E1
JOIN HR.Employees AS E2
ON E1.empid < E2.empid;
3.3.3 多表聯(lián)接
SELECT C.custid,C.companyname,O.orderid,OD.productid,OD.qty
FROM Sales.Customers AS C
JOIN Sales.Orders AS O
ON C.custid = O.custid
JOIN Sales.OrderDetails AS OD
ON O.orderid = OD.orderid;
關(guān)于on 與where的使用,個(gè)人理解,on的一次作用在兩張表,where的一次作用在一張表。
百度理解:
1、 on條件是在生成臨時(shí)表時(shí)使用的條件,它不管on中的條件是否為真,都會(huì)返回左邊表中的記錄。
2、where條件是在臨時(shí)表生成好后,再對(duì)臨時(shí)表進(jìn)行過濾的條件。這時(shí)已經(jīng)沒有l(wèi)eft join的含義(必須返回左邊表的記錄)了,條件不為真的就全部過濾掉。
3.4 外聯(lián)接
??外聯(lián)接會(huì)應(yīng)用內(nèi)聯(lián)接所應(yīng)用的兩個(gè)邏輯處理步驟(笛卡爾積和ON過濾),此外還多加一個(gè)外聯(lián)接特有的第三步:添加外部行。
??外聯(lián)接的第三個(gè)邏輯查詢處理步驟就是要識(shí)別保留表中按照ON條件在另一個(gè)表找不到與之匹配的那些行,再把這些行添加到聯(lián)接的前兩個(gè)步驟生成的結(jié)果表中;對(duì)于來自聯(lián)接的非保留表的那些列,追加的外部行中的這些列用NULL作為占字符。
3.4.1 外聯(lián)接基礎(chǔ)
SELECT C.custid,C.companyname
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid
WHERE O.custid IS NULL;