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