应优先用st_dwithin或预连接行政区划表实现地理分组;停留时长需按用户+区域+时间序用lag计算,再过滤噪声后聚合,同时务必建立空间和时间索引。

用 ST_DWithin 或地理围栏字段直接分组
直接按地理区域分组,前提是你的用户轨迹数据里已有明确的区域标识(比如 region_name、district_id),或者能通过空间函数动态判断归属。如果表里只有经纬度(lon, lat)和时间戳(event_time),就得先关联区域边界——别硬写多层子查询,优先用 ST_DWithin(PostGIS)或预连接行政区划表。
常见错误是把“停留时长”当成单条记录字段:实际上它得从连续定位点中计算间隔,不是现成的列。所以分组前必须先算出每段停留时长,再按区域聚合。
- 若区域是多边形表(如
districts),用ST_Contains(districts.geom, ST_Point(users.lon, users.lat))关联,注意 SRID 一致(通常用4326) - 若只是半径范围(如“距离某商场 500 米内”),用
ST_DWithin(geom, ST_Point(lon, lat), 500),单位是米(要求 geometry 列为地理坐标系) - 千万避免在
WHERE里对经纬度用BETWEEN模拟区域——球面距离偏差大,且无法利用空间索引
计算停留时长必须按用户+时空顺序排序
停留时长 = 下一点时间 − 当前点时间,但只对同一用户、且在同一个地理区域内连续的点才有效。跳区域、换用户、时间倒序都会让差值失效。
PostgreSQL 示例(假设已关联好 region_name):
SELECT region_name,
AVG(duration_sec) AS avg_stay_seconds
FROM (
SELECT region_name,
EXTRACT(EPOCH FROM (event_time - LAG(event_time) OVER (
PARTITION BY user_id, region_name ORDER BY event_time
))) AS duration_sec
FROM user_locations_with_region
) t
WHERE duration_sec > 0 AND duration_sec 1小时的“停留”大概率是断连)
GROUP BY region_name;
-
LAG()必须带PARTITION BY user_id, region_name,否则跨区域或跨用户取前一行,结果完全错乱 -
EXTRACT(EPOCH FROM ...)是 PostgreSQL 写法;MySQL 用TIMESTAMPDIFF(SECOND, prev_time, curr_time) - 原始数据时间精度要是
TIMESTAMP,别用DATE或字符串,否则LAG计算为 NULL
聚合前务必过滤掉“伪停留”噪声点
真实场景中,GPS 漂移、重复上报、后台保活心跳都会产生密集短时点,导致大量 duration_sec 在 1–5 秒之间,拉低平均值且无业务意义。
- 加条件
duration_sec >= 30才计入平均——这是多数线下场景的合理阈值 - 若设备上报频率不固定(如省电模式下 5 分钟一报),需结合业务定义“有效停留”:比如连续 3 个点都在同一区域,且首尾时间差 ≥ 120 秒
- 别依赖单次
ST_Contains结果定区域:用户可能刚跨过边界,两个相邻点分属不同区,这时该段时长不该计入任一区的停留——需要先做轨迹分段(is_new_segment标志位)
性能关键:空间索引 + 时间范围分区
没索引时,100 万轨迹点关联 1000 个行政区,空间判断可能跑十几分钟。别等慢了再优化。
- 在轨迹表的
lon/lat上建geography类型列并加GIST索引:ALTER TABLE user_locations ADD COLUMN geom GEOGRAPHY(POINT,4326); UPDATE user_locations SET geom = ST_SetSRID(ST_MakePoint(lon, lat),4326); CREATE INDEX idx_user_geom ON user_locations USING GIST(geom); - 时间字段(
event_time)单独建 B-tree 索引,WHERE 条件里加上event_time >= '2024-01-01'能显著减少扫描行数 - 如果数据按天分表(如
user_locations_202401),直接查对应分区表,比在总表上加时间条件快一个数量级
真正麻烦的不是 SQL 写法,而是“停留”的业务定义是否和空间判断逻辑对齐——比如商场中庭和停车场算同一区域吗?两点间穿过马路但 GPS 没采到,是否中断停留?这些得和业务方对齐,再反推 SQL 中的 PARTITION BY 和过滤条件。











