3.6.8 查询哪个城市最偏僻,即离高速公路的距离最远,通过display函数在地图上展示最偏僻的城市和与其最近的高速公路,display函数要求查询结果模式至少包含gid,name和geom属性,其中高速公路的name为full_name(4分)
解题思路:
-
定义"偏僻":城市到最近高速公路的距离最远
-
两步查询:
-
第一步:找到距离高速公路最远的城市
-
第二步:找到离这个城市最近的高速公路(用于地图展示)
-
代码详解:
sql
# 查询最偏僻的城市
query1 = """
WITH city_highway_distances AS (
SELECT
c.gid,
c.name,
c.geom,
MIN(ST_Distance(c.geom::geography, h.geom::geography)) as min_distance
FROM uscities c, ushighways h
WHERE c.geom IS NOT NULL AND h.geom IS NOT NULL
GROUP BY c.gid, c.name, c.geom
)
SELECT
gid,
name,
geom,
min_distance
FROM city_highway_distances
ORDER BY min_distance DESC
LIMIT 1
"""
关键点解释:
-
**
WITH city_highway_distances AS (...):**创建公共表表达式,计算每个城市到所有高速公路的最小距离 -
**
ST_Distance(c.geom::geography, h.geom::geography):**将geometry转换为geography类型,计算真实地球距离(米) -
**
MIN(...):**对每个城市,找到到所有高速公路中的最小距离 -
GROUP BY
c.gid, c.name, c.geom:按城市分组,计算每个城市的最小距离 -
ORDER BY
min_distance DESC LIMIT 1:按距离降序排列,取最远的城市
sql
# 查询与最偏僻的城市最近的高速公路
query2 = """
WITH farthest_city AS (
WITH city_highway_distances AS (
SELECT
c.gid as city_gid,
c.name as city_name,
c.geom as city_geom,
h.gid as highway_gid,
h.full_name as highway_name,
h.geom as highway_geom,
ST_Distance(c.geom::geography, h.geom::geography) as distance
FROM uscities c, ushighways h
WHERE c.geom IS NOT NULL AND h.geom IS NOT NULL
),
min_distances AS (
SELECT
city_gid,
MIN(distance) as min_distance
FROM city_highway_distances
GROUP BY city_gid
)
SELECT
chd.city_gid,
chd.city_name,
chd.city_geom,
chd.highway_gid,
chd.highway_name,
chd.highway_geom,
chd.distance
FROM city_highway_distances chd
JOIN min_distances md ON chd.city_gid = md.city_gid AND chd.distance = md.min_distance
ORDER BY chd.distance DESC
LIMIT 1
)
SELECT
highway_gid as gid,
highway_name as name,
highway_geom as geom
FROM farthest_city
"""
嵌套WITH子句说明:
-
最内层:计算每个城市到每条高速公路的距离
-
中间层:找到每个城市的最小距离
-
最外层:通过JOIN找到对应最小距离的具体高速公路信息
* GROUP BY 详解
基本概念
GROUP BY 是 SQL 中用于对查询结果进行分组的关键字, 它通常与聚合函数(如 COUNT, SUM, AVG, MAX, MIN 等)一起使用。GROUP BY 语句将查询结果按照一个或多个列的值进行分组,然后对每个组应用聚合函数计算统计值。
基本语法
sql
SELECT column_name(s), aggregate_function(column_name)
FROM table_name
WHERE condition
GROUP BY column_name(s)
[ORDER BY column_name(s)];
详细说明
1. 单列分组
最常见的用法是只按一个列进行分组:
sql
SELECT department, COUNT(*) as employee_count
FROM employees
GROUP BY department;
这个查询会统计每个部门的员工数量。
2. 多列分组
可以同时按多个列进行分组:
sql
SELECT department, job_title, AVG(salary) as avg_salary
FROM employees
GROUP BY department, job_title;
这个查询会计算每个部门中每个职位的平均薪资。
3. 与聚合函数配合使用
GROUP BY 通常与以下聚合函数配合使用:
- COUNT(): 计算行数
- SUM(): 计算总和
- AVG(): 计算平均值
- MAX(): 获取最大值
- MIN(): 获取最小值
4. HAVING 子句
HAVING 子句用于过滤分组后的结果:
sql
SELECT department, COUNT(*) as employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;
这个查询只返回员工数超过5人的部门。
5. 执行顺序
在 SQL 查询中,GROUP BY 的执行顺序如下:
- FROM 子句
- WHERE 子句
- GROUP BY 子句
- HAVING 子句
- SELECT 子句
- ORDER BY 子句
实际应用场景
1. 销售数据分析
sql
SELECT product_category,
SUM(sales_amount) as total_sales,
AVG(sales_amount) as avg_sales,
COUNT(*) as transaction_count
FROM sales
WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY product_category
HAVING SUM(sales_amount) > 10000
ORDER BY total_sales DESC;
2. 用户行为分析
sql
SELECT user_segment,
COUNT(DISTINCT user_id) as unique_users,
AVG(session_duration) as avg_duration,
SUM(page_views) as total_views
FROM user_sessions
GROUP BY user_segment;
3. 库存管理
sql
SELECT warehouse_location,
product_type,
SUM(current_stock) as total_inventory,
MIN(last_restock_date) as oldest_stock
FROM inventory
GROUP BY warehouse_location, product_type;
注意事项
- SELECT 子句中出现的非聚合列必须包含在 GROUP BY 子句中
- WHERE 子句在分组前过滤行,而 HAVING 子句在分组后过滤组
- GROUP BY 的性能可能会受到数据量的影响,在大表上使用时要考虑索引优化
- 某些数据库支持 GROUPING SETS、CUBE 和 ROLLUP 等高级分组操作
* ORDER BY 基本语法
ORDER BY 子句用于对查询结果进行排序,可以按一个或多个列排序,支持升序(ASC)或降序(DESC)。默认排序方式为升序。
sql
SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...;
单列排序
按单个列排序,例如按 salary 降序排列:
sql
SELECT name, salary
FROM employees
ORDER BY salary DESC;
多列排序
可以同时按多个列排序,例如先按 department 升序,再按 salary 降序:
sql
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;
按表达式或函数排序
可以使用表达式或函数进行排序,例如按 name 长度排序:
sql
SELECT name, LENGTH(name) AS name_length
FROM employees
ORDER BY name_length;
按列别名排序
如果查询结果中包含别名列,可以直接使用别名排序:
sql
SELECT name, salary * 12 AS annual_salary
FROM employees
ORDER BY annual_salary DESC;
按列位置排序
可以按 SELECT 语句中的列序号排序(从 1 开始),例如按第 2 列(salary)排序:
sql
SELECT name, salary
FROM employees
ORDER BY 2 DESC;
NULL 值的处理
NULL 值在排序时默认被视为最小值(升序时排在最前,降序时排在最后)。可以使用 NULLS FIRST 或 NULLS LAST 调整 NULL 值的位置:
sql
SELECT name, commission
FROM employees
ORDER BY commission DESC NULLS LAST;
与 LIMIT 结合使用
ORDER BY 常与 LIMIT 配合使用,例如查询薪资最高的 5 名员工:
sql
SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 5;
在 JOIN 查询中使用
在多表 JOIN 查询时,ORDER BY 可以按任意表的列排序:
sql
SELECT e.name, d.department_name, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.id
ORDER BY d.department_name, e.salary DESC;
3.6.9 查询哪些高速公路穿越湖,列出高速公路及其在湖中的长度,按长度从长到短排列,通过display函数在地图上展示这些高速公路和湖,display函数要求查询结果模式至少包含gid,name和geom属性, 其中高速公路的hname为full_name(4分)
3.6.10 将交通事故与高速公路基于空间距离进行关联,即距离某高速公路小于200米,认为该交通事故发生在这条高速公路上,查询哪条高速公路上的交通事故最多?由于交通事故较多,完整的查询大约需要30分钟,可以使用ST_DWithin加速距离判断,同时仅考虑在10月发生的交通事故。通过display函数在地图上展示这些高速公路和其关联的交通事故,display函数要求查询结果模式至少包含gid,name和geom属性,其中高速公路的name为full_name。本题在台式机上查询时间约为3分钟(5分)
空间关联查询 :此类空间关联查询是数据挖掘中的常见方法,应用较为广泛,如道路与车辆关联分析道路拥堵情况?
3.6.11 导入加州cal的shapefile文件,修改空间参考系为4326,计算加州的经纬度范围,在该范围内,基于所给的网格生成代码,构建,即X方向50,Y方向46,网格与加州多边形求交,即边界上的方格仅保留与加州相交的部分,统计每个方格内的交通事故数目,通过choroplethMap进行可视化,choroplethMap函数要求查询结果模式至少包含gid,name,geom和value属性,其中name和value均为其内的交通事故数目,可以通过with语句简化SQL语句(4分)
空间网格关联查询 :此类空间网格关联查询在实际应用中较为常见,如滴滴和Uber基于六边形网格统计打车人数,进行实时调价,Uber Deck Hexagon layer (中文Deck.gl)
3.6.12 查询在加州范围内的交通事故,通过heatMap进行可视化,heatMap函数要求查询结果模式至少包含gid,name和geom属性,其中name可为任意值(2分)
数据可视化 :利用人眼的感知能力对数据进行交互的可视表达以增强认知的技术,是数据分析的有效手段,如Uber数据可视化
-
ST_NPoints(): 计算几何对象中的点数 -
ST_ConvexHull(): 计算几何对象的凸包 -
ST_NumInteriorRings(): 计算多边形中的内环数(岛的数量) -
ST_Area(): 计算几何对象的面积 -
ST_Length(): 计算几何对象的长度 -
ST_Centroid(): 计算几何对象的质心 -
ST_Distance(): 计算两个几何对象之间的距离 -
ST_Touches(): 判断两个几何对象是否接触