1.常见表表达式(CTEs)
如果您想要查询子查询,那就是CTEs施展身手的时候 - CTEs基本上创建了一个临时表。
使用常用表表达式(CTEs)是模块化和分解代码的好方法,与您将文章分解为几个段落的方式相同。
请在Where子句中使用子查询进行以下查询。
1.1 在查询中有许多子查询,那么怎么样?这就是CTEs发挥作用的地方。
---示例---
SELECT
name,
salary
FROM
People
WHERE
NAME IN ( SELECT DISTINCT NAME FROM population WHERE country = "Canada" AND city = "Toronto" )
AND salary >= (
SELECT
AVG( salary )
FROM
salaries
WHERE
gender = "Female")
---CTEs---
with toronto_ppl as (
SELECT DISTINCT name
FROM population
WHERE country = "Canada"
AND city = "Toronto"
)
, avg_female_salary as (
SELECT AVG(salary) as avgSalary
FROM salaries
WHERE gender = "Female"
)
SELECT name
, salary
FROM People
WHERE name in (SELECT DISTINCT FROM toronto_ppl)
AND salary >= (SELECT avgSalary FROM avg_female_salary)
现在很清楚,Where子句是在多伦多的名称中过滤。如果您注意到,CTE很有用,因为您可以将代码分解为较小的块,但它们也很有用,因为它允许您为每个CTE分配变量名称(即toronto_ppl和avg_female_salary) 同样,CTEs允许您完成更高级的技术,如创建递归表
2.临时函数
如果您想了解有关临时函数的更多信息,请检查此项,但知道如何编写临时功能是重要的原因:
- 它允许您将代码的块分解为较小的代码块
- 它适用于写入清洁代码
- 它可以防止重复,并允许您重用类似于使用Python中的函数的代码。
2.1 标量函数返回单个值,可以用于 SELECT 语句中的计算
SELECT name
, CASE WHEN tenure < 1 THEN "analyst"
WHEN tenure BETWEEN 1 and 3 THEN "associate"
WHEN tenure BETWEEN 3 and 5 THEN "senior"
WHEN tenure > 5 THEN "vp"
ELSE "n/a"
END AS seniority
FROM employees
-- 创建标量函数
CREATE FUNCTION dbo.MyTenureName
(
-- 参数列表
@Input int
)
RETURNS Nvarchar(255)
AS
BEGIN
-- 函数的逻辑
DECLARE @Result nvarchar(255);
-- 示例逻辑:将输入参数加倍
-- 使用 CASE 语句
SET @Result =
CASE
WHEN @Input < 1 THEN 'analyst'
WHEN @Input BETWEEN 1 and 3 THEN 'associate'
WHEN @Input BETWEEN 3 and 5 THEN 'senior'
WHEN @Input > 5 THEN 'vp'
ELSE 'n/a'
END;
RETURN @Result;
END;
-- 使用标量函数
SELECT name,tenure
,dbo.MyTenureName(tenure) as tenureName
FROM employees
2.2 创建表值函数(Table-Valued Function): 表值函数返回一个表,可以在 FROM 子句中用于查询。
-- 示例1
CREATE FUNCTION dbo.MyTableValuedFunction
(
-- 参数列表
@InputParameter INT
)
RETURNS TABLE
AS
RETURN (
-- 返回的表结构
SELECT
Column1,
Column2
FROM
YourTable
WHERE
SomeCondition = @InputParameter
);
-- 示例2
create function dbo.MyTableValuedFunction
(
-- 参数列表
@InputTopRow int = 10,
@InputName nvarchar(50) = ''
)
Returns Table
as
return (
SELECT top (@InputTopRow)
[name],
tenure,
CASE WHEN tenure < 1 THEN 'analyst'
WHEN tenure BETWEEN 1 and 3 THEN 'associate'
WHEN tenure BETWEEN 3 and 5 THEN 'senior'
WHEN tenure > 5 THEN 'vp'
ELSE 'n/a'
END AS seniority
FROM employees where [name] like '%'+ISNULL(@InputName,'')+'%')
)
-- 使用表值函数
select * from MyTableValuedFunction(5,'张三')
2.3 删除函数
-- 删除标量函数
DROP FUNCTION dbo.MyScalarFunction;
-- 删除表值函数
DROP FUNCTION dbo.MyTableValuedFunction;
3.使用CASE WHEN枢转数据
您很可能会看到许多要求在陈述时使用CASE WHEN的问题,这只是因为它是一种多功能的概念。如果要根据其他变量分配某个值或类,则允许您编写复杂的条件语句。较少众所周知,它还允许您枢转数据。例如,如果您有一个月列,并且您希望为每个月创建一个单个列,则可以使用语句追溯数据的情况。
示例问题:编写SQL查询以重新格式化表,以便每个月有一个收入列
-- 创建表
CREATE TABLE Case_Test_Table (
id INT,
revenue INT,
month VARCHAR(3)
);
-- 插入数据
INSERT INTO Case_Test_Table (id, revenue, month)
VALUES
(1, 8000, 'Jan'),
(2, 9000, 'Jan'),
(3, 10000, 'Feb'),
(1, 7000, 'Feb'),
(1, 6000, 'Mar');
Initial table:
+------+---------+-------+
| id | revenue | month |
+------+---------+-------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
+------+---------+-------+
Result table:
+------+-------------+-------------+-------------+-----+-----------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-----------+
| 1 | 8000 | 7000 | 6000 | ... | null |
| 2 | 9000 | null | null | ... | null |
| 3 | null | 10000 | null | ... | null |
+------+-------------+-------------+-------------+-----+-----------+
-- 结果示例 Sql
SELECT
id,
MAX(CASE WHEN month = 'Jan' THEN revenue END) AS Jan_Revenue,
MAX(CASE WHEN month = 'Feb' THEN revenue END) AS Feb_Revenue,
MAX(CASE WHEN month = 'Mar' THEN revenue END) AS Mar_Revenue,
MAX(CASE WHEN month = 'Apr' THEN revenue END) AS Apr_Revenue,
MAX(CASE WHEN month = 'May' THEN revenue END) AS May_Revenue,
MAX(CASE WHEN month = 'Jun' THEN revenue END) AS Jun_Revenue,
MAX(CASE WHEN month = 'Jul' THEN revenue END) AS Jul_Revenue,
MAX(CASE WHEN month = 'Aug' THEN revenue END) AS Aug_Revenue,
MAX(CASE WHEN month = 'Sep' THEN revenue END) AS Sep_Revenue,
MAX(CASE WHEN month = 'Oct' THEN revenue END) AS Oct_Revenue,
MAX(CASE WHEN month = 'Nov' THEN revenue END) AS Nov_Revenue,
MAX(CASE WHEN month = 'Dec' THEN revenue END) AS Dec_Revenue
FROM Case_Test_Table
GROUP BY id
ORDER BY id;
4.EXCEPT vs NOT IN 差异
EXCEPT
和 NOT IN
都是用于从查询结果中排除特定值的 SQL 查询语句的部分。然而,它们有一些关键的差异:
- 语法结构:
EXCEPT
使用两个 SELECT 语句,它从第一个查询的结果中排除第二个查询的结果。NOT IN
在单个 SELECT 语句中使用,并且通常结合子查询来排除特定值。
- 处理 NULL 值:
EXCEPT
会自动处理 NULL 值。如果两个查询中都有 NULL 值,它们将被视为相等,不会被排除。NOT IN
在比较中对 NULL 值的处理可能不同,具体取决于数据库的实现。通常情况下,NOT IN
可能需要特殊处理 NULL。
- 性能:
- 在某些情况下,数据库引擎可能对
EXCEPT
优化得更好,尤其是当查询中包含大量结果时。 NOT IN
的性能可能受到查询中值的数量和索引的影响。
- 在某些情况下,数据库引擎可能对
- 查询结果:
EXCEPT
返回两个查询结果的差异,即从第一个结果中排除了第二个结果。NOT IN
返回满足条件的所有行,除了子查询中指定的值。
以下是示例说明:
使用 EXCEPT
的示例:
SELECT column1 FROM table1
EXCEPT
SELECT column1 FROM table2;
使用 NOT IN
的示例:
SELECT column1 FROM table1
WHERE column1 NOT IN (SELECT column1 FROM table2);
总体而言,选择使用 EXCEPT
还是 NOT IN
取决于具体的查询需求和对 NULL 值的处理偏好。
5.自联结
一个SQL表自行连接自己。你可能会认为没有用,但你会感到惊讶的是这是多么常见。在许多现实生活中,数据存储在一个大型表中而不是许多较小的表中。在这种情况下,可能需要自我连接来解决独特的问题。
让我们来看看一个例子。
示例问题:给定下面的员工表,写出一个SQL查询,了解员工的工资,这些员工比其管理人员工资更多。对于上表来说,Joe是唯一一个比他的经理工资更多的员工。
CREATE TABLE YourTable (
Id INT,
Name VARCHAR(50),
Salary INT,
ManagerId INT
);
INSERT INTO YourTable (Id, Name, Salary, ManagerId)
VALUES
(1, 'Joe', 70000, 3),
(2, 'Henry', 80000, 4),
(3, 'Sam', 60000, NULL),
(4, 'Max', 90000, NULL);
+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+Answer:
SELECT
a.Name as Employee
FROM
Employee as a
JOIN Employee as b on a.ManagerID = b.Id
WHERE a.Salary > b.Salary
6. Rank vs Dense Rank vs Row Number
6.1.ROW_NUMBER() OVER (ORDER BY GPA desc)
6.2 RANK() OVER (ORDER BY GPA desc)
6.3 DENSE_RANK() OVER (ORDER BY GPA desc)
这些都是用于在 SQL 中进行排名(ranking)的窗口函数。它们通常与 OVER
子句一起使用,用于根据指定的排序条件对结果集中的行进行排名。
- ROW_NUMBER() OVER (ORDER BY GPA desc):
ROW_NUMBER()
分配唯一的整数值给结果集中的每一行,按照指定的排序条件(这里是GPA
降序)进行排序。即,每行都有一个唯一的排名,不会有相同的排名。- 例如,如果有两个相同的 GPA,它们将被分配不同的
ROW_NUMBER()
。
- RANK() OVER (ORDER BY GPA desc):
RANK()
为每一行分配一个排名,但允许有相同的排名。如果两个行具有相同的排序条件(GPA
),它们将被分配相同的排名,并且下一个排名将被跳过。- 例如,如果两个相同的 GPA,它们将被分配相同的
RANK()
,下一个行将被跳过。
- DENSE_RANK() OVER (ORDER BY GPA desc):
DENSE_RANK()
类似于RANK()
,也为每一行分配一个排名。然而,不同之处在于它不会跳过排名。即,如果有两个行具有相同的排序条件(GPA
),它们将被分配相同的排名,并且下一个排名不会被跳过。- 例如,如果两个相同的 GPA,它们将被分配相同的
DENSE_RANK()
,下一个行将被继续分配下一个排名。
这些排名函数通常用于在查询结果中创建排名列,以便更容易了解每行在排序中的位置。
7. OVER
窗口函数
OVER
子句通常与窗口函数一起使用,用于定义窗口(window),指定在执行窗口函数时要考虑的行的范围。OVER
子句的主要作用是控制窗口函数的计算范围,从而提供更灵活的查询和分析能力。以下是一些常见的用法:
CREATE TABLE YourTable (
column1 INT,
column2 INT,
column3 INT
);
INSERT INTO YourTable (column1, column2, column3)
VALUES
(1, 10, 100),
(2, 20, 200),
(3, 30, 300),
(4, 40, 400),
(5, 50, 500);
-
ORDER BY 子句:
OVER
子句通常包含ORDER BY
子句,用于指定窗口函数计算时的排序条件。这可以确保在窗口函数中按照指定的顺序处理数据。
sqlCopy codeSELECT column1, column2, SUM(column3) OVER (ORDER BY column1) AS RunningTotal FROM YourTable;
-
PARTITION BY 子句:
OVER
子句还可以包含PARTITION BY
子句,用于将数据分成逻辑上的分区,窗口函数在每个分区内进行计算。这允许在分组级别上执行窗口函数。
sqlCopy codeSELECT column1, column2, AVG(column3) OVER (PARTITION BY column1) AS AvgInPartition FROM YourTable;
-
ROWS 或 RANGE 子句:
OVER
子句还可以包含ROWS
或RANGE
子句,用于指定窗口的实际行范围。这允许定义窗口函数计算时要考虑的具体行数或范围。
sqlCopy codeSELECT column1, column2, SUM(column3) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS MovingSum FROM YourTable;
-
UNBOUNDED 子句:
OVER
子句还可以使用UNBOUNDED
关键字,用于表示窗口的边界不受限制。
sqlCopy codeSELECT column1, column2, SUM(column3) OVER (ORDER BY column1 ROWS UNBOUNDED PRECEDING) AS CumulativeSum FROM YourTable;
通过结合 OVER
子句和不同的窗口函数,可以实现各种复杂的分析和聚合操作
8.计算Delta值
另一个常见应用程序是将不同时期的值进行比较。例如,本月和上个月的销售之间的三角洲是什么?或者本月和本月去年这个月是什么?
在将不同时段的值进行比较以计算Deltas时,这是Lead()和LAG()发挥作用时。
这是一些例子:
# Comparing each month's sales to last month
SELECT month
, sales
, sales - LAG(sales, 1) OVER (ORDER BY month)
FROM monthly_sales
# Comparing each month's sales to the same month last year
SELECT month
, sales
, sales - LAG(sales, 12) OVER (ORDER BY month)
FROM monthly_sales
9.计算运行总数
如果你知道关于row_number()和lag()/ lead(),这可能对您来说可能不会惊喜。但如果你没有,这可能是最有用的窗口功能之一,特别是当您想要可视化增长!
使用具有SUM()的窗口函数,我们可以计算运行总数。请参阅下面的示例:
SELECT Month
, Revenue
, SUM(Revenue) OVER (ORDER BY Month) AS Cumulative
FROM monthly_revenue
10.日期时间操纵
您应该肯定会期望某种涉及日期时间数据的SQL问题。例如,您可能需要将数据分组组或将可变格式从DD-MM-Yyyy转换为简单的月份。
您应该知道的一些功能是:
- 提炼
- 日元
- date_add,date_sub.
- date_trunc.
示例问题:给定天气表,写一个SQL查询,以查找与其上一个(昨天)日期相比的温度较高的所有日期的ID。
+---------+------------------+------------------+
| Id(INT) | RecordDate(DATE) | Temperature(INT) |
+---------+------------------+------------------+
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
+---------+------------------+------------------+Answer:
SELECT
a.Id
FROM
Weather a,
Weather b
WHERE
a.Temperature > b.Temperature
AND DATEDIFF(a.RecordDate, b.RecordDate) = 1
标签:CASE,md,SqlServer,WHEN,OVER,玩法,month,子句,SELECT
From: https://www.cnblogs.com/goodluckily/p/17980552