SQL 查询:每次特定参数获取特定值时,如何select 所有后续行
SQL query: How to select all following rows every time specific parameter acquires specific value
在我的数据库中,我有一个包含 3 列的 table:日期时间、参数、值。我的目标是获取数据的一个子集。我想获取参数 = 'B' 和值 > 5 之后的所有行(将此行包含在结果集中)。省略参数 = 'B' 和值 <=5 之后的所有行(将此行包含在结果集中)。
source table and expected result
换句话说,我想在参数为 B 的行中使用 VALUE 作为标志(伪代码):
include_flag = 0
result_set = empty table
for row in rows:
if parameter = B and value > 5:
result_set.append(row)
include_flag = 1
elif parameter = B and value <= 5:
result_set.append(row)
include_flag = 0
elif parameter <> B:
if include_flag = 1:
result_set.append(row)
elif include_flag = 0:
skip(row)
来源table:
date_time
parameter
value
1.9.2021 12:34:00
A
0.50
2.9.2021 14:01:00
B
7.40
2.9.2021 21:52:00
C
85.40
3.9.2021 3:15:00
B
3.80
4.9.2021 1:42:00
C
67.30
5.9.2021 12:34:00
A
0.3
6.9.2021 12:34:00
C
76.50
6.9.2021 17:22:00
A
0.40
6.9.2021 19:37:00
B
8.10
7.9.2021 12:34:00
C
91.70
7.9.2021 22:12:00
C
87.60
8.9.2021 7:17:00
A
0.60
9.9.2021 5:34:00
B
5.80
9.9.2021 12:34:00
B
4.90
10.9.2021 19:56:00
A
0.60
想要的结果集:
date_time
parameter
value
2.9.2021 14:01:00
B
7.40
2.9.2021 21:52:00
C
85.40
3.9.2021 3:15:00
B
3.80
6.9.2021 19:37:00
B
8.10
7.9.2021 12:34:00
C
91.70
7.9.2021 22:12:00
C
87.60
8.9.2021 7:17:00
A
0.60
9.9.2021 5:34:00
B
5.80
9.9.2021 12:34:00
B
4.90
此外,我对导致替代结果集的解决方案感兴趣,该结果集的行数与源 table 相同,但仅包含主要结果集中包含的那些值。
(因此(参数 = 'B' 和值 <=5)之后的行也包括在内,只是忽略了值)
- 解决方案有何不同?
备选结果集:
date_time
parameter
value
1.9.2021 12:34:00
A
NaN
2.9.2021 14:01:00
B
7.40
2.9.2021 21:52:00
C
85.40
3.9.2021 3:15:00
B
3.80
4.9.2021 1:42:00
C
NaN
5.9.2021 12:34:00
A
NaN
6.9.2021 12:34:00
C
NaN
6.9.2021 17:22:00
A
NaN
6.9.2021 19:37:00
B
8.10
7.9.2021 12:34:00
C
91.70
7.9.2021 22:12:00
C
87.60
8.9.2021 7:17:00
A
0.60
9.9.2021 5:34:00
B
5.80
9.9.2021 12:34:00
B
4.90
10.9.2021 19:56:00
A
NaN
- 这个问题的最佳解决方案是什么?
如有任何帮助,我们将不胜感激。
编辑:
查询:
SELECT t.parameter, t.date_time, t.value, B_over_limit = CASE
WHEN t.parameter = 'B' AND t.value > 5.0 THEN 1
WHEN t.parameter = 'B' AND t.value <= 5.0 THEN 0
ELSE null END
FROM table t ORDER BY t.date_time
带来:
date_time
parameter
value
B_over_limit
1.9.2021 12:34:00
A
0.50
null
2.9.2021 14:01:00
B
7.40
1
2.9.2021 21:52:00
C
85.40
null
3.9.2021 3:15:00
B
3.80
0
4.9.2021 1:42:00
C
67.30
null
5.9.2021 12:34:00
A
0.3
null
6.9.2021 12:34:00
C
76.50
null
6.9.2021 17:22:00
A
0.40
null
6.9.2021 19:37:00
B
8.10
1
7.9.2021 12:34:00
C
91.70
null
7.9.2021 22:12:00
C
87.60
null
8.9.2021 7:17:00
A
0.60
null
9.9.2021 5:34:00
B
5.80
1
9.9.2021 12:34:00
B
4.90
0
10.9.2021 19:56:00
A
0.60
null
- 用最后一个 0 或 1 填充所有空值几乎就是最终结果。我怎样才能做到这一点?
- 即使用前面的查询作为子查询 s,然后像 (?):
SELECT s.parameter, s.date_time, s.values,
LAST_VALUE(s.B_over_limit) OVER(...) FROM subquery s ORDER BY date_time
result of subquery and desired intermediate result
您需要有一个唯一的列 (id) 并使用“OUTER APPLY”以获得预期的结果。
由于您的示例数据没有唯一列,我创建了一个临时的 table 来创建唯一 ID 列并存储示例数据。
- 示例输入:
- 使用标识列和输入数据中的所有列创建临时 table。
- insert Input data into temp table order by date_time 维护数据顺序。
温度 table:
- 从此临时 table 编写查询以生成 B_over_limit 列并将其插入另一个临时 table 以供进一步使用。
- 现在用outer apply写一个查询,当NULL时获取列的前一个值得到最终结果。
查询:
select * from tb1 --sample source data
order by date_time
--create temp table #t1 (use original table datatypes)
create table #t1
(id int identity not null,
date_time varchar(50),
parameter char(1),
value float
)
insert into #t1 insert input into
select * from tb1 order by convert(datetime,date_time)
select * from #t1;
SELECT id, t.date_time, t.parameter, t.value,
B_over_limit = CASE WHEN t.parameter = 'B' AND t.value > 5.0 THEN 1
WHEN t.parameter = 'B' AND t.value <= 5.0 THEN 0
ELSE null
END
into #t2
FROM #t1 t ORDER BY convert(datetime,t.date_time)
select * from #t2
select a.date_time, a.parameter, a.value,
case when id = 1 and a.B_over_limit is NULL then 0
else ISNULL(a.B_over_limit, t.B_over_limit)
end as B_over_limit
from #t2 a
outer apply
(select top 1 B_over_limit from #t2 b
where b.id < a.id and b.date_time is not null
and b.parameter is not null and b.value is not null
and b.B_over_limit is not null
and a.B_over_limit is null
order by id desc ) t
在我的数据库中,我有一个包含 3 列的 table:日期时间、参数、值。我的目标是获取数据的一个子集。我想获取参数 = 'B' 和值 > 5 之后的所有行(将此行包含在结果集中)。省略参数 = 'B' 和值 <=5 之后的所有行(将此行包含在结果集中)。
source table and expected result
换句话说,我想在参数为 B 的行中使用 VALUE 作为标志(伪代码):
include_flag = 0
result_set = empty table
for row in rows:
if parameter = B and value > 5:
result_set.append(row)
include_flag = 1
elif parameter = B and value <= 5:
result_set.append(row)
include_flag = 0
elif parameter <> B:
if include_flag = 1:
result_set.append(row)
elif include_flag = 0:
skip(row)
来源table:
date_time | parameter | value |
---|---|---|
1.9.2021 12:34:00 | A | 0.50 |
2.9.2021 14:01:00 | B | 7.40 |
2.9.2021 21:52:00 | C | 85.40 |
3.9.2021 3:15:00 | B | 3.80 |
4.9.2021 1:42:00 | C | 67.30 |
5.9.2021 12:34:00 | A | 0.3 |
6.9.2021 12:34:00 | C | 76.50 |
6.9.2021 17:22:00 | A | 0.40 |
6.9.2021 19:37:00 | B | 8.10 |
7.9.2021 12:34:00 | C | 91.70 |
7.9.2021 22:12:00 | C | 87.60 |
8.9.2021 7:17:00 | A | 0.60 |
9.9.2021 5:34:00 | B | 5.80 |
9.9.2021 12:34:00 | B | 4.90 |
10.9.2021 19:56:00 | A | 0.60 |
想要的结果集:
date_time | parameter | value |
---|---|---|
2.9.2021 14:01:00 | B | 7.40 |
2.9.2021 21:52:00 | C | 85.40 |
3.9.2021 3:15:00 | B | 3.80 |
6.9.2021 19:37:00 | B | 8.10 |
7.9.2021 12:34:00 | C | 91.70 |
7.9.2021 22:12:00 | C | 87.60 |
8.9.2021 7:17:00 | A | 0.60 |
9.9.2021 5:34:00 | B | 5.80 |
9.9.2021 12:34:00 | B | 4.90 |
此外,我对导致替代结果集的解决方案感兴趣,该结果集的行数与源 table 相同,但仅包含主要结果集中包含的那些值。 (因此(参数 = 'B' 和值 <=5)之后的行也包括在内,只是忽略了值)
- 解决方案有何不同?
备选结果集:
date_time | parameter | value |
---|---|---|
1.9.2021 12:34:00 | A | NaN |
2.9.2021 14:01:00 | B | 7.40 |
2.9.2021 21:52:00 | C | 85.40 |
3.9.2021 3:15:00 | B | 3.80 |
4.9.2021 1:42:00 | C | NaN |
5.9.2021 12:34:00 | A | NaN |
6.9.2021 12:34:00 | C | NaN |
6.9.2021 17:22:00 | A | NaN |
6.9.2021 19:37:00 | B | 8.10 |
7.9.2021 12:34:00 | C | 91.70 |
7.9.2021 22:12:00 | C | 87.60 |
8.9.2021 7:17:00 | A | 0.60 |
9.9.2021 5:34:00 | B | 5.80 |
9.9.2021 12:34:00 | B | 4.90 |
10.9.2021 19:56:00 | A | NaN |
- 这个问题的最佳解决方案是什么?
如有任何帮助,我们将不胜感激。
编辑:
查询:
SELECT t.parameter, t.date_time, t.value, B_over_limit = CASE
WHEN t.parameter = 'B' AND t.value > 5.0 THEN 1
WHEN t.parameter = 'B' AND t.value <= 5.0 THEN 0
ELSE null END
FROM table t ORDER BY t.date_time
带来:
date_time | parameter | value | B_over_limit |
---|---|---|---|
1.9.2021 12:34:00 | A | 0.50 | null |
2.9.2021 14:01:00 | B | 7.40 | 1 |
2.9.2021 21:52:00 | C | 85.40 | null |
3.9.2021 3:15:00 | B | 3.80 | 0 |
4.9.2021 1:42:00 | C | 67.30 | null |
5.9.2021 12:34:00 | A | 0.3 | null |
6.9.2021 12:34:00 | C | 76.50 | null |
6.9.2021 17:22:00 | A | 0.40 | null |
6.9.2021 19:37:00 | B | 8.10 | 1 |
7.9.2021 12:34:00 | C | 91.70 | null |
7.9.2021 22:12:00 | C | 87.60 | null |
8.9.2021 7:17:00 | A | 0.60 | null |
9.9.2021 5:34:00 | B | 5.80 | 1 |
9.9.2021 12:34:00 | B | 4.90 | 0 |
10.9.2021 19:56:00 | A | 0.60 | null |
- 用最后一个 0 或 1 填充所有空值几乎就是最终结果。我怎样才能做到这一点?
- 即使用前面的查询作为子查询 s,然后像 (?):
SELECT s.parameter, s.date_time, s.values,
LAST_VALUE(s.B_over_limit) OVER(...) FROM subquery s ORDER BY date_time
result of subquery and desired intermediate result
您需要有一个唯一的列 (id) 并使用“OUTER APPLY”以获得预期的结果。
由于您的示例数据没有唯一列,我创建了一个临时的 table 来创建唯一 ID 列并存储示例数据。
- 示例输入:
- 使用标识列和输入数据中的所有列创建临时 table。
- insert Input data into temp table order by date_time 维护数据顺序。
温度 table:
- 从此临时 table 编写查询以生成 B_over_limit 列并将其插入另一个临时 table 以供进一步使用。
- 现在用outer apply写一个查询,当NULL时获取列的前一个值得到最终结果。
查询:
select * from tb1 --sample source data
order by date_time
--create temp table #t1 (use original table datatypes)
create table #t1
(id int identity not null,
date_time varchar(50),
parameter char(1),
value float
)
insert into #t1 insert input into
select * from tb1 order by convert(datetime,date_time)
select * from #t1;
SELECT id, t.date_time, t.parameter, t.value,
B_over_limit = CASE WHEN t.parameter = 'B' AND t.value > 5.0 THEN 1
WHEN t.parameter = 'B' AND t.value <= 5.0 THEN 0
ELSE null
END
into #t2
FROM #t1 t ORDER BY convert(datetime,t.date_time)
select * from #t2
select a.date_time, a.parameter, a.value,
case when id = 1 and a.B_over_limit is NULL then 0
else ISNULL(a.B_over_limit, t.B_over_limit)
end as B_over_limit
from #t2 a
outer apply
(select top 1 B_over_limit from #t2 b
where b.id < a.id and b.date_time is not null
and b.parameter is not null and b.value is not null
and b.B_over_limit is not null
and a.B_over_limit is null
order by id desc ) t