我有2个具有以下结构的SQL Server表
Turns-time
cod_turn (PrimaryKey)
time (datetime)
Taken turns
cod_taken_turn (Primary Key)
cod_turn
...
和其他几个与问题无关的字段。我无法改变桌面结构,因为该应用程序是由其他人制作的。
给出一个数值变量参数,我们假设这个例子为“3”,并且给定时间,我需要创建一个从那个时间开始查看的查询,它看起来是时间的前3个连续记录未被标记为“已被拍摄”。例如:
例如,对于这些转弯,从用户选择的“8:00”时间开始
8:00 (not taken)
9:00 (not taken)
10:00 (taken)
11:00 (not taken)
12:00 (not taken)
13:00 (not taken)
14:00 (taken)
它必须列出的查询
11:00
12:00
13:00
如果可能的话,我无法弄清楚如何在纯sql中进行查询。
答案 0 :(得分:3)
带光标
declare @GivenTime datetime,
@GivenSequence int;
select @GivenTime = cast('08:00' as datetime),
@GivenSequence = 3;
declare @sequence int,
@code_turn int,
@time datetime,
@taked int,
@firstTimeInSequence datetime;
set @sequence = 0;
declare turnCursor cursor FAST_FORWARD for
select turn.cod_turn, turn.[time], taken.cod_taken_turn
from [Turns-time] as turn
left join [Taken turns] as taken on turn.cod_turn = taken.cod_turn
where turn.[time] >= @GivenTime
order by turn.[time] asc;
open turnCursor;
fetch next from turnCursor into @code_turn, @time, @taked;
while @@fetch_status = 0 AND @sequence < @GivenSequence
begin
if @taked IS NULL
select @firstTimeInSequence = coalesce(@firstTimeInSequence, @time)
,@sequence = @sequence + 1;
else
select @sequence = 0,
@firstTimeInSequence = null;
fetch next from turnCursor into @code_turn, @time, @taked;
end
close turnCursor;
deallocate turnCursor;
if @sequence = @GivenSequence
select top (@GivenSequence) * from [Turns-time] where [time] >= @firstTimeInSequence
order by [time] asc
答案 1 :(得分:2)
我认为没有一种简单的方法可以达到这个目的。
但可能有很多复杂的方法:)。这是一种应该在Transact-SQL中使用的方法:
CREATE TABLE #CONSECUTIVE_TURNS (id int identity, time datetime, consecutive int)
INSERT INTO #CONSECUTIVE_TURNS (time, consecutive, 0)
SELECT cod_turn
, time
, 0
FROM Turns-time
ORDER BY time
DECLARE @i int
@n int
SET @i = 0
SET @n = 3 -- Number of consecutive not taken records
while (@i < @n) begin
UPDATE #CONSECUTIVE_TURNS
SET consecutive = consecutive + 1
WHERE not exists (SELECT 1
FROM Taken-turns
WHERE id = cod_turn + @i
)
SET @i = @i + 1
end
DECLARE @firstElement int
SELECT @firstElement = min(id)
FROM #CONSECUTIVE_TURNS
WHERE consecutive >= @n
SELECT *
FROM #CONSECUTIVE_TURNS
WHERE id between @firstElement
and @firstElement + @n - 1
这是未经测试的,但我认为它会起作用。
答案 2 :(得分:2)
WITH Base AS (
SELECT *,
CASE WHEN EXISTS(
SELECT *
FROM Taken_turns taken
WHERE taken.cod_turn = turns.cod_turn) THEN 1 ELSE 0 END AS taken
FROM [Turns-time] turns)
, RecursiveCTE As (
SELECT TOP 1 cod_turn, [time], taken AS run, 0 AS grp
FROM Base
WHERE [time] >= @start_time
ORDER BY [time]
UNION ALL
SELECT R.cod_turn, R.[time], R.run, R.grp
FROM (
SELECT T.*,
CASE WHEN T.taken = 0 THEN 0 ELSE run+1 END AS run,
CASE WHEN T.taken = 0 THEN grp + 1 ELSE grp END AS grp,
rn = ROW_NUMBER() OVER (ORDER BY T.[time])
FROM Base T
JOIN RecursiveCTE R
ON R.[time] < T.[time]
) R
WHERE R.rn = 1 AND run < @run_length
), T AS(
SELECT *,
MAX(grp) OVER () AS FinalGroup,
COUNT(*) OVER (PARTITION BY grp) AS group_size
FROM RecursiveCTE
)
SELECT cod_turn,time
FROM T
WHERE grp=FinalGroup AND group_size=@run_length
答案 3 :(得分:1)
Pure SQL
SELECT TOP 3 time FROM [turns-time] WHERE time >= (
-- get first result of the 3 consecutive results
SELECT TOP 1 time AS first_result
FROM [turns-time] tt
-- start from given time, which is 8:00 in this case
WHERE time >= '08:00'
-- turn is not taken
AND cod_turn NOT IN (SELECT cod_turn FROM taken_turns)
-- 3 consecutive turns from current turn are not taken
AND (
SELECT COUNT(*) FROM
(
SELECT TOP 3 cod_turn AS selected_turn FROM [turns-time] tt2 WHERE tt2.time >= tt.time
GROUP BY cod_turn ORDER BY tt2.time
) AS temp
WHERE selected_turn NOT IN (SELECT cod_turn FROM taken_turns)) = 3
) ORDER BY time
注意:我在Postgresql上测试了它(有一些代码修改),但不是MS SQL Server。与T-SQL相比,我不确定性能。
答案 4 :(得分:0)
另一种基于集合的解决方案(已测试):
DECLARE @Results TABLE
(
cod_turn INT NOT NULL
,[status] TINYINT NOT NULL
,RowNumber INT PRIMARY KEY
);
INSERT @Results (cod_turn, [status], RowNumber)
SELECT a.cod_turn
,CASE WHEN b.cod_turn IS NULL THEN 1 ELSE 0 END [status] --1=(not taken), 0=(taken)
,ROW_NUMBER() OVER(ORDER BY a.[time]) AS RowNumber
FROM [Turns-time] a
LEFT JOIN [Taken_turns] b ON a.cod_turn = b.cod_turn
WHERE a.[time] >= @Start;
--SELECT * FROM @Results r ORDER BY r.RowNumber;
SELECT *
FROM
(
SELECT TOP(1) ca.LastRowNumber
FROM @Results a
CROSS APPLY
(
SELECT SUM(c.status) CountNotTaken, MAX(c.RowNumber) LastRowNumber
FROM
(
SELECT TOP(@Len)
b.RowNumber, b.[status]
FROM @Results b
WHERE b.RowNumber <= a.RowNumber
ORDER BY b.RowNumber DESC
) c
) ca
WHERE ca.CountNotTaken = @Len
ORDER BY a.RowNumber ASC
) x INNER JOIN @Results y ON x.LastRowNumber - @Len + 1 <= y.RowNumber AND y.RowNumber <= x.LastRowNumber;