T-SQL基础教程Day2

发布时间 2023-04-14 13:58:29作者: kingster


单表查询
2.1 SELECT语句的元素
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;
1 FROM 从Sales.Orders表查询行
2 WHERE 仅筛选客户ID等于71的订单
3 GROUP BY按雇员ID和订单年度对订单分组
4 HAVING 仅筛选出大于1个订单的组
5 SELECT 返回每组的雇员ID、订单年度和订单数量
6 ORDER BY 按雇员ID和订单年度排序输出行

2.1.1 FROM子句
FROM子句是逻辑化处理的第一个查询子句,此子句指定要查询的表名称和进行多表运算的表运算符。
FROM Sales.Orders
建议:要在代码中始终使用架构限定式的对象名称。
如果不显示制定架构名称,SQL Server必须基于其隐式名称解析规则来确定所归属的架构,这造成了一些不必要的额外支出,并且会导致SQL Server选择不同的对象,而不是所期望的对象。

2.1.2 WHERE子句
可以指定一个谓词或逻辑表达式来筛选由From阶段返回的行。
在谈到查询性能时,WHERE子句具有重要意义。
基于筛选表达式,SQL Server将评估访问请求数据要使用的索引。通过使用索引,相比全表扫描,SQL Server有时可以用更少的工作获得所需数据。
T-SQL使用三值谓词逻辑
“返回TRUE”并不等同于“不返回FALSE”,还有UNKNOWN部分。

2.1.3 GROUP BY子句
GROUP BY阶段允许把前面逻辑查询阶段返回的行排列到组中,组是根据GROUP BY子句中指定的元素而确定的。
如果查询涉及分组,那么GROUP BY阶段的所有后续阶段,包括HAVING、SELECT、ORDER BY都是对组的操作,而不是对单个行进行操作。
不参与到GROUP BY列表中的元素仅允许作为一个聚合函数的输入,如COUNT、SUM、AVG、MIN或MAX。
注意,除了COUNT(*)之外,所有聚合函数忽略NULL标记。

2.1.4 HAVING子句
可以指定一个谓词来筛选组,而不是筛选单个行。
只有HAVING子句中逻辑表达式计算结果为TRUE的组,由HAVING阶段返回到下一个逻辑查询处理阶段。逻辑表达式计算结果为FALSE或UNKNOWN的组会被筛选掉。
由于HAVING子句是在行分组后被处理,所以可以在逻辑表达式中引用集合函数。

2.1.5 SELECT子句
SELECT子句是用户指定要返回到查询结果表中的属性(列)的地方。用户可基于所查询表的属性,在SELECT列表中建立表达式。
SELECT子句是在FROM、WHERE、GROUP BY和HAVING子句之后处理的,这意味着SELECT子句中分配给表达式的别名,不会存在于之前的SELECT相关子句中。
例如以下语句是错误的
SELECT orderid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE orderyear > 2006;
SELECT语句保持唯一性的方法,是加上DISTINCT子句,删除重复行。

2.1.6 ORDER BY子句
出于展示效果的考虑,ORDER BY子句允许你对输出行进行排序。
理解SQL的最重要一点是表中没有确定的顺序,因为表是被假定为表示一个集合(或是多重集合,如果有重复数据的话),并且集合是没有顺序的。这意味着在查询表时没有制定ORDER BY子句,查询将返回一个表结果,并且SQL Server可以按任意顺序自由返回输出行。
标准SQL中把具有ORDER BY子句的结果称为游标——一个具有确定行顺序的非关系型结果。
返回表结果或是游标的差异:在一些语言元素或运算符需要操作表结果,而不是游标。例如:表表达式,集合运算符等。

2.1.7 TOP和OFFSET-FETCH筛选
1 TOP筛选
TOP选项是一个专有的T-SQL功能,用于限制查询返回的行数或行的百分比。
SELECT TOP (1) PERCENT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;
2 OFFSET-FETCH筛选
TOP选项不是标准SQL,且不支持跳过功能,OFFSET-FETCH是标准SQL,SQL Server2012时引入。
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate, orderid
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

2.1.8 开窗函数速览
开窗函数的功能是:对于基本查询中的每一行,按行的窗口(组)进行运算,并计算一个标量结果值。行的窗口使用OVER子句定义。
例子:
SELECT orderid, custid, val,
ROW_NUMBER() OVER(PARTITION BY custid
ORDER BY val) AS rownum
FROM Sales.OrderValues
ORDER BY custid, val;

2.2谓词和运算符
谓词是计算为TRUE、FALSE或UNKNOWN的逻辑表达式。
可以使用逻辑运算符来连接谓词,如AND、NOT和OR。
T-SQL支持的谓词,例如IN、BETWEEN、LIKE等。
T-SQL支持的比较运算符包括=、>、<、>=、<=、<>、!=、!>、!<。
最后三个运算符不是标准的。
T-SQL支持的算术运算符:+、-、*、/、以及取模%
以下列出了运算符的优先级(从最高到最低)
1 ()
2 *、/、%
3 +(正号)-(符号)+(加号)+(串联)-(减号)
4 =、>、<、>=、<=、<>、!=、!>、!<
5 NOT
6 AND
7 BETWEEN、IN、LIKE、OR
8 =(赋值)

2.3 CASE表达式
CASE表达式是一个标量表达式,返回一个基于条件逻辑的值。
需要注意的是,CASE是表达式而不是语句,它不允许你控制活动流或做一些基于条件逻辑的操作。
不过,它的返回值缺失基于条件逻辑的。
CASE表达式具有“简单”和“搜索”两种格式。
简单格式允许在一个可能值列表中比较一个值或标量表达式,并返回第一个匹配值。如果无匹配值,则返回ELSE子句中的值或NULL。
CASE搜索格式更加灵活,允许你再WHEN子句中指定谓词或逻辑表达式,二不是限制于进行相等比较。
CASE表达式返回第一个WHEN逻辑表达式计算结果为TRUE的相关联THEN子句中的值。如果没有WHEN表达式计算结果为TRUE,则返回ELSE子句(如有)中的值或NULL。

2.4 NULL标记
SQL不同的语言元素对于UNKNOWN有不同的处理方式。
对于查询筛选而言,SQL的正确处理定义是“接受TRUE”,意味着FALSE和UNKNOWN都会被筛选掉。
对于CHECK约束而言,SQL的正确处理定义是“拒绝FALSE”,意味着TRUE和UNKNOWN会被接受。
UNKNOWN,当否定它时,仍然会得到UNKNOWN值。
比较两个NULL标记的表达式(NULL=NULL)计算为UNKNOWN。
SQL提供了谓词IS NULL和IS NOT NULL来解决比较问题。
对于分组和排序目的,两个NULL标记被视为相等。
即GROUP BY子句将所有NULL标记排列为一组。
ORDER BY子句也将所有NULL标记排序在一起。

2.5 同时操作
SQL支持一个称作同时操作(all at once operations)的概念,即出现在同一逻辑处理阶段的所有表达式在同一时间点进行逻辑计算。
select 1 as a, 2 as b into #allatonce
update #allatonce set a=b,b=a
select * from #allatonce

2.6.1 数据类型
SQL Server支持两种字符数据类型——常规和Unicode。
常规数据类型包括CHAR和VARCHAR,
Unicode数据类型包括NCHAR和NVARCHAR。
常规字符的每个字符使用一个字节,而Unicode数据的每个字符要求2个字节,并且需要一个代理项对时,要求4个字节。
(代理项(Surrogate),是一种仅在 UTF-16 中用来表示补充字符的方法。在 UTF-16 中,为补充字符分配两个 16 位的 Unicode 代码单元:
第一个代码单元,被称为高代理项代码单元或前导代码单元;
第二个代码单元,被称为低代理项代码单元或尾随代码单元。
这两个代码单元组合在一起,就被称为代理项对。)
在表示常规字符文本时,只需使用单引号,表示Unicode字符文本时,需要指定字符N(即National)作为前缀。
当使用MAX说明符来定义可变长度数据类型时,当大小在8000内时,可以内置存储在行内,超过时,作为大型对象(LOB)存储在行外部。

2.6.2 排序规则
排序规则是一个字符数据属性,其封装了多项内容,包括语言支持、排序顺序、区分大小写、区分重音等。
CI数据不区分大小写
AS数据区分重音
数据库的排序规则决定了数据库对象元数据的排序规则,包括对象和列名。如果区分大小写,则可以创建名为t1和T1的两个表。

2.6.3 运算符和函数
1 字符串连接(加号运算符和CONCAT函数)
2 SUBSTRING函数
3 LEFT和RIGHT函数
4 LEN和DATALENGTH函数
5 CHARINDEX函数
6 PATINDEX函数
7 REPLACE函数
8 REPLICATE函数
9 STUFF函数
10 UPPER和LOWER函数
11 RTRIM和LTRIM函数
12 FORMAT函数
13 COMPRESS和DECOMPRESS函数
14 STRING_SPLIT函数
15 LIKE谓词

2.7.2 日期和时间常量
SQL Server不同日期和时间的常量表示方法,相反,它允许用户指定可以被显式或隐式转换为日期和时间数据类型的不同类型常量。
使用字符串表示日期和时间值是最好的做法。

2.7.3 独立使用日期和时间
SQL Server 2008引入了独立的DATE和TIME数据类型。

2.7.5 日期和时间函数
1 当前日期和时间
2 CAST、CONVERT和PARSE函数,及其TRY_对应函数
3 SWITCHOFFSET函数
4 TODATETIMEOFFSET函数
5 DATEADD函数
6 DATEDIFF函数
7 DATEPART函数
8 YEAR、MONTH和DAY函数
9 DATENAME函数
10 ISDATE函数
11 FROMPARTS函数
12 EOMONTH函数

2.8.1 目录视图
目录视图为数据库中的对象提供了非常详细的信息。
如果想列出数据库中的表和架构,可以查询sys.tables视图。
SELECT SCHEMA_NAME(schema_id) AS table_schema_name, name AS table_name
FROM sys.tables;
要获取表中列的信息,可以查询sys.columns表。
SELECT
name AS column_name,
TYPE_NAME(system_type_id) AS column_type,
max_length,
collation_name,
is_nullable
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Sales.Orders');

2.8.2 信息结构视图
信息架构视图是一个视图集合,位于名为INFORMATION_SCHEMA的架构中,并以标准方式提供元数据信息。
SELECT TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = N'BASE TABLE';

SELECT
COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH,
COLLATION_NAME, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = N'Sales'
AND TABLE_NAME = N'Orders';

2.8.3 系统存储过程和函数
系统存储过程和函数用来内部查询系统目录,有整理后的元数据信息。
EXEC sys.sp_tables;

EXEC sys.sp_help
@objname = N'Sales.Orders';

EXEC sys.sp_columns
@table_name = N'Orders',
@table_owner = N'Sales';

EXEC sys.sp_helpconstraint
@objname = N'Sales.Orders';

SELECT
SERVERPROPERTY('ProductLevel');

SELECT
DATABASEPROPERTYEX(N'TSQL2012', 'Collation');

SELECT
OBJECTPROPERTY(OBJECT_ID(N'Sales.Orders'), 'TableHasPrimaryKey');

SELECT
COLUMNPROPERTY(OBJECT_ID(N'Sales.Orders'), N'shipcountry', 'AllowsNull');

 

章节代码

---------------------------------------------------------------------
-- Microsoft SQL Server 2012 T-SQL Fundamentals
-- Chapter 02 - Single-Table Queries
-- ?Itzik Ben-Gan 
---------------------------------------------------------------------

---------------------------------------------------------------------
-- Elements of the SELECT Statement
---------------------------------------------------------------------

-- Listing 2-1: Sample Query
USE TSQL2012;

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;

---------------------------------------------------------------------
-- The FROM Clause
---------------------------------------------------------------------

SELECT orderid, custid, empid, orderdate, freight
FROM Sales.Orders;

---------------------------------------------------------------------
-- The WHERE Clause
---------------------------------------------------------------------

SELECT orderid, empid, orderdate, freight
FROM Sales.Orders
WHERE custid = 71;

---------------------------------------------------------------------
-- The GROUP BY Clause
---------------------------------------------------------------------

SELECT empid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate);

SELECT
  empid,
  YEAR(orderdate) AS orderyear,
  SUM(freight) AS totalfreight,
  COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate);

/*
SELECT empid, YEAR(orderdate) AS orderyear, freight
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate);
*/

SELECT 
  empid, 
  YEAR(orderdate) AS orderyear, 
  COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY empid, YEAR(orderdate);

---------------------------------------------------------------------
-- The HAVING Clause
---------------------------------------------------------------------

SELECT empid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1;

---------------------------------------------------------------------
-- The SELECT Clause
---------------------------------------------------------------------

SELECT orderid orderdate
FROM Sales.Orders;

SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1;

/*
SELECT orderid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE orderyear > 2006;
*/

SELECT orderid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE YEAR(orderdate) > 2006;

/*
SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate)
HAVING numorders > 1;
*/

SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1;

-- Listing 2-2: Query Returning Duplicate Rows
SELECT empid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71;

-- Listing 2-3: Query With a DISTINCT Clause
SELECT DISTINCT empid, YEAR(orderdate) AS orderyear
FROM Sales.Orders
WHERE custid = 71;

SELECT *
FROM Sales.Shippers;

/*
SELECT orderid,
  YEAR(orderdate) AS orderyear,
  orderyear + 1 AS nextyear
FROM Sales.Orders;
*/

SELECT orderid,
  YEAR(orderdate) AS orderyear,
  YEAR(orderdate) + 1 AS nextyear
FROM Sales.Orders;

---------------------------------------------------------------------
-- The ORDER BY Clause
---------------------------------------------------------------------

-- Listing 2-4: Query Demonstrating the ORDER BY Clause
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 empid, firstname, lastname, country
FROM HR.Employees
ORDER BY hiredate;

/*
SELECT DISTINCT country
FROM HR.Employees
ORDER BY empid;
*/

---------------------------------------------------------------------
-- The TOP and OFFSET-FETCH Filters
---------------------------------------------------------------------

---------------------------------------------------------------------
-- The TOP Filter
---------------------------------------------------------------------

-- Listing 2-5: Query Demonstrating the TOP Option
SELECT TOP (5) orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

SELECT TOP (1) PERCENT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

-- Listing 2-6: Query Demonstrating TOP with Unique ORDER BY List
SELECT TOP (5) orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC, orderid DESC;

SELECT TOP (5) WITH TIES orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

---------------------------------------------------------------------
-- The OFFSET-FETCH Filter
---------------------------------------------------------------------

-- OFFSET-FETCH
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate, orderid
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

---------------------------------------------------------------------
-- A Quick Look at Window Functions
---------------------------------------------------------------------

SELECT orderid, custid, val,
  ROW_NUMBER() OVER(PARTITION BY custid
                    ORDER BY val) AS rownum
FROM Sales.OrderValues
ORDER BY custid, val;

---------------------------------------------------------------------
-- Predicates and Operators
---------------------------------------------------------------------

-- Predicates: IN, BETWEEN, LIKE
SELECT orderid, empid, orderdate
FROM Sales.Orders
WHERE orderid IN(10248, 10249, 10250);

SELECT orderid, empid, orderdate
FROM Sales.Orders
WHERE orderid BETWEEN 10300 AND 10310;

SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname LIKE N'D%';

-- Comparison operators: =, >, <, >=, <=, <>, !=, !>, !< 
SELECT orderid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20080101';

-- Logical operators: AND, OR, NOT
SELECT orderid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20080101'
  AND empid IN(1, 3, 5);

-- Arithmetic operators: +, -, *, /, %
SELECT orderid, productid, qty, unitprice, discount,
  qty * unitprice * (1 - discount) AS val
FROM Sales.OrderDetails;

-- Operators Precedence

-- AND precedes OR
SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE
        custid = 1
    AND empid IN(1, 3, 5)
    OR  custid = 85
    AND empid IN(2, 4, 6);

-- Equivalent to
SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE
      ( custid = 1
        AND empid IN(1, 3, 5) )
    OR
      ( custid = 85
        AND empid IN(2, 4, 6) );

-- *, / precedes +, -
SELECT 10 + 2 * 3   -- 16

SELECT (10 + 2) * 3 -- 36

---------------------------------------------------------------------
-- CASE Expression
---------------------------------------------------------------------

-- Simple
SELECT productid, productname, categoryid,
  CASE categoryid
    WHEN 1 THEN 'Beverages'
    WHEN 2 THEN 'Condiments'
    WHEN 3 THEN 'Confections'
    WHEN 4 THEN 'Dairy Products'
    WHEN 5 THEN 'Grains/Cereals'
    WHEN 6 THEN 'Meat/Poultry'
    WHEN 7 THEN 'Produce'
    WHEN 8 THEN 'Seafood'
    ELSE 'Unknown Category'
  END AS categoryname
FROM Production.Products;

-- Searched
SELECT orderid, custid, val,
  CASE 
    WHEN val < 1000.00                   THEN 'Less than 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;

---------------------------------------------------------------------
-- NULLs
---------------------------------------------------------------------

SELECT custid, country, region, city
FROM Sales.Customers
WHERE region = N'WA';

SELECT custid, country, region, city
FROM Sales.Customers
WHERE region <> N'WA';

SELECT custid, country, region, city
FROM Sales.Customers
WHERE region = NULL;

SELECT custid, country, region, city
FROM Sales.Customers
WHERE region IS NULL;

SELECT custid, country, region, city
FROM Sales.Customers
WHERE region <> N'WA'
   OR region IS NULL;

---------------------------------------------------------------------
-- All-At-Once Operations
---------------------------------------------------------------------

/*
SELECT 
  orderid, 
  YEAR(orderdate) AS orderyear, 
  orderyear + 1 AS nextyear
FROM Sales.Orders;
*/

/*
SELECT col1, col2
FROM dbo.T1
WHERE col1 <> 0 AND col2/col1 > 2;
*/

/*
SELECT col1, col2
FROM dbo.T1
WHERE
  CASE
    WHEN col1 = 0 THEN 'no' -- or 'yes' if row should be returned
    WHEN col2/col1 > 2 THEN 'yes'
    ELSE 'no'
  END = 'yes';
*/

/*
SELECT col1, col2
FROM dbo.T1
WHERE (col1 > 0 AND col2 > 2*col1) OR (col1 < 0 AND col2 < 2*col1); 
*/

select 1 as a, 2 as b into #allatonce
update #allatonce set a=b,b=a
select * from #allatonce


---------------------------------------------------------------------
-- Working with Character Data
---------------------------------------------------------------------

---------------------------------------------------------------------
-- Collation
---------------------------------------------------------------------

SELECT name, description
FROM sys.fn_helpcollations();

SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname = N'davis';

SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname COLLATE Latin1_General_CS_AS = N'davis';

---------------------------------------------------------------------
-- Operators and Functions
---------------------------------------------------------------------

-- Concatenation
SELECT empid, firstname + N' ' + lastname AS fullname
FROM HR.Employees;

-- Listing 2-7: Query Demonstrating String Concatenation
SELECT custid, country, region, city,
  country + N',' + region + N',' + city AS location
FROM Sales.Customers;

-- convert NULL to empty string
SELECT custid, country, region, city,
  country + COALESCE( N',' + region, N'') + N',' + city AS location
FROM Sales.Customers;

-- using CONCAT (2012-only)
SELECT custid, country, region, city,
  CONCAT(country, N',' + region, N',' + city) AS location
FROM Sales.Customers;

-- Functions
SELECT SUBSTRING('abcde', 1, 3); -- 'abc'

SELECT RIGHT('abcde', 3); -- 'cde'

SELECT LEN(N'abcde'); -- 5

SELECT DATALENGTH(N'abcde'); -- 10

SELECT CHARINDEX(' ','Itzik Ben-Gan'); -- 6

SELECT PATINDEX('%[0-9]%', 'abcd123efgh'); -- 5

SELECT REPLACE('1-a 2-b', '-', ':'); -- '1:a 2:b'

SELECT empid, lastname,
  LEN(lastname) - LEN(REPLACE(lastname, 'e', '')) AS numoccur
FROM HR.Employees;

SELECT REPLICATE('abc', 3); -- 'abcabcabc'

SELECT supplierid,
  RIGHT(REPLICATE('0', 9) + CAST(supplierid AS VARCHAR(10)),
        10) AS strsupplierid
FROM Production.Suppliers;

SELECT STUFF('xyz', 2, 1, 'abc'); -- 'xabcz'

SELECT UPPER('Itzik Ben-Gan'); -- 'ITZIK BEN-GAN'

SELECT LOWER('Itzik Ben-Gan'); -- 'itzik ben-gan'

SELECT RTRIM(LTRIM('   abc   ')); -- 'abc'

SELECT FORMAT(1759, '0000000000'); -- '0000001759'

SELECT COMPRESS('abcd') as Comp

SELECT
CAST(
DECOMPRESS(COMPRESS(N'abcd'))
AS NVARCHAR(MAX));

SELECT
CAST(
COMPRESS(N'abcd')
AS NVARCHAR(MAX));

SELECT CAST(value AS INT) AS myvalue
FROM STRING_SPLIT('10248,10249,10250', ',') AS S;
---------------------------------------------------------------------
-- LIKE Predicate
---------------------------------------------------------------------

-- Last name starts with D
SELECT empid, lastname
FROM HR.Employees
WHERE lastname LIKE N'D%';

-- Second character in last name is e
SELECT empid, lastname
FROM HR.Employees
WHERE lastname LIKE N'_e%';

-- First character in last name is A, B or C
SELECT empid, lastname
FROM HR.Employees
WHERE lastname LIKE N'[ABC]%';

-- First character in last name is A through E
SELECT empid, lastname
FROM HR.Employees
WHERE lastname LIKE N'[A-E]%';

-- First character in last name is not A through E
SELECT empid, lastname
FROM HR.Employees
WHERE lastname LIKE N'[^A-E]%';

---------------------------------------------------------------------
-- Working with Date and Time Data
---------------------------------------------------------------------

-- Literals
SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate = '20070212';

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate = CAST('20070212' AS DATETIME);

SET LANGUAGE British;
SELECT CAST('02/12/2007' AS DATETIME);

SET LANGUAGE us_english;
SELECT CAST('02/12/2007' AS DATETIME);

SET LANGUAGE British;
SELECT CAST('20070212' AS DATETIME);

SET LANGUAGE us_english;
SELECT CAST('20070212' AS DATETIME);

SELECT CONVERT(DATETIME, '02/12/2007', 101);

SELECT CONVERT(DATETIME, '02/12/2007', 103);

SELECT PARSE('02/12/2007' AS DATETIME USING 'en-US');

SELECT PARSE('02/12/2007' AS DATETIME USING 'en-GB');

-- Working with Date and Time Separately
SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate = '20070212';

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20070212'
  AND orderdate < '20070213';

SELECT CAST('12:30:15.123' AS DATETIME);

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE YEAR(orderdate) = 2007;

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20070101' AND orderdate < '20080101';

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE YEAR(orderdate) = 2007 AND MONTH(orderdate) = 2;

SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20070201' AND orderdate < '20070301';

-- Functions

-- Current Date and Time
SELECT
  GETDATE()           AS [GETDATE],
  CURRENT_TIMESTAMP   AS [CURRENT_TIMESTAMP],
  GETUTCDATE()        AS [GETUTCDATE],
  SYSDATETIME()       AS [SYSDATETIME],
  SYSUTCDATETIME()    AS [SYSUTCDATETIME],
  SYSDATETIMEOFFSET() AS [SYSDATETIMEOFFSET];

SELECT
  CAST(SYSDATETIME() AS DATE) AS [current_date],
  CAST(SYSDATETIME() AS TIME) AS [current_time];

-- The CAST, CONVERT and PARSE Functions and their TRY_ Counterparts
SELECT CAST('20090212' 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);

SELECT PARSE('02/12/2007' AS DATETIME USING 'en-US');
SELECT PARSE('02/12/2007' AS DATETIME USING 'en-GB');

-- SWITCHOFFSET
SELECT SWITCHOFFSET(SYSDATETIMEOFFSET(), '-05:00');
SELECT SWITCHOFFSET(SYSDATETIMEOFFSET(), '+00:00');

-- TODATETIMEOFFSET
/*
UPDATE dbo.T1
  SET dto = TODATETIMEOFFSET(dt, theoffset);
*/

-- DATEADD
SELECT DATEADD(year, 1, '20090212');

-- DATEDIFF
SELECT DATEDIFF(day, '20080212', '20090212');

SELECT
  DATEADD(
    day, 
    DATEDIFF(day, '20010101', CURRENT_TIMESTAMP), '20010101');

SELECT
  DATEADD(
    month, 
    DATEDIFF(month, '20010101', CURRENT_TIMESTAMP), '20010101');

SELECT
  DATEADD(
    month, 
    DATEDIFF(month, '20091231', CURRENT_TIMESTAMP), '20091231');

-- DATEPART

SELECT DATEPART(month, '20090212');

-- DAY, MONTH, YEAR

SELECT
  DAY('20090212') AS theday,
  MONTH('20090212') AS themonth,
  YEAR('20090212') AS theyear;

-- DATENAME
SELECT DATENAME(month, '20090212');

SELECT DATENAME(year, '20090212');

-- ISDATE
SELECT ISDATE('20090212');
SELECT ISDATE('20090230');

-- fromparts
SELECT
  DATEFROMPARTS(2012, 02, 12),
  DATETIME2FROMPARTS(2012, 02, 12, 13, 30, 5, 1, 7),
  DATETIMEFROMPARTS(2012, 02, 12, 13, 30, 5, 997),
  DATETIMEOFFSETFROMPARTS(2012, 02, 12, 13, 30, 5, 1, -8, 0, 7),
  SMALLDATETIMEFROMPARTS(2012, 02, 12, 13, 30),
  TIMEFROMPARTS(13, 30, 5, 1, 7);

-- EOMONTH
SELECT EOMONTH(SYSDATETIME());

-- orders placed on last day of month
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate = EOMONTH(orderdate);

---------------------------------------------------------------------
-- Querying Metadata
---------------------------------------------------------------------

-- Catalog Views
USE TSQL2012;

SELECT SCHEMA_NAME(schema_id) AS table_schema_name, name AS table_name
FROM sys.tables;

SELECT 
  name AS column_name,
  TYPE_NAME(system_type_id) AS column_type,
  max_length,
  collation_name,
  is_nullable
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Sales.Orders');

-- Information Schema Views
SELECT TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = N'BASE TABLE';

SELECT 
  COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, 
  COLLATION_NAME, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = N'Sales'
  AND TABLE_NAME = N'Orders';

-- System Stored Procedures and Functions
EXEC sys.sp_tables;

EXEC sys.sp_help
  @objname = N'Sales.Orders';

EXEC sys.sp_columns
  @table_name = N'Orders',
  @table_owner = N'Sales';

EXEC sys.sp_helpconstraint
  @objname = N'Sales.Orders';

SELECT 
  SERVERPROPERTY('ProductLevel');

SELECT
  DATABASEPROPERTYEX(N'TSQL2012', 'Collation');

SELECT 
  OBJECTPROPERTY(OBJECT_ID(N'Sales.Orders'), 'TableHasPrimaryKey');

SELECT
  COLUMNPROPERTY(OBJECT_ID(N'Sales.Orders'), N'shipcountry', 'AllowsNull');
  

 练习代码

---------------------------------------------------------------------
-- Microsoft SQL Server 2012 T-SQL Fundamentals
-- Chapter 02 - Single-Table Queries
-- Solutions
-- ?Itzik Ben-Gan 
---------------------------------------------------------------------

-- 1 
-- Return orders placed on June 2007
-- Tables involved: TSQL2012 database, Sales.Orders table

-- Desired output:
orderid     orderdate               custid      empid
----------- ----------------------- ----------- -----------
10555       2007-06-02 00:00:00.000 71          6
10556       2007-06-03 00:00:00.000 73          2
10557       2007-06-03 00:00:00.000 44          9
10558       2007-06-04 00:00:00.000 4           1
10559       2007-06-05 00:00:00.000 7           6
10560       2007-06-06 00:00:00.000 25          8
10561       2007-06-06 00:00:00.000 24          2
10562       2007-06-09 00:00:00.000 66          1
10563       2007-06-10 00:00:00.000 67          2
10564       2007-06-10 00:00:00.000 65          4
10565       2007-06-11 00:00:00.000 51          8
10566       2007-06-12 00:00:00.000 7           9
10567       2007-06-12 00:00:00.000 37          1
10568       2007-06-13 00:00:00.000 29          3
10569       2007-06-16 00:00:00.000 65          5
10570       2007-06-17 00:00:00.000 51          3
10571       2007-06-17 00:00:00.000 20          8
10572       2007-06-18 00:00:00.000 5           3
10573       2007-06-19 00:00:00.000 3           7
10574       2007-06-19 00:00:00.000 82          4
10575       2007-06-20 00:00:00.000 52          5
10576       2007-06-23 00:00:00.000 80          3
10577       2007-06-23 00:00:00.000 82          9
10578       2007-06-24 00:00:00.000 11          4
10579       2007-06-25 00:00:00.000 45          1
10580       2007-06-26 00:00:00.000 56          4
10581       2007-06-26 00:00:00.000 21          3
10582       2007-06-27 00:00:00.000 6           3
10583       2007-06-30 00:00:00.000 87          2
10584       2007-06-30 00:00:00.000 7           4

(30 row(s) affected)

-- Solution
USE TSQL2012;

SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate >= '20070601' 
  AND orderdate < '20070701';

-- 2
-- Return orders placed on the last day of the month
-- Tables involved: Sales.Orders table

-- Desired output:
orderid     orderdate               custid      empid
----------- ----------------------- ----------- -----------
10269       2006-07-31 00:00:00.000 89          5
10317       2006-09-30 00:00:00.000 48          6
10343       2006-10-31 00:00:00.000 44          4
10399       2006-12-31 00:00:00.000 83          8
10432       2007-01-31 00:00:00.000 75          3
10460       2007-02-28 00:00:00.000 24          8
10461       2007-02-28 00:00:00.000 46          1
10490       2007-03-31 00:00:00.000 35          7
10491       2007-03-31 00:00:00.000 28          8
10522       2007-04-30 00:00:00.000 44          4
10583       2007-06-30 00:00:00.000 87          2
10584       2007-06-30 00:00:00.000 7           4
10616       2007-07-31 00:00:00.000 32          1
10617       2007-07-31 00:00:00.000 32          4
10686       2007-09-30 00:00:00.000 59          2
10687       2007-09-30 00:00:00.000 37          9
10725       2007-10-31 00:00:00.000 21          4
10806       2007-12-31 00:00:00.000 84          3
10807       2007-12-31 00:00:00.000 27          4
10987       2008-03-31 00:00:00.000 19          8
10988       2008-03-31 00:00:00.000 65          3
10989       2008-03-31 00:00:00.000 61          2
11060       2008-04-30 00:00:00.000 27          2
11061       2008-04-30 00:00:00.000 32          4
11062       2008-04-30 00:00:00.000 66          4
11063       2008-04-30 00:00:00.000 37          3

(26 row(s) affected)

-- Solution

-- in SQL Server 2012
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate = EOMONTH(orderdate);

-- pre-SQL Server 2012 (advanced)
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate = DATEADD(month, DATEDIFF(month, '19991231', orderdate), '19991231');

-- 3 
-- Return employees with last name containing the letter 'a' twice or more
-- Tables involved: HR.Employees table

-- Desired output:
empid       firstname  lastname
----------- ---------- --------------------
9           Zoya       Dolgopyatova

(1 row(s) affected)

-- Solution
SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname LIKE '%a%a%';

-- 4 
-- Return orders with total value(qty*unitprice) greater than 10000
-- sorted by total value
-- Tables involved: Sales.OrderDetails table

-- Desired output:
orderid     totalvalue
----------- ---------------------
10865       17250.00
11030       16321.90
10981       15810.00
10372       12281.20
10424       11493.20
10817       11490.70
10889       11380.00
10417       11283.20
10897       10835.24
10353       10741.60
10515       10588.50
10479       10495.60
10540       10191.70
10691       10164.80

(14 row(s) affected)

-- Solution
SELECT orderid, SUM(qty*unitprice) AS totalvalue
FROM Sales.OrderDetails
GROUP BY orderid
HAVING SUM(qty*unitprice) > 10000
ORDER BY totalvalue DESC;

-- 5 
-- Return the three ship countries with the highest average freight in 2007
-- Tables involved: Sales.Orders table

-- Desired output:
shipcountry     avgfreight
--------------- ---------------------
Austria         178.3642
Switzerland     117.1775
Sweden          105.16

(3 row(s) affected)

-- Solution
SELECT TOP (3) shipcountry, AVG(freight) AS avgfreight
FROM Sales.Orders
WHERE orderdate >= '20070101' AND orderdate < '20080101'
GROUP BY shipcountry
ORDER BY avgfreight DESC;

-- in SQL Server 2012
SELECT shipcountry, AVG(freight) AS avgfreight
FROM Sales.Orders
WHERE orderdate >= '20070101' AND orderdate < '20080101'
GROUP BY shipcountry
ORDER BY avgfreight DESC
OFFSET 0 ROWS FETCH FIRST 3 ROWS ONLY;

-- 6 
-- Calculate row numbers for orders
-- based on order date ordering (using order id as tiebreaker)
-- for each customer separately
-- Tables involved: Sales.Orders table

-- Desired output:
custid      orderdate               orderid     rownum
----------- ----------------------- ----------- --------------------
1           2007-08-25 00:00:00.000 10643       1
1           2007-10-03 00:00:00.000 10692       2
1           2007-10-13 00:00:00.000 10702       3
1           2008-01-15 00:00:00.000 10835       4
1           2008-03-16 00:00:00.000 10952       5
1           2008-04-09 00:00:00.000 11011       6
2           2006-09-18 00:00:00.000 10308       1
2           2007-08-08 00:00:00.000 10625       2
2           2007-11-28 00:00:00.000 10759       3
2           2008-03-04 00:00:00.000 10926       4
...

(830 row(s) affected)

-- Solution
SELECT custid, orderdate, orderid,
  ROW_NUMBER() OVER(PARTITION BY custid ORDER BY orderdate, orderid) AS rownum
FROM Sales.Orders
ORDER BY custid, rownum;

-- 7
-- Figure out and return for each employee the gender based on the title of courtesy
-- Ms., Mrs. - Female, Mr. - Male, Dr. - Unknown
-- Tables involved: HR.Employees table

-- Desired output:
empid       firstname  lastname             titleofcourtesy           gender
----------- ---------- -------------------- ------------------------- -------
1           Sara       Davis                Ms.                       Female 
2           Don        Funk                 Dr.                       Unknown
3           Judy       Lew                  Ms.                       Female 
4           Yael       Peled                Mrs.                      Female 
5           Sven       Buck                 Mr.                       Male   
6           Paul       Suurs                Mr.                       Male   
7           Russell    King                 Mr.                       Male   
8           Maria      Cameron              Ms.                       Female 
9           Zoya       Dolgopyatova         Ms.                       Female 

(9 row(s) affected)

-- Solutions
SELECT empid, firstname, lastname, titleofcourtesy,
  CASE titleofcourtesy
    WHEN 'Ms.'  THEN 'Female'
    WHEN 'Mrs.' THEN 'Female'
    WHEN 'Mr.'  THEN 'Male'
    ELSE             'Unknown'
  END AS gender
FROM HR.Employees;

SELECT empid, firstname, lastname, titleofcourtesy,
  CASE 
    WHEN titleofcourtesy IN('Ms.', 'Mrs.') THEN 'Female'
    WHEN titleofcourtesy = 'Mr.'           THEN 'Male'
    ELSE                                        'Unknown'
  END AS gender
FROM HR.Employees;

-- 8 (advanced, optional)
-- Return for each customer the customer ID and region
-- sort the rows in the output by region
-- having NULLs sort last (after non-NULL values)
-- Note that the default in T-SQL is that NULL sort first
-- Tables involved: Sales.Customers table

-- Desired output:
custid      region
----------- ---------------
55          AK
10          BC
42          BC
45          CA
37          Co. Cork
33          DF
71          ID
38          Isle of Wight
46          Lara
78          MT
...
1           NULL
2           NULL
3           NULL
4           NULL
5           NULL
6           NULL
7           NULL
8           NULL
9           NULL
11          NULL
...

(91 row(s) affected)

-- Solution
SELECT custid, region
FROM Sales.Customers
ORDER BY
  CASE WHEN region IS NULL THEN 1 ELSE 0 END, region;