如何处理SQL中任意数量的间隔?
我在一个SQL数据库中有两个表。第一个路径是路径,保存路径(或轨迹)上的点。每个点都有一行 第二个表“Interval”列出了第一个表中表示的路径上的间隔。它们在某种程度上被认为是特殊的,例如,在路径的一部分中,对象移动不多 我们期望在路径上确定多个间隔 我想在如何处理SQL中任意数量的间隔?,sql,snowflake-cloud-data-platform,ansi-sql,Sql,Snowflake Cloud Data Platform,Ansi Sql,我在一个SQL数据库中有两个表。第一个路径是路径,保存路径(或轨迹)上的点。每个点都有一行 第二个表“Interval”列出了第一个表中表示的路径上的间隔。它们在某种程度上被认为是特殊的,例如,在路径的一部分中,对象移动不多 我们期望在路径上确定多个间隔 我想在path表中添加一个新列,其值表示路径的该部分是否在其中一个间隔内。我在下面的示例中给出了这个附加列的示例 如果只有一个间隔,我会使用 CASE WHEN p.time BETWEEN i.Start_Time AND i.End_Tim
path
表中添加一个新列,其值表示路径的该部分是否在其中一个间隔内。我在下面的示例中给出了这个附加列的示例
如果只有一个间隔,我会使用
CASE WHEN p.time BETWEEN i.Start_Time AND i.End_Time THEN True ELSE False END
我可以做些什么来处理任意数量的间隔
间隔时间:
| Interval ID | Start_Time | End_Time |
|-------------|------------|----------|
| 1 | 5 | 36 |
| 2 | 71 | 78 |
| 3 | 206 | 308 |
| ... | | |
下面是成功添加了所需“at_rest”列的路径表的示例
| time | x | y | at_rest |
|------|---|----|---------|
| 0 | 5 | 9 | 0 |
| 1 | 6 | 10 | 0 |
| 2 | 7 | 31 | 1 |
| 3 | 9 | 49 | 1 |
| ... | | | |
在不同的DBMS中,答案可能不同。
我认为这种逻辑是可行的,但是实现取决于您的环境。
我假设间隔没有重叠
在案例中,当存在和选择语句时,可以组合使用:
CASE WHEN EXISTS(
SELECT Interval_ID
FROM intervals
WHERE p.time BETWEEN Start_Time AND End_Time
) THEN (
SELECT Interval_ID
FROM intervals
WHERE p.time BETWEEN Start_Time AND End_Time
)
ELSE 0
我认为,拥有间隔_ID比只是一个是/否指示器更有意义。列atRest保留了您最初的Yes/No概念。此外,我向path表添加了更多数据以获得结果--
这需要转换为更新sql以替换现有值。结果是--
如果只需要一行带有true
/false
,请使用exists
。许多数据库直接支持布尔类型,因此您只需使用:
select . . .,
(exists (select 1
from intervals i
where p.time bertween i.start_time and i.end_time
)
) as flag
在其他数据库中,需要一个case
表达式:
select . . .,
(case when exists (select 1
from intervals i
where p.time bertween i.start_time and i.end_time
)
then 1 else 0
) as flag
我的同事和我提出的解决方案使用了可怕的交叉联接
,但我们可以预先将表过滤到几千行,这样就不会出现这样的问题
WITH interval_path AS (SELECT path.*, intervals.*
FROM path
CROSS JOIN intervals
WHERE path.time BETWEEN intervals.Start_Time AND intervals.End_Time)
SELECT path.time, path.x, path.y,
COALESCE(interval_path.in_cluster, 0) AS in_cluster
FROM path
LEFT JOIN interval_path ON interval_path.time = path.time
ORDER BY path.time
正如donPablo所指出的,这可以通过连接来解决(正如您所描述的):
WITH intervals AS (
SELECT *
FROM VALUES
(1,1,5,36)
,(1,2,71,78)
,(1,3,206,308)
v(unit_id, interval_id, start_time, end_time)
), paths AS (
SELECT *
FROM VALUES
(1,0,5,9 )
,(1,1,6,10)
,(1,2,7,31)
,(1,3,9,49)
,(1,4,9,48)
,(1,5,9,47)
,(1,6,9,46)
p(unit_id, time, x, y)
)
SELECT p.unit_id
,p.time
,p.x
,p.y
,i.unit_id is not null as atRest
FROM paths p
LEFT JOIN intervals i
ON p.unit_id = i.unit_id and p.time between i.start_time and i.end_time
order by 1,2;
给予:
UNIT_ID TIME X Y ATREST
1 0 5 9 FALSE
1 1 6 10 FALSE
1 2 7 31 FALSE
1 3 9 49 FALSE
1 4 9 48 FALSE
1 5 9 47 TRUE
1 6 9 46 TRUE
UNIT_ID TIME X Y ATREST
1 0 5 9 FALSE
1 1 6 10 FALSE
1 2 7 31 TRUE
1 3 9 49 TRUE
1 4 9 48 TRUE
1 5 9 47 TRUE
1 6 9 46 TRUE
请注意,您给出的示例间隔并不意味着您给出的第一个点处于“静止”状态,因为这些点不在间隔内
我还包括一个“单位id”,因为只有一个实体拥有大量数据是非常不可能的
现在,如果间隔数据重叠,则需要更改计数:
WITH intervals AS (
SELECT *
FROM VALUES
(1,1,2,4)
,(1,2,4,6)
,(1,3,206,308)
v(unit_id, interval_id, start_time, end_time)
), paths AS (
SELECT *
FROM VALUES
(1,0,5,9 )
,(1,1,6,10)
,(1,2,7,31)
,(1,3,9,49)
,(1,4,9,48)
,(1,5,9,47)
,(1,6,9,46)
p(unit_id, time, x, y)
)
SELECT p.unit_id
,p.time
,p.x
,p.y
,count(i.unit_id) > 0 as atRest
FROM paths p
LEFT JOIN intervals i
ON p.unit_id = i.unit_id and p.time between i.start_time and i.end_time
group by 1,2,3,4
order by 1,2;
给予:
UNIT_ID TIME X Y ATREST
1 0 5 9 FALSE
1 1 6 10 FALSE
1 2 7 31 FALSE
1 3 9 49 FALSE
1 4 9 48 FALSE
1 5 9 47 TRUE
1 6 9 46 TRUE
UNIT_ID TIME X Y ATREST
1 0 5 9 FALSE
1 1 6 10 FALSE
1 2 7 31 TRUE
1 3 9 49 TRUE
1 4 9 48 TRUE
1 5 9 47 TRUE
1 6 9 46 TRUE
如果您想要0
或1
作为雪花中的at_rest值,则交换到内联IFF是最紧凑的
,iff(count(i.unit_id) > 0,1,0) as atRest
path.at_rest是否是现有的旧列?还是你新专栏的名字?或者,您想为新列命名什么?用您正在使用的数据库标记您的问题。同时显示您想要的结果。@donPablo我在文本中添加了一些说明。“at_rest”列的意思是我们正在添加的新列。抱歉,这不清楚。不幸的是,在一个时间间隔内可能有大量行。@keith。那又怎么样?这提供了一个标志,表示给定行是否在任何间隔内。