2017/3/14
RDBMS:關系型數(shù)據(jù)庫管理系統(tǒng)
關系模型獨立于語言
SQL有幾種不同類型的語言:數(shù)據(jù)定義語言(DDL)、數(shù)據(jù)處理語言(DML)、數(shù)據(jù)控制語言(DCL)
DDL:用于處理數(shù)據(jù)對象的定義,包括的語句有CREATE、ALTER、DROP
DML:用于查詢和修改數(shù)據(jù),包括的語句有SELECT、INSERT、UPDATE、DELTE、MERGE
DCL:用于處理權限管理,包括的語句有GRANT、REVOKE
關系模型基于的兩個分支:集合論和謂詞邏輯
集合:一組對象、實體、
謂詞:在關系模型中,謂詞用于維護數(shù)據(jù)的邏輯完整性和定義它的結構;對數(shù)據(jù)進行過濾以定義其子集;在結合論中可以用謂詞來定義集合
example:集合{0,1,2,3,4,5,6,7,8,9}可以定義為以下謂詞為true的所有元素的集合==>"X是一個大于等于0,而小于等于9的整數(shù)"
關系模型的定義(Relational Model):表示數(shù)據(jù)的語義模型,理論基礎是集合論和謂詞邏輯
關系模型的目標:要用最少或者完全無冗余的支持完整數(shù)據(jù)的持久化表示,而且還要將數(shù)據(jù)完整性定義為數(shù)據(jù)的一部分
什么是關系(Relation)?
在集合論中關系是集合的一種表示,在關系模型中關系是相關信息的一個集合,在數(shù)據(jù)庫的實現(xiàn)中就表現(xiàn)為(數(shù)據(jù)表)
關系模型的關鍵要點:一個關系代表一個集合ps:對關系進行操作的結果得到的還是一個關系
數(shù)據(jù)庫中設計數(shù)據(jù)模型時,所有數(shù)據(jù)都是用關系來表示的
域或類型是最基礎的關系型構建模塊之一,一個域就是一個屬性可能的(有效的)一組取值的集合
域是一種數(shù)據(jù)庫中形式最簡單的謂詞,因為它限制屬性允許的取值
關系模型最大的優(yōu)點之一就是將數(shù)據(jù)完整性定義為模型的一部分,完整性是通過規(guī)則(或約束來實施的)
域完整性(Domain Integrity),約束的其他例子還包括提供實體完整性(entity Integrity)的候選鍵,以及提供引用完整性的外鍵(Referential Integrity)
候選鍵(candidate key):是指在關系中能夠防止同一元組(數(shù)據(jù)行)多次出現(xiàn)的屬性集(一個或多個屬性),基于候選鍵的謂詞能夠唯一標識一行數(shù)據(jù)。在關系中可以定義多個候選鍵,可以任意選擇一個候選鍵作為主鍵(Primary Key),換言之主鍵一定要在候選鍵中選擇,主鍵以外的候選鍵成為備用候選鍵(alternate key)
外鍵(foreign key)用于實施引用完整性,外鍵只在關系(這里的關系在數(shù)據(jù)庫中稱之為表)中一個或多個屬性上定義的,通過它引用另一個關系(數(shù)據(jù)庫中即為表)中的候選鍵,這種約束要求引用關系中的外鍵屬性值要與被引用關系(referenced relation)的候選鍵的屬性值相一致
關系模型中的規(guī)范化規(guī)則(范式)
第一范式(1NF):要求表中的行必須是唯一的,屬性應該是原子的,這個范式對關系的定義來說是冗余的(最低要求)
行的唯一性是通過在表中定義一個唯一的主鍵而實現(xiàn)的
第二范式(2NF):第二范式包括兩條規(guī)則==>1)首頁數(shù)據(jù)必須滿足第一范式,其次要求非建屬性和候選屬性之間必須滿足一定的條件,每個非建屬性必須完全函數(shù)依賴于整個候選鍵ps:函數(shù)依賴指的是存在組合候選鍵中的某些字段決定非關鍵字段的情況
2017/3/15
第三范式(3NF):第三范式也有兩條規(guī)則,首先數(shù)據(jù)必須滿足第二范式,其次所有非建屬性必須非傳遞依賴于候選鍵,這條規(guī)則意味著所有的非建屬性都必須互相獨立,換句話說,一個非建屬性不能依賴于其他的非建屬性
總的概括一下第二范式和第三范式:每個非鍵屬性都依賴于鍵,全部鍵,除了鍵沒有別的
聯(lián)機事物處理(OLTP):數(shù)據(jù)首先進入聯(lián)機事務處理系統(tǒng),OLTP系統(tǒng)的重點是的數(shù)據(jù)的輸入,而不是生成報表主要處理的事物包括插入、更新和刪除數(shù)據(jù),關系模型的目標主要定位于OLTP系統(tǒng)
數(shù)據(jù)倉庫(Data WareHouses): 專為數(shù)據(jù)檢索和生成報表而設計的環(huán)境,數(shù)據(jù)倉庫最簡單的設計就是所謂的星型模式,如果規(guī)范化一個維度表生成表示該維度的多個表,得到的就是雪花型維度,而包含雪花型 維度的模式成為雪花模式(相對于星型模式)
從原系統(tǒng)抽取數(shù)據(jù),對數(shù)據(jù)進行處理,并將數(shù)據(jù)加載到數(shù)據(jù)倉庫的工具成為ETL*(Extract Transform anf Load)。SQL Server提供一個稱為Microsoft的SSIS(SQL Server Intergration Services)的工具來處理ETL需求
聯(lián)機分析處理(OLAP):系統(tǒng)支持對聚合后的數(shù)據(jù)進行動態(tài)的在線分析,為OLAP需求而設計的特殊的產(chǎn)品--SSAS(這是一個獨立于Sql Sever服務的一種服務或引擎)。用于管理和查詢SSAS的數(shù)據(jù)方塊的語言稱為多為表達式(MDX,Multidimensional Expressions)
數(shù)據(jù)挖掘(DM,data mining),數(shù)據(jù)挖掘模型進行篩選數(shù)據(jù);用于管理和查詢數(shù)據(jù)挖掘模型的語言是數(shù)據(jù)挖掘擴展插件語句(DMX)
數(shù)據(jù)庫:各種對象的容器,這些對象可以是表、視圖、存儲過程(stored procedure)等。
系統(tǒng)數(shù)據(jù)庫:master、Resource、model、tempdb、msdb
數(shù)據(jù)庫登錄的賬號可以關聯(lián)到windows憑據(jù),使用windows驗證登陸不需要提供用戶名和密碼使用Sql server驗證來連接SQL Server時就必須提供用戶名和密碼
數(shù)據(jù)庫對象是將被授權訪問的數(shù)據(jù)庫對象的實體。每個數(shù)據(jù)庫至少得有一個數(shù)據(jù)文件和一個事務日志文件。數(shù)據(jù)文件用于保存數(shù)據(jù)庫對象數(shù)據(jù),日志文件則保存SQL Server為了維護事物而需要的文件
表屬于架構,而架構又屬于數(shù)據(jù)庫
在SQL Server環(huán)境中創(chuàng)建數(shù)據(jù)庫代碼如下:
IF DB_ID('testdb') IS NULL
CREATE ?DATABASE testdb;
ps:DB_ID('')接受一個數(shù)據(jù)庫名稱作為輸入,返回它的內部數(shù)據(jù)庫ID,如果輸入名稱的指定的數(shù)據(jù)庫不存在這個函數(shù)將返回NULL
在例子中,使用的架構是dbo,在每個數(shù)據(jù)庫中都會自動創(chuàng)建這個架構,,當用戶沒有將默認架構顯示關聯(lián)到其他架構時,就會將這個dbo作為默認架構。
以下代碼是在數(shù)據(jù)庫testdb中創(chuàng)建一個名為Employees的表:
USE testdb /**切換數(shù)據(jù)庫**/
IF OBJECT_ID('dbo.Employees','U') IS NOT NULL ? /**OBJECT_ID('','')函數(shù)判斷當前創(chuàng)建的表是否已經(jīng)存在于testdb數(shù)據(jù)庫中,該函數(shù)接受一個對象名稱和類型作為它的參數(shù) ?類型'U'代表用戶表,如果該表在testdb數(shù)據(jù)庫中存在,則刪除它,然后再創(chuàng)建的新的表**/
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
);
PS:上面的代碼使用了前面推薦的兩部分名稱,如果省略架構名,SQL Server將使用與運行這段代碼的數(shù)據(jù)庫用戶相關聯(lián)的默認架構
數(shù)據(jù)的完整性:作為模型的一部分而實施的數(shù)據(jù)完整性(也就是說作為表定義的一部分)稱為成為聲明式數(shù)據(jù)完整性;用代碼來實施的數(shù)據(jù)完整性稱為過程式數(shù)據(jù)完整性
約束(主鍵約束、唯一約束、外鍵約束)
主鍵約束:以前面創(chuàng)建好的Employees表為例,在它的empid列上定義一個主鍵約束
ALTER TABLE dbo.Employees
ADD CONSTRAINT PK_Employees
PRIMARY KEY(empid);
唯一約束:唯一約束用來保證數(shù)據(jù)行的一個列(或一組列)數(shù)據(jù)的唯一,可以在數(shù)據(jù)庫中實現(xiàn)關系模型的替換鍵的概念。
2017/3/16
SQL Server Management Studio中快捷鍵
Ctrl+k,Ctrl+c ?注釋選中行
以下代碼在Employees表中定義了ssn列上的唯一約束:
ALTER TABLE dbo.Employees
ADD CONSTRAINT UNQ_Employees_ssn
UNIQUE(ssn);
外鍵約束:用于實施引用完整性。這種約束在引用表的一組屬性上進行定義并指向被引用表中的一組候選鍵(主鍵或唯一約束)。注意引用表或被引用表可能是同一張表。外鍵的目的是為了將外鍵允許的值域限制為被引用列中現(xiàn)有的值。
以下代碼創(chuàng)建了一個名為Orders的表,其主鍵定義在orderid列上:
--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)
--);
現(xiàn)在要在Orders表的empid列上添加一個外鍵約束,讓它指向Employees表的主鍵的empid列,如下所示:
ALTER TABLE dbo.Orders
ADD CONSTRAINT FK_Orders_Employees
FOREIGN KEY(empid)
REFERENCES dbo.Employees(empid);
如果想限制Employees表中mgrid列支持的值域為同一張表中已經(jīng)存在的那些empid列的值,可以增加以下約束:
ALTER TABLE dbo.Employees
ADD CONSTRAINT FK_Employees_Employees
FOREIGN KEY(mgrid)
REFERENCES dbo.Employees(empid);
禁止操作:當試圖刪除表被引用表中的行時,或更新被引用的候選鍵時,如果在引用表中存在相關的行,則此操作不能執(zhí)行
CASCADE:操作將被級聯(lián)到引用表中的相關行
ON DELETE CASCADE:意味著當從被引用表中刪除一行時,RDBMS也將從引用表中刪除相關的行
CHECK約束:定義在表中輸入或修改一行數(shù)據(jù)之前必須滿足的一個謂詞
以下的檢查約束能保證Employees表中的salary列只支持正數(shù):
ALTER TABLE dbo.Employees
ADD CONSTRAINT CHK_Employees_salary
CHECK(salary>0);
默認約束:默認約束與特定的屬性關聯(lián)
以下代碼為orderts屬性定義了一個默認約束:
ALTER TABLE dbo.Orders
ADD CONSTRAINT DFT_Orders_orderts
DEFAULT(CURRENT_TIMESTAMP) FOR orderts;
第二章 單表查詢
查詢子句是查詢語法的組成部分,當討論邏輯查詢處理中發(fā)生的某一部邏輯處理時,通常會使用階段
FROM子句是邏輯處理階段第一個要處理的查詢子句,這個子句用于指定要查詢的表名以及對這些表進行操作的表運算符
WHERE子句可以指定一個謂詞或邏輯表達式,從而過濾由FROM階段返回的行
SELECT firstname,lastname,ssn,salary
FROM dbo.Employees
WHERE empid=1;
TSQL使用的是三值謂詞邏輯,返回結果為true和不為true是兩個不同的概念,因為還有一個結果為unknown
GROUP BY子句:以指定的元素進行分組
SELECT empid,hiredate
FROM dbo.Employees
WHERE empid=2
GROUP BY empid,hiredate
--SELECT empid,YEAR(hiredate) as hiredate
--?????FROM dbo.Employees
--?????WHERE empid=2
--?????GROUP BY empid,YEAR(hiredate);
因為聚合函數(shù)只為每個組返回一個值,所以一個元素如果不在GROUP BY列表中出現(xiàn),就只能作為聚合函數(shù)(COUNT、SUM、AVG、MIN以及MAX)的輸入
SELECT empid,firstname,lastname
FROM dbo.Employees
GROUP BY empid,firstname,lastname;
PS:如果試圖引用不在GROUP BY列表中出現(xiàn)的屬性(例如 ssn),而且也沒有將其作為GROUP BY子句之后處理的任何子句中聚合函數(shù)的輸入,SQL Server引擎就會報錯
選擇列表中的列 'dbo.Employees.ssn' 無效,因為該列沒有包含在聚合函數(shù)或 GROUP BY 子句中。
注意所有的聚合函數(shù)都會忽略NULL值,只有一個例外-----COUNT(*)===>假設一個組有5行,其中qty列分別為 10,20,NULL,20,10。表達式COUNT(*)將返回5,因為這個組中有5行,而COUNT(qty)返回4,因為只有4行已知值,如果想處理不重復的已知值可以在聚合函數(shù)的圓括號中指定DISTINCT關鍵字
HAVING子句:用于指定對組進行過濾的謂詞和表達式
記住SELECT子句是在FROM、WHERE、GROUP BY、HAVING子句后處理的
ORDER BY子句:用于展示數(shù)據(jù)時對輸出結果中的行進行排序,從邏輯查詢處理來看,ORDER BY是最后一個處理的子句。
SELECT empid,YEAR(orderts) AS orderyear,COUNT(*) AS numorders
FROM dbo.Orders
WHERE custid=1
GROUP BY empid,YEAR(orderts)
HAVING COUNT(*)>1
ORDER BY empid,orderyear;
表不保證是有序的,因為表是為了代表一個集合,而集合是無序的。
ORDER BY是唯一一個在SELEC階段后被處理的階段
排序出現(xiàn)的列不一定要出現(xiàn)在輸出返回的類中,代碼如下:
SELECT empid,firstname,lastname,salary
FROM dbo.Employees
ORDER BY hiredate;
當指定了DISTINCT以后,ORDER BY子句就被限制為只能選取在SELECT列表中出現(xiàn)的那些元素,以下的查詢無效:
SELECT DISTINCT orderid
FROM dbo.Orders
ORDER BY empid;
消息 145,級別 15,狀態(tài) 1,第 78 行
如果指定了 SELECT DISTINCT,那么 ORDER BY 子句中的項就必須出現(xiàn)在選擇列表中。
TOP選項:用于限制查詢返回的行數(shù)或百分比。如果要從Orders表中返回最近的5個訂單可以采用如下的代碼:
SELECT TOP(5) orderid,orderts,custid,empid
FROM dbo.Orders
ORDER BY orderts DESC;
當使用TOP時,同一ORDER BY 子句既擔當了為TOP決定行的邏輯優(yōu)先順序的角色,同時也擔當了它的常規(guī)角色(展示數(shù)據(jù)),只是最終生成的結果由表變成了具有固定順序的游標。
PERCENT關鍵字:
SELECT TOP(50) PERCENT orderid,orderts,custid,empid
FROM dbo.Orders
ORDER BY orderts;
2017/03/17
OVER子句用于為行定義一個窗口,以便進行特定的運算:
SELECT empid,ssn,salary,
SUM(salary) OVER() AS totalvalue,
SUM(salary) OVER(PARTITION BY ssn ) AS custtotalValue
FROM dbo.Employees;
所有結果行的totalvalue列表示所有行的價格總數(shù),custtotalValue列表示所有行中與當前行具有相同ssn值的那些行的價格總數(shù)
OVER子句的一個優(yōu)點就是能夠在返回基本列的同時 ,在同一行對他們進行聚合 。一下代碼查詢?yōu)镋mployees的每一行計算當前價格占總價格的百分比:
SELECT empid,ssn,salary,
100. *salary/ SUM(salary) OVER() AS totalvalue,
100. *salary/SUM(salary) OVER(PARTITION BY ssn ) AS custtotalValue
FROM dbo.Employees;
聚合函數(shù)和排名函數(shù)都是可以支持OVER子句的運算類型,由于OVER子句為這些函數(shù)提供了一個行的窗口,所以這些函數(shù)又稱為開窗函數(shù)
OVER子句支持的四種排名函數(shù):ROW_NUMBER(行號),RANK(排名),DENSE_RANK(密集排名),以及NTILE。以下代碼演示了這些函數(shù)的用法:
SELECT empid,ssn,salary,
ROW_NUMBER() OVER(ORDER BY salary) AS rownum,
RANK() OVER(ORDER BY salary) AS rank,
DENSE_RANK() OVER(ORDER BY salary) AS dense_rank,
NTILE(10)? OVER(ORDER BY salary) AS ntile
FROM dbo.Employees
ORDER BY salary;
RANK列為9表示前面有8行具有更小的排序值,DENSE_RANK列為9表示前面有8個更小的不同的排序值
表達式ROW_NUMBER() OVER(PARTITION BYcustidORDER BY oredrts)為各行中具有相同custid的子集獨立的分配行號
SELECT orderid,empid,custid,orderts,
ROW_NUMBER() OVER(PARTITION BY custid ORDER BY orderts) AS rownum
FROM dbo.Orders
ORDER BY custid,orderts;
如果在SELECT處理階段指定了開窗函數(shù),開窗計算會在DISTINCT子句(如果存在)之前處理
總結點:作為目前為止已經(jīng)討論過的所有子句的總結,以下列出了他們的邏輯處理順序:
FROM
WHERE
GROUP BY
HAVING
SELECT
1)OVER
2)DISTINCT
3)TOP
ORDER BY
謂詞和運算符
T-SQL支持的謂詞有IN、BETWEEN、LIKE等。
IN這個謂詞用于檢查一個值(標量表達式)是否與一組元素中的至少一個相等。以下代碼查詢返回訂單orderid等于1、3、5的訂單:
SELECT orderid,empid,custid,orderts
FROM dbo.Orders
WHERE orderid IN(1,3,5);
BETWEEN這個謂詞用于檢查一個值是否在指定的范圍內,包括邊界值,如下代碼查詢訂單ID在2到5之間的訂單:
SELECT orderid,empid,custid,orderts
FROM dbo.Orders
WHERE orderid BETWEEN 2 AND 5;
LIKE這個謂詞用于檢查一個字符串是否與指定的模式匹配,如下代碼查詢返回姓氏以'x'開頭的所有雇員:
SELECT empid,firstname,lastname,salary
FROM dbo.Employees
WHERE lastname LIKE N'x%';
T-SQL中比較運算符的使用
以下查詢返回訂單日期在2017/03/05之后生成的所有訂單:
SELECT orderid,empid,custid,orderts
FROM dbo.Orders
WHERE orderts>='2017/03/05';
以下的查詢返回訂單日期在2017/03/05之后并且orderid為1,3,5的所有訂單:
SELECT orderid,empid,custid,orderts
FROM dbo.Orders
WHERE orderts>='2017/03/05'
AND orderid IN(1,3,5);
CASE表達式
case表達式是一個標量表達式,它基于條件邏輯來返回一個值。CASE表達式有兩種:簡單表達式和搜索表達式。
CASE簡單格式將一個值(或一個標量)與一組可能的取值進行比較,并返回第一個匹配的結果。如果列表中沒有值等于測試值,CASE表達式就返回其ELSE子句中列出的值。如果CASE表達式中沒有ELSE子句,則默認將其視為ELSE NULL.以下代碼用于描述categoryid列取值的信息:
SELECT orderid,empid,custid,
CASE custid
WHEN 1 THEN '顧客1'
WHEN 2 THEN '顧客2'
ELSE 'UNKNOWN CATEGORY'
END AS categoryName
FROM dbo.Orders;
CASE簡單表達式只有一個測試值,而CASE搜索表達式不限于只進行相等性比較,代碼如下:
SELECT empid,firstname,lastname,salary,
CASE
WHEN salary < 1500 THEN '工資較低'
WHEN salary BETWEEN 1500 AND 3000 THEN '工資一般'
WHEN salary > 3000 THEN '工資較高'
ELSE 'UNKNOWN SALARY'
END AS salaryDesc
FROM dbo.Employees;
NULL值
UNKNOWN和NOT(UNKNOWN)一樣
如果想查找mgrid是NULL的所有行,不應該使用謂詞mgrid=NULL,而應該使用mgrid IS NULL,以下代碼是兩種查詢方式的對比:
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE mgrid=NULL;
返回空集
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE mgrid IS NULL;
1 ?dai ?xin
2 ? gu shiyi
3 ? li ?xueji
3行記錄
2017/03/20
同時操作
SELECT子句中所有表達式的計算是沒有順序的,他們只是一組表達式。在邏輯上列表中的所有表達式都是在同一時刻進行計算的。以下代碼是沒有作用的:
SELECT orderid,YEAR(orderts) AS orderyear,orderyear +1 AS nextyear
FROM dbo.Orders;
SQL Server中的短路求值,代碼示例如下:
SELECT col1,col2
FROM dbo.T1
WHERE col1 <> 0 AND col2 > 2*col1;
處理字符數(shù)據(jù)
SQL Server支持兩種字符數(shù)據(jù)類型--普通字符和Unicode字符。普通字符數(shù)據(jù)類型包括CHAR和VARCHAR,Unicode字符類型包括NCHAR和NVARCHAR。
普通字符需要一個字節(jié)來保存每個字符,而Unicode字符需要兩個字節(jié)來保存每個字符。
名稱中不包含VAR元素的任何數(shù)據(jù)類型都是固定長度的(CHAR,NCHAR),SQL Server會按照列定義的大小,在行中為該列預留出固定的空間,所以該列的長度并不是字符的實際個數(shù);名稱中含有VAR元素的數(shù)據(jù)類型是可變長度(VARCHAR.NVARCHAR),SQL Sercver會在行中會字符串的實際長度來保存數(shù)據(jù)。
排序規(guī)則(collation)
得到系統(tǒng)中目前支持的所有排序規(guī)則及其描述,可以查詢表函數(shù)fn_helpcollations(),代碼如下所示:
SELECT name,description
FROM sys.fn_helpcollations();
當沒有顯示定義任何排序規(guī)則時,就默認使用字典排序(更確切的說排序規(guī)則名稱中沒有顯示的出現(xiàn)BIN元素)。如果出現(xiàn)BIN元素,就表示要根據(jù)字符的二進制表示對字符數(shù)據(jù)進行排序和比較。
CI--數(shù)據(jù)不區(qū)分大小寫
AS--數(shù)據(jù)區(qū)分重音
可以在4重不同的級別上定義排序規(guī)則:SQL Server實例,數(shù)據(jù)庫,列,以及表達式。最低級的排序規(guī)則是比較有效的定義方式。
當創(chuàng)建用戶數(shù)據(jù)庫時,可以使用COLLATE子句指定數(shù)據(jù)庫的排序規(guī)則,如果不指定則默認采用SQL Server實例的排序規(guī)則。
數(shù)據(jù)庫的排序規(guī)則決定數(shù)據(jù)庫中對象元數(shù)據(jù)的排序規(guī)則,同時也是用戶表列的默認使用的排序規(guī)則。
以下查詢是不區(qū)分大小寫的:
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE lastname=N'XIN';
以下查詢是區(qū)分大小寫的:
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE lastname COLLATE Latin1_General_CS_AS =N'XIN';
單引號('')用于分割文字字符串,如果單引號是文字字符串的一部分,則需要由兩個單引號表示('')
運算符和函數(shù)
字符串串聯(lián)運算符
T-SQL提供加號運算符,可以將兩個或多個字符串合并或串聯(lián)成一個字符串。示例如下:
SELECT empid,firstname +N' '+lastname AS fullname
FROM dbo.Employees;
通過將一個CONCAT_NULL_YIELDS_NULL的會話選項設置為OFF,就可以改變SQL Server處理串聯(lián)的方式。設置的代碼如下:
SET CONCAT_NULL_YIELDS_NULL OFF;
如果想把NULL作為一個空字符串,則應該以編程的方式來實現(xiàn)。COALESCE()函數(shù)接受一列值,返回其中第一個不為NULL的值,COALESCE(region,N'')
SUBSTRING(string,start,length)---SELECT SUBSTRING('abcde',1,3),返回'abc'
LEFT、RIGHT函數(shù)。他們分別返回輸入字符串從左邊或右邊開始指定字符的個數(shù)。SELECT RIGHT('abcde',3);
LEN和DATELENGTH函數(shù),LEN函數(shù)返回字符串中的字符數(shù),即字符長度,如果要得到字節(jié)數(shù)則選喲使用DATELENGTH函數(shù)
SELECT LEN(N'abcde');返回5
SELECT DATALENGTH(N'abcde');返回10;
PS:值得注意的是,字節(jié)數(shù)并不一定等于字符數(shù),對于LEN()函數(shù)而言返回的是字符數(shù)。對于普通字符而言,這兩個值相等;但是對于Unicode字符,這兩個值不等,因為Unicode字符中每個字符需要兩個字節(jié)來保存,所以對于Unicode字符而言,字節(jié)數(shù)等于2倍字符數(shù)。
CHARINDEX函數(shù),返回字符串中某個子串第一次出現(xiàn)的起始位置。CHARINDEX(substring,string[,start_pos]),在String中沒有找到SubString,則CHARINDEX返回0,示例代碼如下:
SELECT CHARINDEX(' ','Dai Xin');
PATINDEX函數(shù),PATINDEX(pattern,string)。 參數(shù)pattern使用的模式與T-SQL中LIKE謂詞使用的模式類似,示例代碼如下,返回結果為5:
SELECT PATINDEX('%[0-9]%','abcd123efgh');
REPLACE函數(shù),將字符串中出現(xiàn)的所有某個子串替換為另一個字符串。REPLACE(string,substring1,substring2);該函數(shù)會將string中出現(xiàn)的所有substring1替換為substring2。示例代碼如下:
SELECT REPLACE('1-A 2-B','-',':');
1:A 2:B
REPLACE函數(shù)也可以用來計算字符串中某個字符出現(xiàn)的次數(shù)。為此,先將先將字符串中所有的那個字符替換為空字符串(長度為0的字符串),再計算字符串的原始長度和新長度的差值。示例代碼如下:
SELECT empid,lastname,LEN(lastname)-LEN(REPLACE(lastname,'i','')) AS numoccur
FROM dbo.Employees;
1 ?xin ?1
2 ?shiyi ?2
3 ?xueji ?1
REPLICATE函數(shù)以指定的次數(shù)復制字符串的值。REPLICATE(string,n),示例代碼如下:
SELECT REPLICATE('ABC',3);
ABCABCABC
整數(shù)類型的供應商ID的字符串表示是用CAST函數(shù)生成的,這個函數(shù)用于轉換輸入值的數(shù)據(jù)類型
STUFF函數(shù)可以先刪除字符串中的一個字串,再插入一個新的字符串作為替換。STUFF(string,pos,delete_length,insertstring)。示例代碼如下:
SELECT STUFF('xyz',2,1,'daixin');
xdaixinz
UPPER和LOWER函數(shù)將輸入字符串中所有字符都轉換為大寫或小寫。UPPER(string),LOWER(string)。
SELECT UPPER('daixin')
SELECT LOWER('DAIXIN');
RTRIM和LTRIM函數(shù)用于刪除字符串中的尾隨空格和前導空格。如果既想刪除輸入字符串的前導空格和尾隨空格,則可以將一個函數(shù)的結果所謂另一個函數(shù)的輸入來使用,示例代碼如下:
SELECT RTRIM(LTRIM(' daixin '));
LIKE謂詞
%通配符,%代表任意長度的字符串,包括空字符串,示例代碼如下:
SELECT empid,lastname
FROM dbo.Employees
WHERE lastname LIKE N'x%';
_通配符,_代表任意單個字符,示例代碼如下:
SELECT empid,lastname
FROM dbo.Employees
WHERE lastname LIKE N'_i%';
[<字符列>]通配符,方括號中包含一列字符,表示必須匹配列指定字符中的一個字符,示例代碼如下:
SELECT empid,lastname
FROM dbo.Employees
WHERE lastname LIKE N'[xn]%';
[<字符>-<字符>]通配符,方括號中包含一個字符范圍,表示必須匹配指定范圍內的一個字符。示例代碼如下:
SELECT empid,lastname
FROM dbo.Employees
WHERE lastname LIKE N'[a-z]%';
[^<字符列或范圍>]通配符,表示不屬于指定字符列或范圍內的單個字符,示例代碼如下:
SELECT empid,lastname
FROM dbo.Employees
WHERE lastname LIKE N'[^a-s]%';
ESCAPE轉義字符
col1 LIKE '%!_%' ESCAPE '!';說明‘!’是轉義字符的標識,那么后面的'_'就不是通配符
如果想指定字符串文字‘02/12/2017’的格式為mm/dd/yyyy,則可以使用樣式號101
SELECT CONVERT(DATETIME,'02/12/2017',101);
2017-02-12 00:00:00.000
如果想指定字符串文字‘02/12/2017’的格式為dd/mm/yyyy,則可以使用樣式號103
SELECT CONVERT(DATETIME,'02/12/2017',103);
2017-12-02 00:00:00.000
CURRENT_TIMESTAMP和GETDATE返回的內容相同,當時優(yōu)先選用CURRENT_STAMP
以下代碼為取得當前時間和時間函數(shù)的用法:
SELECT GETDATE() AS [GATEDATE],
CURRENT_TIMESTAMP AS [CURRENT_TIMESTAMP],
GETUTCDATE() AS [GETUTCDATE],
SYSDATETIME() AS [SYSDATETIME],
SYSUTCDATETIME() AS [SYSUTCDATETIME],
SYSDATETIMEOFFSET() AS [SYSDATETIMEOFFSET];
將CURRENT_TIMESTAMP或SYSDATETIME轉換為DATE或TIME,代碼如下所示:
SELECT CAST(SYSDATETIME() AS DATE) AS [CURRENT_DATE],
CAST(SYSDATETIME() AS TIME) AS [CURRENT_DATE];
CAST和CONVERT函數(shù)用于轉換值的數(shù)據(jù)類型
語法:CAST(value AS datetype)
CONVERT(datetype,value[,style_number])
CAST是ANSI標準SQL ,CONVERT不是
以下代碼是使用CAST和CONVERT處理時間和日期數(shù)據(jù)類型的例子:
SELECT CAST('20121225' AS DATE);
SELECT CAST(SYSDATETIME() AS DATE);
SELECT CAST(SYSDATETIME() AS TIME);
SELECT CONVERT(CHAR(8),CURRENT_TIMESTAMP,112);
SELECT CAST(CONVERT(CHAR(8),CURRENT_TIMESTAMP,112) AS DATETIME);
SELECT CONVERT(CHAR(12),CURRENT_TIMESTAMP,114);
SELECT CAST(CONVERT(CHAR(12),CURRENT_TIMESTAMP,114) AS DATETIME);
SWITCHOFFSET函數(shù)可以按指定的時區(qū)對輸入的DATETIMEOFFSET值進行調整
SWITCHOFFSET(datetimeoffset_value,time_zone)
SELECT SWITCHOFFSET(SYSDATETIMEOFFSET(),'-05:00');//按時區(qū)-05:00調整
SELECT SWITCHOFFSET(SYSDATETIMEOFFSET(),'+00:00');//調整為UTC時間
TODATETIMEOFFSET函數(shù)
SELECT TODATETIMEOFFSET(SYSDATETIMEOFFSET(),'-05:00');
為當前日期增加一年DATEADD(part,n,dt_val)
SELECT DATEADD(year,1,'20170320');
DATEDIF返回兩個日期和時間值之間相差的指定部分的計數(shù):DATEDIFF(part,dt_val1,dt_val2)
SELECT DATEDIFF(day,'20121225','20170320');
DATEPART函數(shù)返回給定日期和時間值的指定部分的整數(shù):
SELECT DATEPART(month,CURRENT_TIMESTAMP);
YEAR、MONTH、DAY函數(shù)是DATEPART的簡略版本。
2017/03/21
以下代碼返回給定日期的所在月份的最后一天,很重要:
SELECT DATEADD(month,DATEDIFF(month,'19991231','20170321'),'19991231');
所以解決一下問題就很容易了,以下代碼返回訂單日期是每個月最后一天的所有的訂單:
SELECT orderid,empid,orderts,custid
FROM dbo.Orders
WHERE orderts=DATEADD(month,DATEDIFF(month,'19991231',orderts),'19991231');
可以用模式‘%a%a%'來表達字符'a'在字符串的任意位置至少出現(xiàn)了兩次
在訂單列表中返回總價格大于10000的訂單,這個問題很容易被誤解成返回價格大于10000的訂單,注意這是兩個不同的問題,分析該問題時,要知道首先這不是一行記錄能解決的事情,我們得按orderid進行分組,分完組之后,我們再添加過濾條件,分析代碼如下:
SELECT orderid,SUM(qty*unitprice) AS totalPrice
FROM dbo.Orders
GROUP BY orderid
HAVING SUM(qty*unitprice)>10000
ORDER BY totalPrice DESC;
以下代碼返回平均運費最高的三個國家:
第三章 聯(lián)接查詢
JOIN表運算符對兩個表進行操作,聯(lián)接有三種基本類型:交叉聯(lián)接、內鏈接、外聯(lián)接。
交叉聯(lián)接
ANSI SQL-92語法。示例代碼如下:
SELECT e.empid,o.orderid
FROM dbo.Employees AS e
CROSS JOIN dbo.Orders AS o;
交叉聯(lián)接的關鍵字 ?CROSS JOIN
ANSI SQL-89語法.示例代碼如下:
SELECT e.empid,o.orderid
FROM dbo.Employees AS e,dbo.Orders AS o;
三種聯(lián)接類型都支持自聯(lián)接(self-join),在自聯(lián)接中,必須為 表起別名,如果不為表指定別名,聯(lián)接結果中的列名就會有歧義。
創(chuàng)建表Digits,并向其中插入數(shù)據(jù)
--IF OBJECT_ID('dbo.Digits','U') IS NOT NULL
--?????DROP TABLE dbo.Digits;
--CREATE TABLE Digits(
--?????digit INT NOT NULL PRIMARY KEY
--);
--INSERT INTO dbo.Digits(digit) VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
已下代碼生成1-1000的整數(shù)序列:
SELECT D3.digit*100+D2.digit*10+D1.digit+1 AS n
FROM dbo.Digits AS D1
CROSS JOIN dbo.Digits AS D2
CROSS JOIN dbo.Digits AS D3
ORDER BY n;
內聯(lián)接
ANSI SQL-92語法。須在兩個表名之間指定INNER_JOIN關鍵字。INNER關鍵字是可選的,因為內聯(lián)接是默認的聯(lián)接方式,所以可以只單獨指定JOIN關鍵字,聯(lián)接條件的關鍵字是ON
以下代碼是對數(shù)據(jù)庫中的Employees表和Orders表進行內聯(lián)接運算:
SELECT e.empid,e.firstname,e.lastname,o.orderid
FROM dbo.Employees AS e
JOIN dbo.Orders AS o
ON e.empid=o.orderid;
ANSI SQL-89語法.
SELECT e.empid,e.firstname,e.lastname,o.orderid
FROM dbo.Employees AS e,dbo.Orders AS o
WHERE e.empid=o.orderid;
特殊的聯(lián)接實例:
組合聯(lián)接(composite join)、不等聯(lián)接(non-equi join)、多表聯(lián)接(multi-table join)
組合聯(lián)接:聯(lián)接條件涉及聯(lián)接兩邊的多個列的查詢。
新建一個名為OrderDetailsAudit的客戶審核表,代碼如下:
不等聯(lián)接
多表聯(lián)接
3.4外聯(lián)接
外聯(lián)接是在ANSI SQL-92中被引入的,因為它只有一種標準語法---在表名之間指定JOIN關鍵字,在ON子句中指明聯(lián)接條件。
外聯(lián)接會應用內聯(lián)接的兩個邏輯處理步驟,此外還多加一個外聯(lián)接特有的第三步--添加外部行。
在外聯(lián)接種要把一個表標記為‘保留表’
LEFT OUTER JOIN ?、 RIGHT OUTER JOIN 、 ?FULL OUTER JOIN(OUTER 關鍵字可選)
LEFT關鍵字表示左邊表的行是保留的
RIGHT關鍵字表示右邊表的行是保留的
FULL關鍵字表示左右兩邊表的行都是保留的
新建并填充輔助表Nums的代碼:
--SET NOCOUNT ON;
--IF OBJECT_ID('dbo.Nums','U') IS NOT NULL
--?????DROP TABLE dbo.Employees;
--CREATE TABLE dbo.Nums(
--?????n INT NOT NULL PRIMARY KEY
--);
--DECLARE @i AS INT =1;
--BEGIN TRAN
--?????WHILE @i <=100000
--?????BEGIN
--????????????INSERT INTO dbo.Nums VALUES (@i);
--????????????SET @i=@i+1;
--?????END
--?????COMMIT TRAN
--?????SET NOCOUNT OFF;
這部分較難,過后再理解
1)寫一條查詢語句把所有雇員的記錄復制5次。涉及的表:dbo.Employees和dbo.Nums
SELECT E.empid,E.firstname,E.lastname,Nums.n
FROM dbo.Employees AS E
CROSS JOIN dbo.Nums
WHERE Nums.n <= 5
ORDER BY n,empid;
2)寫一個查詢,為每個雇員和從2009/06/12至2009/06/16范圍內的每天返回一行
第4章 子查詢
最外層查詢結果集會返回給調用者,稱為外部查詢;內部查詢結果是供外部查詢使用的,也稱為子查詢。
子查詢可以分為獨立子查詢和相關子查詢。獨立子查詢不依賴于它所屬的外部查詢,而相關子查詢必須依賴于它所屬的外部查詢。
子查詢返回的結果可以是一個單獨的值,多值或整個 表結果。本章重點討論返回單個值的子查詢(標量子查詢)和多個值的子查詢(多值子查詢)
標量子查詢是返回單個值的子查詢,而不管它是不是獨立子查詢或是相關子查詢,因為相關子查詢也可以返回單個值。標量子查詢可以出現(xiàn)在外部查詢中使用單個值的任何地方(WHERE、 SELECT)
以下代碼返回訂單表中,訂單ID最大的訂單信息:
DECLARE @maxid AS INT =(SELECT MAX(orderid) FROM dbo.Orders);
SELECT orderid,orderts,empid,custid
FROM dbo.Orders
WHERE orderid=@maxid;
將變量轉化成標量子查詢,代碼示例如下:
SELECT orderid,orderts,empid,custid
FROM dbo.Orders
WHERE orderid=(SELECT MAX(orderid) FROM dbo.Orders);
對于有效的標量子查詢,它的返回值不能超過一個。如果標量子查詢返回了多個值,在運行時可能會失效
SELECT orderid
FROM dbo.orders
WHERE empid=(SELECT E.empid FROM dbo.Employees AS E WHERE E.lastname LIKE N'x%');
消息 512,級別 16,狀態(tài) 1,第 357 行
子查詢返回的值不止一個。當子查詢跟隨在 =、!=、<、<=、>、>= 之后,或子查詢用作表達式時,這種情況是不允許的。
多值子查詢,它的返回值不只一個,謂詞用IN,還是上面的例子:
SELECT orderid
FROM dbo.orders
WHERE empid IN (SELECT E.empid FROM dbo.Employees AS E WHERE E.lastname LIKE N'x%');
以下代碼返回沒有下過訂單的客戶:
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE empid NOT IN (SELECT O.empid FROM dbo.Orders O);
2017/03/22
新建一個名為Orders的表,并用TSQLFundamentals2008數(shù)據(jù)庫中Orders表訂單ID為偶數(shù)的訂單來填充這個表
這里需要注意一下 SELECT INTO子句是用來創(chuàng)建一個目標表,并用查詢出來的結果集填充這個表
借助輔助表Nums,篩選出介于Orders表中訂單ID的最小值和最大值之間,而且沒有在Orders表的訂單ID集合中出現(xiàn)的數(shù)字。
以下代碼是自己運行的結果:
USE tempdb;
--SELECT *
--?????INTO dbo.Orders
--?????FROM testdb.dbo.Orders
--?????WHERE orderid % 2=0;
SELECT n
FROM testdb.dbo.Nums
WHERE n BETWEEN (SELECT MIN(O.orderid) FROM dbo.Orders AS O) AND (SELECT MAX(O.orderid) FROM dbo.Orders AS O)
AND n NOT IN (SELECT O.orderid FROM dbo.Orders AS O);
4.2相關子查詢
相關子查詢是指引用外部查詢中出現(xiàn)的表的列的子查詢,這就意味著子查詢要依賴于外部查詢。
以下代碼查詢會為每一個客戶返回其訂單ID的最大的訂單:
SELECT orderid,custid,orderts,empid
FROM dbo.Orders AS O1
WHERE orderid=(
SELECT MAX(O2.orderid) FROM dbo.Orders AS O2
WHERE O2.custid=O1.custid//子查詢依賴于外部查詢,一個客戶可能有多個訂單,我們選擇orderid最大的那個
);
以下代碼返回,當前訂單金額占客戶訂單總額的百分比:
4.2.1 EXISTS謂詞
它的輸入是一個子查詢,,如果子查詢能夠返回任何行,則謂詞返回TRUE,否則返回FALSE。
以下代碼返回,下過訂單的西班牙客戶:
以下代碼返回沒有下過訂單的西班牙客戶:
EXISTS謂詞是使用二值邏輯
對于每一個訂單,返回當前訂單的信息和前一個訂單的ID,示例代碼如下所示:
SELECT orderid,orderts,empid,
(SELECT MAX(O2.orderid) FROM dbo.Orders AS O2
WHERE O2.orderid < O1.orderid) AS preorderid
FROM dbo.Orders AS O1;
對于每一個訂單,返回當前訂單的信息和下一個訂單的ID,示例代碼如下:
SELECT orderid,orderts,empid,
(SELECT MIN(O2.orderid) FROM dbo.Orders AS O2
WHERE O2.orderid > O1.orderid) AS nextorderid
FROM dbo.Orders AS O1;
假設現(xiàn)在有個任務需要返回每年的訂單年份、訂貨量,以及連續(xù)幾年的訂貨量:
現(xiàn)在我們要返回沒有下過訂單的客戶,示例代碼如下:
現(xiàn)在我們假設Orders訂單表中,新增一條數(shù)據(jù)然后將他的custid設置為NULL,那么以上的代碼就會返回空集,unknown,那么這時候怎么做才能讓他顯示上面的結果呢?示例代碼如下:
SELECT empid,firstname,lastname
FROM dbo.Employees AS E
WHERE empid NOT IN(SELECT O.empid FROM dbo.Orders AS O WHERE O.empid IS NOT NULL);
隱式的排除NULL值的一個例子是使用NOT EXISTS謂詞取代NOT IN謂詞,如下圖所示:
--IF OBJECT_ID('dbo.MyShippers','U') IS NOT NULL
--?????DROP TABLE dbo.MyShippers;
--CREATE TABLE dbo.MyShippers(
--?????shipper_id INT NOT NULL,
--?????companyname NVARCHAR(40) NOT NULL,
--?????phone NVARCHAR(24) NOT NULL,
--?????CONSTRAINT PK_MyShippers PRIMARY KEY (shipper_id)
--);
--INSERT INTO dbo.MyShippers(shipper_id,companyname,phone) VALUES(1,N'Shipper GVSUA','(503) 555-0137');
--INSERT INTO dbo.MyShippers(shipper_id,companyname,phone) VALUES(2,N'Shipper ETYNR','(425) 555-0136');
--INSERT INTO dbo.MyShippers(shipper_id,companyname,phone) VALUES(3,N'Shipper ZHISN','(415) 555-0138');
以下查詢返回將訂單發(fā)貨給1號客戶的發(fā)貨人,代碼如下:
SELECT shipper_id,companyname
FROM dbo.MyShippers
WHERE shipper_id IN(SELECT shipper_id FROM dbo.Orders WHERE custid=1);
以上的代碼是有問題的,因為Orders表中沒有shipper_id列,但是它也能返回以下的數(shù)據(jù):
1 ?Shipper GVSUA
2 ?Shipper ETYNR
3?Shipper ZHISN
因為在子查詢中沒有找到,就會去外部查詢中找,這樣就變成了相關子查詢了。
怎么解決這個問題呢?
為表起別名,在被查詢的列名前加上前綴。
1)返回活動最后一天所下的所有訂單
SELECT orderid,empid,orderts,custid
FROM dbo.Orders
WHERE orderts=(SELECT MAX(O.orderts)? FROM dbo.Orders AS O);
2)返回訂單數(shù)量最多的客戶的信息
SELECT TOP(1) WITH TIES O.custid
FROM dbo.Orders AS O
GROUP BY O.custid
ORDER BY COUNT(*) DESC;//先獲取訂單最多的客戶的ID
//接下來在根據(jù)custid查詢訂單最多的客戶下過的所有訂單
SELECT custid,orderid,orderts,empid
FROM dbo.Orders
WHERE custid IN(SELECT TOP(1) WITH TIES O.custid
FROM dbo.Orders AS O
GROUP BY O.custid
ORDER BY COUNT(*) DESC);
3)返回從2017/03/10以后沒有處理過訂單的雇員
首先查詢03/10以后的處理訂單的雇員ID
SELECT O.empid
FROM dbo.Orders AS O
WHERE orderts > '20170310';
然后再在Employees表中查找empid列不屬于以上結果集的記錄
SELECT empid,firstname,lastname
FROM dbo.Employees
WHERE empid NOT IN(SELECT O.empid
FROM dbo.Orders AS O
WHERE orderts > '20170310');
還有例子沒有列出來