37、SQL 日期与时间处理技巧全解析

SQL 日期与时间处理技巧全解析

在数据库操作中,日期和时间的处理是一个常见且重要的任务。无论是统计销售数据、分析库存时间,还是确定特定日期的业务情况,都离不开对日期和时间的精确处理。本文将深入探讨一些常见的日期和时间处理场景,并给出相应的 SQL 解决方案。

1. 日期计数列表的创建

有时候,我们需要创建一个日期计数列表。可以通过以下 SQL 代码来创建一个较大的计数列表:

SELECT     ROW_NUMBER() OVER (ORDER BY Y.StockCode) - 1 AS nm
FROM       stock Y
CROSS JOIN Stock Z

这个代码通过对 stock 表进行交叉连接,并使用 ROW_NUMBER() 函数生成一个计数列表。

2. 每月最后一天的销售数据统计

销售经理想了解 2016 年每个月最后一天的销售情况,我们可以使用以下 SQL 来实现:

WITH TallyTable_CTE
AS
(
SELECT     ROW_NUMBER() OVER (ORDER BY StockCode) 
AS nm
FROM       stock
ORDER BY   nm
LIMIT      12
)
,LastDayOfMonth_CTE
AS
(
SELECT       LAST_DAY(CONCAT('2016-', nm, '-01')) AS 
LastDayDate
FROM         TallyTable_CTE
)
SELECT        CTE.LastDayDate
             ,SUM(SLS.SalePrice) AS TotalDailySales
FROM         salesbycountry SLS
JOIN         LastDayOfMonth_CTE CTE
             ON CTE.LastDayDate = 
DATE(SLS.SaleDate)
GROUP BY     CTE.LastDayDate
ORDER BY     CTE.LastDayDate;

操作步骤
1. 创建 TallyTable_CTE :使用 ROW_NUMBER() 函数生成一个从 1 开始的递增数字列表,由于一年最多 12 个月,所以限制列表长度为 12。
2. 创建 LastDayOfMonth_CTE :从 TallyTable_CTE 中获取数字,使用 CONCAT() 函数拼接成日期,再使用 LAST_DAY() 函数提取每个月的最后一天。
3. 主查询 :将 LastDayOfMonth_CTE salesbycountry 表进行连接,根据最后一天的日期进行分组,统计每天的销售总额。

3. 每月最后一个星期五的日期确定

HR 总监需要一份 2018 年每个月最后一个星期五的列表,以下 SQL 可以满足需求:

WITH TallyTable_CTE
AS
(
SELECT     ROW_NUMBER() OVER (ORDER BY StockCode) 
AS nm
FROM       stock
ORDER BY   nm
LIMIT      12
)
,LastDayOfMonth_CTE
AS
(
SELECT       LAST_DAY(CONCAT('2023-', nm, '-01')) 
AS MonthEndDate
             ,WEEKDAY(LAST_DAY(CONCAT('2023-', nm, 
'-01'))) + 1
             AS MonthEndDay
FROM         TallyTable_CTE
)
SELECT       MonthEndDate
             ,CASE
                 WHEN MonthEndDay >= 5 THEN 
DATE_ADD(MonthEndDate
                      ,5 - MonthEndDay) 
                 ELSE DATE_SUB(MonthEndDate
             ,2 + MonthEndDay)
             END AS LastFridayOfMonth
FROM         LastDayOfMonth_CTE;

操作步骤
1. 创建 TallyTable_CTE :同前面的例子,生成一个 1 到 12 的数字列表。
2. 创建 LastDayOfMonth_CTE :生成每个月的最后一天日期,并计算该日期是星期几(星期五在代码中被视为第 6 天)。
3. 主查询 :使用 CASE 语句根据最后一天是星期几来计算最后一个星期五的日期。如果最后一天是星期五、星期六或星期日,使用 DATE_ADD() 函数;如果是星期一到星期四,使用 DATE_SUB() 函数。

4. 车辆库存时间的分析

Prestige Cars 的 CEO 希望了解每辆车在库存中的总年数、总月数和总天数,以下 SQL 可以实现:

SELECT       ST.StockCode
            ,ST.DateBought
            ,SLS.SaleDate
            ,TIMESTAMPDIFF(YEAR, ST.DateBought
              ,IFNULL(SLS.SaleDate, CURDATE())) AS 
Years
            ,TIMESTAMPDIFF(MONTH, ST.DateBought
              ,IFNULL(SLS.SaleDate, CURDATE())) AS 
Months
            ,TIMESTAMPDIFF(DAY, ST.DateBought
              ,IFNULL(SLS.SaleDate, CURDATE())) AS 
Days
FROM        stock ST
LEFT JOIN   (
            SELECT SA.SaleDate
                   ,StockID                   
            FROM   salesdetails SD
            JOIN   sales SA USING(SalesID)
            ) SLS
            ON ST.StockCode = SLS.StockID 
WHERE       SLS.SaleDate IS NULL
ORDER BY    Years DESC, Months DESC, Days DESC; 

操作步骤
1. 表连接 :使用 LEFT JOIN stock 表与子查询连接,确保所有车辆信息都被包含,即使没有销售记录。
2. 时间计算 :使用 TIMESTAMPDIFF() 函数计算车辆在库存中的年数、月数和天数。该函数需要三个参数:时间单位(如 YEAR MONTH DAY )、开始日期和结束日期。如果没有销售日期,则使用当前日期。
3. 筛选和排序 :筛选出没有销售日期的车辆,并按照年数、月数和天数降序排序。

5. TIMESTAMPDIFF() 函数参数表

TIMESTAMPDIFF() 函数在计算时间间隔时非常有用,以下是一些常用的参数及其描述:
| Interval code | Description |
| ---- | ---- |
| DAY | 提取两个时间戳之间的完整天数 |
| WEEK | 提取两个时间戳之间的完整周数 |
| MONTH | 提取两个时间戳之间的完整月数 |
| QUARTER | 提取两个时间戳之间的完整季度数 |
| YEAR | 提取两个时间戳之间的完整年数 |
| HOUR | 提取两个时间戳之间的小时数 |
| MINUTE | 提取两个时间戳之间的分钟数 |
| SECOND | 提取两个时间戳之间的秒数 |
| MILLISECOND | 提取两个时间戳之间的毫秒数 |

6. 从日期和时间数据中分离时间周期

销售团队想知道 2017 年哪些汽车在库存中停留的时间最短,可能只有几分钟甚至几秒,以下 SQL 可以实现:

SELECT       ST.DateBought
            ,SA.SaleDate
            ,CONCAT(MakeName, '-', ModelName) AS 
MakeAndModel
            ,TIMESTAMPDIFF(HOUR, ST.DateBought, SA.SaleDate) AS 
Hours
            ,TIMESTAMPDIFF(MINUTE, ST.DateBought, SA.SaleDate) AS 
Minutes
            ,TIMESTAMPDIFF(SECOND, ST.DateBought, SA.SaleDate) AS 
Seconds
            ,CONCAT(FLOOR(TIMESTAMPDIFF(HOUR, ST.DateBought, 
SA.SaleDate) / 24) 
                    ,' Days - '
                    ,TIMESTAMPDIFF(HOUR, ST.DateBought, SA.SaleDate) 
% 24
                    ,' Hours')
                 AS DaysAndHours
FROM         stock ST
JOIN         model MD
             ON ST.ModelID = MD.ModelID 
JOIN         make MK
             ON MD.MakeID = MK.MakeID 
JOIN         salesdetails SD
             ON ST.StockCode = SD.StockID 
JOIN         sales SA
             ON SD.SalesID = SA.SalesID 
WHERE        YEAR(SA.SaleDate) = 2017
ORDER BY     Hours DESC, Minutes DESC, Seconds 
DESC;

操作步骤
1. 表连接 :连接 stock model make salesdetails sales 表,获取所需的字段。
2. 筛选数据 :使用 WHERE 子句筛选出 2017 年销售的车辆。
3. 时间计算 :使用 TIMESTAMPDIFF() 函数计算车辆在库存中的小时数、分钟数和秒数,并计算天数和小时数。
4. 排序结果 :按照小时数、分钟数和秒数降序排序。

7. 按时间显示销售数据

销售经理想了解 2017 年汽车销售的具体时间,以下 SQL 可以实现:

SELECT     MakeName, ModelName, SalePrice, SaleDate
          ,DATE_FORMAT(SaleDate, 'hh:mm') AS 
TimeOfDaySold
FROM      allsales
WHERE     YEAR(SaleDate) = 2017
ORDER BY  TimeOfDaySold;

操作步骤
1. 数据筛选 :从 allsales 表中筛选出 2017 年的销售数据。
2. 时间格式化 :使用 DATE_FORMAT() 函数将 SaleDate 字段格式化为 hh:mm 格式。
3. 结果排序 :按照格式化后的时间排序。

8. 按小时区间聚合销售数据

CEO 希望了解 2017 年按小时区间聚合的销售情况,以下 SQL 可以实现:

SELECT
            CONCAT(HourOfDay, '-', HourOfDay + 1) 
AS HourBand
            ,SUM(SalePrice) AS SalesByHourBand
FROM
    (
     SELECT     SalePrice
               ,HOUR(SaleDate) AS HourOfDay
     FROM      salesbycountry
     WHERE     YEAR(SaleDate) = 2017
    ) A
GROUP BY   HourOfDay
ORDER BY   HourOfDay;

操作步骤
1. 子查询 :从 salesbycountry 表中筛选出 2017 年的销售数据,并使用 HOUR() 函数提取销售时间的小时数。
2. 主查询 :使用 CONCAT() 函数将小时数拼接成小时区间,使用 SUM() 函数聚合每个小时区间的销售金额。
3. 分组和排序 :按照小时数分组,并按小时数排序。

Tricks and Traps

在处理日期和时间数据时,还需要注意以下几点:
1. 日期类型要求 TIMESTAMPDIFF() 函数使用的日期元素必须是 DATE TIMESTAMP 数据类型,或者是 Databricks 可以解释为日期的数据。
2. 完整周期计算 TIMESTAMPDIFF() 函数计算的是两个日期之间的完整周期。
3. USING() 函数使用条件 :在子查询中使用 USING() 函数连接表时,要求两个表中的字段名相同。
4. 时间格式化注意事项 :使用 DATE_FORMAT() 函数格式化后的输出是文本类型,不能用于数据排序或时间计算,应使用原始数据字段进行这些操作。
5. 查询优化 :可以将整个查询包装在 CTE 或子查询中,然后按聚合金额排序,以找出最盈利的时间段进行更深入的分析。

通过以上的 SQL 示例和技巧,我们可以更灵活地处理日期和时间数据,满足各种业务需求。在实际应用中,根据具体情况选择合适的方法和函数,能够提高数据处理的效率和准确性。

SQL 日期与时间处理技巧全解析

9. 各场景操作流程总结

为了更清晰地展示前面各个场景的操作流程,我们可以用 mermaid 流程图来呈现。以下是每月最后一天销售数据统计的流程图:

graph TD
    A[开始] --> B[创建 TallyTable_CTE]
    B --> C[创建 LastDayOfMonth_CTE]
    C --> D[主查询:连接表并分组统计]
    D --> E[结束]

每月最后一个星期五日期确定的流程图:

graph TD
    A[开始] --> B[创建 TallyTable_CTE]
    B --> C[创建 LastDayOfMonth_CTE]
    C --> D[主查询:使用 CASE 语句计算最后星期五日期]
    D --> E[结束]

车辆库存时间分析的流程图:

graph TD
    A[开始] --> B[表连接]
    B --> C[时间计算]
    C --> D[筛选和排序]
    D --> E[结束]
10. 不同场景的应用案例拓展
  • 场景拓展:按周统计销售数据
    如果销售经理想了解 2017 年每周的销售情况,我们可以使用以下 SQL:
SELECT 
    WEEK(SaleDate) AS WeekNumber,
    SUM(SalePrice) AS TotalSales
FROM 
    salesbycountry
WHERE 
    YEAR(SaleDate) = 2017
GROUP BY 
    WEEK(SaleDate)
ORDER BY 
    WeekNumber;

操作步骤
1. 数据筛选 :从 salesbycountry 表中筛选出 2017 年的销售数据。
2. 周数提取 :使用 WEEK() 函数提取销售日期的周数。
3. 分组统计 :按照周数分组,使用 SUM() 函数统计每周的销售总额。
4. 结果排序 :按照周数排序。

  • 场景拓展:统计特定时间段内的销售高峰时段
    假设销售团队想知道 2017 年 6 月到 8 月之间,哪个时间段的销售最火爆。可以使用以下 SQL:
SELECT
    CONCAT(HOUR(SaleDate), '-', HOUR(SaleDate) + 1) AS HourBand,
    SUM(SalePrice) AS SalesByHourBand
FROM
    salesbycountry
WHERE
    YEAR(SaleDate) = 2017
    AND MONTH(SaleDate) BETWEEN 6 AND 8
GROUP BY
    HOUR(SaleDate)
ORDER BY
    SalesByHourBand DESC;

操作步骤
1. 数据筛选 :从 salesbycountry 表中筛选出 2017 年 6 月到 8 月的销售数据。
2. 小时数提取 :使用 HOUR() 函数提取销售时间的小时数。
3. 小时区间拼接 :使用 CONCAT() 函数将小时数拼接成小时区间。
4. 分组统计 :按照小时数分组,使用 SUM() 函数统计每个小时区间的销售金额。
5. 结果排序 :按照销售金额降序排序,找出销售高峰时段。

11. 总结与最佳实践

在处理日期和时间数据时,我们可以总结出以下最佳实践:
1. 函数选择 :根据具体的业务需求选择合适的日期和时间函数,如 TIMESTAMPDIFF() 用于计算时间间隔, DATE_FORMAT() 用于时间格式化, WEEK() MONTH() YEAR() 等用于提取日期的不同部分。
2. 表连接策略 :使用 LEFT JOIN 可以确保所有相关数据都被包含,即使某些记录没有对应的关联数据。在连接表时,使用 USING() 函数可以简化代码,但要注意字段名必须相同。
3. 数据筛选和排序 :使用 WHERE 子句进行数据筛选,确保只处理需要的数据。在排序时,根据具体需求选择升序或降序排序。
4. 代码优化 :将复杂的查询拆分成多个 CTE 或子查询,提高代码的可读性和可维护性。可以将整个查询包装在 CTE 或子查询中,按聚合金额排序,进行更深入的数据分析。
5. 数据类型注意事项 :确保使用的日期元素是 DATE TIMESTAMP 数据类型,或者是数据库可以解释为日期的数据。格式化后的时间数据是文本类型,不要用于排序或时间计算,应使用原始数据字段。

通过遵循这些最佳实践,我们可以更高效、准确地处理日期和时间数据,为业务决策提供有力支持。在实际应用中,不断积累经验,根据具体情况灵活运用这些技巧,能够更好地满足各种复杂的业务需求。

总之,日期和时间处理是 SQL 中的重要部分,掌握相关的函数和操作技巧,能够让我们在数据处理和分析中更加得心应手。无论是统计销售数据、分析库存时间,还是确定特定日期的业务情况,都可以通过合理运用 SQL 来实现。希望本文介绍的内容能够帮助你在实际工作中更好地处理日期和时间数据。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值