当前位置: 首页 > news >正文

SQL知识点总结

总结的知识点主要来源于前段时间在牛客刷SQL题目中遇到的错误

目录

1.WHERE字句不能与高级函数连用

2.去重——distinct

3.不等于某个值

4.查多个范围内的值

5. 升/降序排序

6.占位符

7.统计某类别总数+计算平均值

8.合并查询——UNION (ALL)

9.CASE 函数 (添加新字段)

10.日期相关函数

11.字符串分割

12.取出某属性为最小值的记录

13.CTE与滑动窗口函数

CTE(Common Table Expression,公共表表达式)

滑动窗口函数的基本概念

滑动窗口函数的语法

关键点

常见的滑动窗口函数

示例

滑动窗口函数的优势

14.日期函数与滑动窗口 应用


1.WHERE字句不能与高级函数连用

### 选项A:

```sql

select productid from orders where count(productid) > 1

```

这个查询试图从`orders`表中选择`productid`,其中`productid`的数量大于1。然而,这个查询语句在SQL中是无效的,因为`WHERE`子句不能直接使用聚合函数(如`COUNT`)。聚合函数通常在`HAVING`子句中使用,该子句用于过滤聚合结果。

### 选项D:

```sql

select productid from orders group by productid having count(productid) > 1

```

这个查询是有效的。它首先按`productid`对`orders`表进行分组,然后使用`HAVING`子句来过滤那些`productid`出现次数大于1的组。这意味着它返回的是那些在订单中至少出现两次的产品ID。

总结:

- **选项A**试图执行一个无效的SQL查询,因为它错误地在`WHERE`子句中使用了聚合函数。

- **选项D**正确地使用了`GROUP BY`和`HAVING`子句来选择那些在订单中至少出现两次的产品ID。

### 1. 聚合函数

聚合函数用于从一组值中生成单个值。常见的聚合函数包括:

- `COUNT()`:计算行数。

- `SUM()`:计算数值列的总和。

- `AVG()`:计算数值列的平均值。

- `MAX()`:找出数值列的最大值。

- `MIN()`:找出数值列的最小值。

### 2. GROUP BY 子句

`GROUP BY`子句用于将结果集的行分组,以便可以对每个组执行聚合函数。例如,你可以按产品ID分组,然后计算每个产品的总销售额。

### 3. HAVING 子句

`HAVING`子句用于筛选分组后的结果。与`WHERE`子句不同,`HAVING`可以与聚合函数一起使用。`WHERE`子句用于在分组之前筛选行,而`HAVING`子句用于在分组之后筛选组。

### 4. 使用场景

- **基本聚合**:直接使用聚合函数,如`SELECT COUNT(*) FROM table;`。

- **分组聚合**:使用`GROUP BY`进行分组,然后应用聚合函数,如`SELECT productid, COUNT(*) FROM orders GROUP BY productid;`。

- **筛选聚合结果**:使用`HAVING`子句筛选聚合后的结果,如`SELECT productid FROM orders GROUP BY productid HAVING COUNT(*) > 1;`。

### 5. 注意事项

- `GROUP BY`子句中的列必须出现在选择列表中,或者在聚合函数中使用。

- `HAVING`子句中的条件可以是聚合函数的结果。

- `WHERE`子句不能包含聚合函数,它用于在分组之前筛选行。

### 6. 示例

假设有一个`orders`表,包含`productid`和`quantity`列,你可以使用以下查询来找出每个产品的平均订购数量:

```sql

SELECT productid, AVG(quantity) AS average_quantity

FROM orders

GROUP BY productid;

```

如果你想找出平均订购数量超过10的产品,可以使用`HAVING`子句:

```sql

SELECT productid, AVG(quantity) AS average_quantity

FROM orders

GROUP BY productid

HAVING AVG(quantity) > 10;

```

### 7. 性能优化

- 在使用`GROUP BY`时,确保分组的列上有索引,以提高查询性能。

- 避免在`GROUP BY`子句中使用复杂的表达式或函数,这可能会降低性能。

在SQL查询中,`GROUP BY productid;` 子句的作用是将结果集按照 `productid` 进行分组。这意味着对于每个不同的 `productid`,都会计算一个平均值。让我们详细探讨一下加上和不加 `GROUP BY` 子句对结果的影响:

### 加上 `GROUP BY productid;`

当你使用 `GROUP BY productid;` 子句时,查询会按照 `productid` 对结果进行分组。具体来说,这个查询会:

1. 将 `orders` 表中所有行按照 `productid` 进行分组。

2. 对每个 `productid` 分组,计算 `quantity` 的平均值。

3. 返回每个 `productid` 及其对应的平均 `quantity`。

例如,假设 `orders` 表如下:

| productid | quantity |

|-----------|----------|

| 1         | 10       |

| 1         | 20       |

| 2         | 15       |

| 2         | 25       |

加上 `GROUP BY productid;` 后的查询结果将是:

| productid | average_quantity |

|-----------|------------------|

| 1         | 15               |

| 2         | 20               |

### 不加 `GROUP BY productid;`

如果不使用 `GROUP BY productid;` 子句,查询将不会对结果进行分组。这意味着聚合函数 `AVG(quantity)` 将应用于整个结果集,而不是每个 `productid` 分组。具体来说,这个查询会:

1. 计算整个 `orders` 表中所有 `quantity` 的平均值。

2. 返回一个包含整个表平均 `quantity` 的单一行。

例如,对于上述 `orders` 表,不加 `GROUP BY productid;` 的查询结果将是:

| average_quantity |

|------------------|

| 18.75            |

2.去重——distinct

3.不等于某个值

普通值用“!=” ;不等于空值用 “is not

4.查多个范围内的值

Where in / not in ( , , )

5. 升/降序排序

Oder by … ASC/DESC

6.占位符

查找包含特定字符串的记录,可以使用 LIKE 操作符,并结合通配符。常用的通配符有:

  • %:表示任意长度的任意字符序列(包括空字符序列)。
  • _:表示任意单个字符。

7.统计某类别总数+计算平均值

### 原始查询

```sql

SELECT male_num, avg_gpa

FROM user_profile

GROUP BY gender

HAVING AVG(gpa);

```

### 问题分析

1. **`SELECT` 列未定义**

   - `male_num` `avg_gpa` 在查询中没有定义,它们既不是表中的列,也不是聚合函数的结果。

   - 你需要明确指定要选择的列,并使用聚合函数计算这些值。

2. **`GROUP BY` 的使用**

   - `GROUP BY` 用于对数据进行分组,但你不能直接在 `SELECT` 中使用未分组的列。

   - 如果你想要统计每个性别的总数和平均 GPA,应该使用 `COUNT` `AVG` 函数。

3. **`HAVING` 的使用**

   - `HAVING` 子句通常用于过滤分组后的结果,但它需要一个条件表达式。

   - 在你的查询中,`HAVING AVG(gpa)` 是不完整的,因为它没有指定具体的条件。

### 修正后的查询

假设你想要统计男性用户的总数和平均 GPA,可以使用以下查询:

```sql

SELECT

    COUNT(*) AS male_num,

    AVG(gpa) AS avg_gpa

FROM

    user_profile

WHERE

    gender = 'M';

```

### 如果需要分组统计

如果你想要统计每个性别的总数和平均 GPA,可以使用以下查询:

```sql

SELECT

    gender,

    COUNT(*) AS num,

    AVG(gpa) AS avg_gpa

FROM

    user_profile

GROUP BY

    gender;

```

### 如果需要过滤特定性别的分组结果

如果你想要过滤出平均 GPA 大于某个值的性别分组,可以使用 `HAVING` 子句。例如,过滤出平均 GPA 大于 3.5 的性别分组:

```sql

SELECT

    gender,

    COUNT(*) AS num,

    AVG(gpa) AS avg_gpa

FROM

    user_profile

GROUP BY

    gender

HAVING

    AVG(gpa) > 3.5;

```

  • SELECT 列的定义:SELECT 子句中的列必须是表中的实际列,或者是通过聚合函数计算得到的值。
  • GROUP BY 的使用:GROUP BY 用于分组,SELECT 子句中的列必须是分组列或聚合函数的结果。
  • HAVING 子句的使用:HAVING 用于过滤分组后的结果,必须包含一个条件表达式。
  • WHERE 子句的使用:WHERE 用于过滤行,HAVING 用于过滤分组后的结果。

分组计算:

8.合并查询——UNION (ALL)

Union all:用于合并两个查询的结果集,并且不会去重。如果两个结果集中有相同的行,它们会被保留。

Union:去重合并

9.CASE 函数 (添加新字段)

SELECT

CASE WHEN age < 25 OR age IS NULL THEN '25岁以下'

     WHEN age >= 25 THEN '25岁及以上'

END as age_cut,COUNT(*) as number

FROM user_profile

GROUP BY age_cut

10.日期相关函数

1. 基本日期函数

  • YEAR(date)
    • 作用:从日期值中提取年份部分。
    • 语法:YEAR(date)
    • 示例:YEAR('2021-08-15') 返回 2021。
  • MONTH(date)
    • 作用:从日期值中提取月份部分。
    • 语法:MONTH(date)
    • 示例:MONTH('2021-08-15') 返回 8。
  • DAY(date)
    • 作用:从日期值中提取天数部分。
    • 语法:DAY(date)
    • 示例:DAY('2021-08-15') 返回 15。
  • HOUR(time)
    • 作用:从时间值中提取小时部分。
    • 语法:HOUR(time)
    • 示例:HOUR('14:30:00') 返回 14。
  • MINUTE(time)
    • 作用:从时间值中提取分钟部分。
    • 语法:MINUTE(time)
    • 示例:MINUTE('14:30:00') 返回 30。
  • SECOND(time)
    • 作用:从时间值中提取秒数部分。
    • 语法:SECOND(time)
    • 示例:SECOND('14:30:00') 返回 0。

2. 日期格式化函数

  • DATE_FORMAT(date, format)
    • 作用:将日期值格式化为指定的字符串格式。
    • 语法:DATE_FORMAT(date, format)
    • 示例
      • DATE_FORMAT('2021-08-15', '%Y-%m-%d') 返回 '2021-08-15'。
      • DATE_FORMAT('2021-08-15', '%Y%m%d') 返回 '20210815'。

3. 日期范围筛选

  • BETWEEN start AND end
    • 作用:用于筛选某个字段值在指定范围内的记录。
    • 语法:field BETWEEN start AND end
    • 示例
      • date BETWEEN '2021-08-01' AND '2021-08-31' 用于筛选日期在 2021 年 8 月 1 日到 2021 年 8 月 31 日之间的记录。

4. 日期计算函数

  • DATE_ADD(date, INTERVAL expr type)
    • 作用:在日期上加上指定的时间间隔。
    • 语法:DATE_ADD(date, INTERVAL expr type)
    • 示例
      • DATE_ADD('2021-08-15', INTERVAL 10 DAY) 返回 '2021-08-25'。
  • DATE_SUB(date, INTERVAL expr type)
    • 作用:从日期上减去指定的时间间隔。
    • 语法:DATE_SUB(date, INTERVAL expr type)
    • 示例
      • DATE_SUB('2021-08-15', INTERVAL 10 DAY) 返回 '2021-08-05'。
  • DATEDIFF(date1, date2)
    • 作用:计算两个日期之间的天数差。
    • 语法:DATEDIFF(date1, date2)
    • 示例
      • DATEDIFF('2021-08-25', '2021-08-15') 返回 10。

5. 当前日期和时间函数

  • NOW()
    • 作用:返回当前的日期和时间。
    • 语法:NOW()
    • 示例:NOW() 返回当前的日期和时间,例如 '2025-03-24 14:30:00'。
  • CURDATE()
    • 作用:返回当前的日期。
    • 语法:CURDATE()
    • 示例:CURDATE() 返回当前的日期,例如 '2025-03-24'。
  • CURTIME()
    • 作用:返回当前的时间。
    • 语法:CURTIME()
    • 示例:CURTIME() 返回当前的时间,例如 '14:30:00'。

6. 时间戳函数

  • UNIX_TIMESTAMP(date)
    • 作用:将日期时间转换为 Unix 时间戳(从 1970-01-01 00:00:00 UTC 开始的秒数)。
    • 语法:UNIX_TIMESTAMP(date)
    • 示例:UNIX_TIMESTAMP('2021-08-15 14:30:00') 返回对应的 Unix 时间戳。
  • FROM_UNIXTIME(timestamp)
    • 作用:将 Unix 时间戳转换为日期时间格式。
    • 语法:FROM_UNIXTIME(timestamp)
    • 示例:FROM_UNIXTIME(1628994600) 返回 '2021-08-15 14:30:00'。

7. 其他日期函数

  • LAST_DAY(date)
    • 作用:返回指定日期所在月份的最后一天。
    • 语法:LAST_DAY(date)
    • 示例:LAST_DAY('2021-08-15') 返回 '2021-08-31'。
  • DAYOFWEEK(date)
    • 作用:返回指定日期是星期几(1 = 星期天,2 = 星期一,...,7 = 星期六)。
    • 语法:DAYOFWEEK(date)
    • 示例:DAYOFWEEK('2021-08-15') 返回 1(星期天)。
  • DAYOFYEAR(date)
    • 作用:返回指定日期是一年中的第几天。
    • 语法:DAYOFYEAR(date)
    • 示例:DAYOFYEAR('2021-08-15') 返回 227。

11.字符串分割

SUBSTRING_INDEX函数

SUBSTRING_INDEX(str, delim, count):从字符串str中,以delim为分隔符,提取从开始到第count个分隔符之间的内容。

SUBSTRING_INDEX(profile, ',', 3):提取从开始到第3个逗号之前的内容。

SUBSTRING_INDEX(profile, ',', -1):提取从最后一个逗号到结束的内容。

STR_SPLIT函数

  • 在MySQL 8.0及以上版本中,可以使用STR_SPLIT函数来分割字符串。例如:

sqlCopy

SELECT STR_SPLIT(profile, ',', 1) AS height,

       STR_SPLIT(profile, ',', 2) AS weight,

       STR_SPLIT(profile, ',', 3) AS age,

       STR_SPLIT(profile, ',', 4) AS gender

FROM users;

12.取出某属性为最小值的记录

  1. 子查询部分

(SELECT university, MIN(gpa) FROM user_profile GROUP BY university)

    • 这个子查询首先按照university字段对user_profile表进行分组(GROUP BY) university。
    • 然后,对于每个分组(即每个大学),计算出该大学中学生的最低gpa值(MIN(gpa))。
    • 最终,子查询返回一个包含每个大学及其对应最低gpa值的结果集。
  1. 主查询部分

SELECT device_id, university, gpa

FROM user_profile

WHERE (university, gpa) IN (子查询结果)

ORDER BY university

    • 主查询从user_profile表中选择device_id、university和gpa这三个字段。
    • WHERE子句中的条件(university, gpa) IN (...)用于筛选出那些在user_profile表中,其university和gpa的组合与子查询返回的结果集中的某个组合相匹配的记录。也就是说,只选择每个大学中gpa最低的学生记录。
    • 最后,ORDER BY university将结果按照university字段进行升序排序。

-----------------------------------------------------------

select emp_no,

birth_date,

first_name,

last_name,

gender,

hire_date

from employees

where hire_date = (select max(hire_date) from employees )       //单一值用“=”,后查询需“()”    

举个例子: 假设user_profile表中有以下数据:

device_id

university

gpa

1

MIT

3.0

2

MIT

2.5

3

Stanford

3.5

4

Stanford

3.5

子查询会返回:

university

min_gpa

MIT

2.5

Stanford

3.5

主查询会根据这个结果筛选出:

device_id

university

gpa

2

MIT

2.5

3

Stanford

3.5

4

Stanford

3.5

这样,我们就得到了每个大学中gpa最低的学生记录,并且按照大学名称排序。

13.CTE与滑动窗口函数

CTE(Common Table Expression,公共表表达式)

是一种临时的结果集,可以在SQL查询中被重复使用。它类似于一个临时表,但它的作用范围仅限于当前查询。CTE可以简化复杂的SQL查询,使查询更易于理解和维护。

CTE的语法结构如下:

WITH CTE_name (column1, column2, ...)

AS (

    -- CTE的查询定义

    SELECT column1, column2, ...

    FROM some_table

    WHERE some_condition

)

SELECT column1, column2, ...

FROM CTE_name

WHERE some_condition;

滑动窗口函数的基本概念

滑动窗口函数通过 OVER 子句定义一个窗口(或范围),并在该窗口内对数据进行计算。窗口可以基于行的顺序、分组或特定的范围来定义。常见的滑动窗口函数包括 ROW_NUMBER()RANK()DENSE_RANK()SUM()AVG()MIN()MAX() 等。

滑动窗口函数的语法

滑动窗口函数的语法结构如下:

关键点

  1. PARTITION BY
    • 将数据分成多个分区,窗口函数在每个分区内独立计算。
    • 类似于 GROUP BY,但不会丢失行的细节。
  2. ORDER BY
    • 定义窗口内行的顺序。
  3. ROWS RANGE
    • 定义窗口的范围,可以是基于行数的范围(ROWS)或基于值的范围(RANGE)。

常见的滑动窗口函数

  1. ROW_NUMBER()
    • 为每一行分配一个唯一的序号,序号在每个分区内从1开始递增。

  1. RANK()
    • 为每一行分配一个排名,排名在每个分区内从1开始递增,相同值的行会分配相同的排名,但会跳过后续的排名。

  1. DENSE_RANK()
    • RANK() 类似,但不会跳过后续的排名。

  1. SUM()
    • 计算窗口内的总和。

  1. AVG()
    • 计算窗口内的平均值。

  1. MIN() MAX()
    • 计算窗口内的最小值和最大值。

示例

假设我们有一个销售记录表 sales,包含以下列:id(销售记录ID)、sale_date(销售日期)、amount(销售金额)。我们想计算每个销售日期的累计销售额。


滑动窗口函数的优势

  • 灵活的窗口定义:可以通过 PARTITION BYORDER BY ROWS/RANGE 定义灵活的窗口。
  1. 强大的分析能力:可以轻松实现复杂的分析,如累计和、移动平均、排名等。
  2. 保持行的细节:与传统的聚合函数不同,滑动窗口函数不会丢失行的细节。

14.日期函数与滑动窗口 应用

你正在搭建一个用户活跃度的画像,其中一个与活跃度相关的特征是最长连续登录天数

请用SQL实现“202311-2023131日用户最长的连续登录天数

登陆表 tb_dau

fdate

user_id

2023-01-01

10000

2023-01-02

10000

2023-01-04

10000

输出:

user_id

max_consec_days

10000

2

WITH continueDay as(

    select

        fdate,user_id,

        DATE_SUB(fdate, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY fdate) DAY) AS org

    FROM tb_dau

    where fdate between '2023-01-01' and '2023-1-31'

),

continueLogin as(

    select

        user_id,

        count(*) as consec

    from continueDay

    group by user_id,org

)

SELECT

    user_id,

    MAX(consec) AS max_consec_days

FROM

    continueLogin

GROUP BY

    user_id;

思路:

通过 DATE_SUB(fdate, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY fdate) DAY) 创建一个分组标识grp。如果日期是连续的,值会相同。例如:

2023-01-01 和 2023-01-02 的 grp 都是 2022-12-30。

2023-01-04 的 grp 是 2022-12-31。

相关文章:

  • 解决正点原子IMX6U开发板Buildroot构建qt根文件系统解压后,没有库文件
  • ​无线手持吸尘器无刷BLDC驱动方案功能介绍---【其利天下】
  • 单片机-STM32部分:9、定时器
  • ui生成提示词
  • pm2 list查询服务时如何通过name或者namespace进行区分
  • 笔试专题(十六)
  • 电力电子simulink练习09:复合控制_闭环控制与开环控制结合
  • 高频交直流传感技术在射频器件与微系统测试中的创新实践
  • Windows命令行软件管理器:Chocolatey
  • CH32V208GBU6沁恒绑定配对获取静态地址
  • 【日撸 Java 三百行】Day 6(For语句)
  • python里面的class,类,方法,函数,def
  • 第一次被AI指点出文章的问题
  • 5月8号.
  • 精读计算机体系结构基础 第一章 引言
  • 3.2.3 掌握RDD转换算子 - 4. 按键归约算子 - reduceByKey()
  • C++复习类与对象基础
  • 《信息论与编码课程笔记》——信源编码(1)
  • chili调试笔记14 画线 页面布置 线条导出dxf
  • linux nginx配置访问目录,访问文件直接下载,linux配置nginx直链下载
  • 夜读丨喜马拉雅山的背夫
  • 47本笔记、2341场讲座,一位普通上海老人的阅读史
  • 习近平出席俄罗斯纪念苏联伟大卫国战争胜利80周年庆典
  • 逆境之上,万物生长
  • 国家发改委:美芯片药品等领域关税影响全球科技发展,损害人类共同利益
  • 新疆维吾尔自治区乌鲁木齐市米东区政协原副主席朱文智被查