地理空间数据库作业笔记——酒驾交通事故分析

3.7 酒驾交通事故分析

美国交通局:We' re directly soliciting your help to better understand what these data are telling us. Whether you' re a non-profit, a tech company, or just a curious citizen wanting to contribute to the conversation in your local community, we want you to jump in and help us understand what the data are telling us. Some key questions worth exploring:

  • How might improving economic conditions around the country change how Americans are getting around? What models can we develop to identify communities that might be at a higher risk for fatal crashes?
  • How might climate change increase the risk of fatal crashes in a community?
  • How might we use studies of attitudes toward speeding, distracted driving, and seat belt use to better target marketing and behavioral change campaigns?
  • How might we monitor public health indicators and behavior risk indicators to target communities that might have a high prevalence of behaviors linked with fatal crashes (drinking, drug use/addiction, etc.)? What countermeasures should we create to address these issues?

美国交通局在2018年开展了Visualize Transportation Safety可视化挑战赛。

酒驾(drunk_dr > 0)是否在周末更容易发生?构造SQL语句查询工作日平均每日酒驾事件数与周末平均每日酒驾事件数(avg_weekday_count, avg_weekend_count),保留到小数点后4位,分析查询结果给出结论,注意中美文化差异中星期起始日的差别(3分)

解法一:多级CTE方法

sql

复制代码
WITH drunk_accidents AS (
    SELECT 
        day_week,
        COUNT(*) as daily_count
    FROM usaccidents 
    WHERE drunk_dr > 0
    GROUP BY day_week
),
weekday_weekend AS (
    SELECT 
        CASE 
            WHEN day_week IN (1, 2, 3, 4, 5) THEN 'weekday'
            WHEN day_week IN (6, 7) THEN 'weekend'
        END as day_type,
        daily_count
    FROM drunk_accidents
),
summary AS (
    SELECT 
        day_type,
        AVG(daily_count) as avg_daily_count
    FROM weekday_weekend
    GROUP BY day_type
)
SELECT 
    ROUND(AVG(CASE WHEN day_type = 'weekday' THEN avg_daily_count END)::numeric, 4) as avg_weekday_count,
    ROUND(AVG(CASE WHEN day_type = 'weekend' THEN avg_daily_count END)::numeric, 4) as avg_weekend_count
FROM summary;

详细执行步骤分析:

第一步:drunk_accidents CTE

sql

复制代码
SELECT day_week, COUNT(*) as daily_count
FROM usaccidents 
WHERE drunk_dr > 0
GROUP BY day_week
  • 作用:统计每天的酒驾事故总数

  • 输出示例

text

复制代码
day_week | daily_count
---------|-------------
   1     |    150
   2     |    120
   3     |    110
   4     |    115
   5     |    125
   6     |    180
   7     |    200

第二步:weekday_weekend CTE

sql

复制代码
SELECT 
    CASE 
        WHEN day_week IN (1, 2, 3, 4, 5) THEN 'weekday'
        WHEN day_week IN (6, 7) THEN 'weekend'
    END as day_type,
    daily_count
FROM drunk_accidents
  • 作用:将每天数据分类为工作日/周末

  • 输出示例

text

复制代码
day_type | daily_count
---------|-------------
weekday  |    150  (周日)
weekday  |    120  (周一)
weekday  |    110  (周二)
weekday  |    115  (周三)
weekday  |    125  (周四)
weekend  |    180  (周五)
weekend  |    200  (周六)

第三步:summary CTE

sql

复制代码
SELECT day_type, AVG(daily_count) as avg_daily_count
FROM weekday_weekend
GROUP BY day_type
  • 作用:计算工作日和周末的平均值

  • 输出示例

text

复制代码
day_type | avg_daily_count
---------|-----------------
weekday  |     124.0
weekend  |     190.0

第四步:最终SELECT

sql

复制代码
SELECT 
    ROUND(AVG(CASE WHEN day_type = 'weekday' THEN avg_daily_count END)::numeric, 4) as avg_weekday_count,
    ROUND(AVG(CASE WHEN day_type = 'weekend' THEN avg_daily_count END)::numeric, 4) as avg_weekend_count
FROM summary
  • 问题 :这里使用了不必要的AVG()函数

  • 应该改为

sql

复制代码
SELECT 
    ROUND(MAX(CASE WHEN day_type = 'weekday' THEN avg_daily_count END)::numeric, 4) as avg_weekday_count,
    ROUND(MAX(CASE WHEN day_type = 'weekend' THEN avg_daily_count END)::numeric, 4) as avg_weekend_count
FROM summary

解法二:简洁版本

sql

复制代码
SELECT 
    ROUND(AVG(CASE WHEN day_week IN (1,2,3,4,5) THEN daily_count END)::numeric, 4) as avg_weekday_count,
    ROUND(AVG(CASE WHEN day_week IN (6,7) THEN daily_count END)::numeric, 4) as avg_weekend_count
FROM (
    SELECT 
        day_week,
        COUNT(*) as daily_count
    FROM usaccidents 
    WHERE drunk_dr > 0
    GROUP BY day_week
) as daily_counts;

详细执行步骤分析:

子查询:daily_counts

sql

复制代码
SELECT day_week, COUNT(*) as daily_count
FROM usaccidents 
WHERE drunk_dr > 0
GROUP BY day_week
  • 与解法一的第一步完全相同

  • 输出同样的每日酒驾事故统计

主查询:条件平均值计算

sql

复制代码
ROUND(AVG(CASE WHEN day_week IN (1,2,3,4,5) THEN daily_count END)::numeric, 4)
  • 执行逻辑

    1. 对于每一行数据,检查day_week是否在1-5(工作日)

    2. 如果是工作日,取daily_count值;否则返回NULL

    3. AVG()函数自动忽略NULL值,只计算工作日的平均值

    4. ROUND(...::numeric, 4)进行四舍五入

两种解法的对比分析

解法一的问题

  1. 逻辑错误 :在最后一步错误地使用了AVG()而不是MAX()

  2. 过度复杂:使用了不必要的多级CTE

  3. 效率较低:需要多次中间结果传递

解法二的优势

  1. 逻辑正确:直接使用条件平均值计算

  2. 代码简洁:减少了不必要的中间步骤

  3. 执行高效:数据库优化器可以更好地优化单层查询

关于星期定义的说明:

两种解法都使用了相同的星期定义:

  • 周日=1, 周一=2, 周二=3, 周三=4, 周四=5, 周五=6, 周六=7

  • 工作日:1,2,3,4,5(周日到周四)

  • 周末:6,7(周五、周六)

修正后的最优解法:

sql

复制代码
SELECT 
    ROUND(AVG(CASE WHEN day_week IN (1,2,3,4,5) THEN daily_count END)::numeric, 4) as avg_weekday_count,
    ROUND(AVG(CASE WHEN day_week IN (6,7) THEN daily_count END)::numeric, 4) as avg_weekend_count
FROM (
    SELECT day_week, COUNT(*) as daily_count
    FROM usaccidents 
    WHERE drunk_dr > 0
    GROUP BY day_week
) as daily_counts;

结论:解法二不仅更简洁,而且逻辑正确、执行效率更高,是更好的解决方案。

ROUND函数的基本概念

ROUND函数是一种常见的数学函数,用于对数字进行四舍五入操作。它广泛应用于Excel、SQL、Python等多种编程语言和工具中,主要功能是根据指定的小数位数对数值进行精确舍入。

语法格式

在大多数场景中,ROUND函数的语法如下:

复制代码
ROUND(number, digits)
  • number:需要舍入的原始数值。
  • digits :指定保留的小数位数。
    • 若为正数,表示舍入到小数点右侧的位数。
    • 若为负数,表示舍入到小数点左侧的位数(如整数位、十位等)。
    • 若为0,表示舍入到最接近的整数。

使用示例

  1. 保留两位小数
    ROUND(3.14159, 2) 返回结果为 3.14

  2. 舍入到整数
    ROUND(3.6, 0) 返回结果为 4

  3. 负数位数舍入
    ROUND(1234, -2) 返回结果为 1200(舍入到百位)。

注意事项

  • 当舍入位数的后一位数字为5时,不同语言或工具可能采用不同的规则(如"银行家舍入法"或"四舍五入")。
  • 在Excel中,ROUND函数严格遵循四舍五入规则;而在Python的round()函数中,对中间值(如0.5)的处理可能依赖具体实现。

跨平台差异

  • Excel/Google Sheets:直接支持ROUND函数,行为一致。
  • SQL :语法类似,如MySQL的ROUND(3.1415, 2)
  • Python :通过内置函数round()实现,但需注意浮点数精度问题。

代码逐行解析

第一行:

sql 复制代码
SELECT ROUND(AVG(CASE WHEN day_week IN (1,2,3,4,5) THEN daily_count END)::numeric, 4) as avg_weekday_count,
  • SELECT:开始一个查询语句。
  • ROUND(x, 4):将数值x四舍五入到4位小数。
  • AVG():计算平均值。
  • CASE WHEN day_week IN (1,2,3,4,5) THEN daily_count END:条件判断,若day_week为工作日(1-5),则取daily_count的值,否则返回NULL
  • ::numeric:将结果转换为numeric类型以确保精确计算。
  • as avg_weekday_count:将结果列命名为avg_weekday_count

第二行:

sql 复制代码
ROUND(AVG(CASE WHEN day_week IN (6,7) THEN daily_count END)::numeric, 4) as avg_weekend_count
  • 逻辑与第一行类似,但条件改为day_week IN (6,7)(周末),结果列命名为avg_weekend_count

第三行:

sql 复制代码
FROM (
  • 开始子查询,作为主查询的数据来源。

第四行:

sql 复制代码
SELECT day_week, COUNT(*) as daily_count
  • SELECT day_week:选择day_week列。
  • COUNT(*):统计每组的行数。
  • as daily_count:将统计结果命名为daily_count

第五行:

sql 复制代码
FROM usaccidents
  • 从表usaccidents中获取数据。

第六行:

sql 复制代码
WHERE drunk_dr > 0
  • 筛选条件:仅选择drunk_dr大于0的记录(涉及酒驾的事故)。

第七行:

sql 复制代码
GROUP BY day_week
  • day_week分组,每组对应一周中的某一天(1-7)。

第八行:

sql 复制代码
) as daily_counts;
  • 结束子查询,并将其命名为daily_counts

功能总结

  1. 子查询:统计表usaccidents中每天(day_week)的酒驾事故数量(daily_count)。
  2. 主查询:分别计算工作日(1-5)和周末(6-7)的平均事故数量,并四舍五入到4位小数。
  3. 最终输出两列:avg_weekday_count(工作日平均事故数)和avg_weekend_count(周末平均事故数)。
相关推荐
2301_764441333 小时前
身份证校验工具
前端·python·1024程序员节
苏小瀚3 小时前
[MySQL] JDBC
1024程序员节
Pluchon3 小时前
硅基计划5.0 MySQL 陆 视图&JDBC编程&用户权限控制
数据库·mysql·1024程序员节
VT.馒头3 小时前
【力扣】2725. 间隔取消
javascript·leetcode·1024程序员节
vistaup4 小时前
Android 基于清单文件mate-data数据共享
1024程序员节
百锦再4 小时前
Python、Java与Go:AI大模型时代的语言抉择
java·前端·vue.js·人工智能·python·go·1024程序员节
笑对人生任我行4 小时前
ORM 使用说明
1024程序员节
杨筱毅5 小时前
【底层机制】【Android】【面试】Zygote 为什么使用 Socket 而不是 Binder?
android·1024程序员节·底层机制
阿部多瑞 ABU5 小时前
# AI高精度提示词生成项目——3D-VR 课件—— 最终仓库级 AI 提示词:生成《EduVR Studio》—— 专业级 3D-VR 课件创作平台
gitee·开源·github·aigc·ai编程·1024程序员节