如何进行这个SQL查询

时间:2011-10-22 12:02:39

标签: sql-server

我有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中进行查询。

5 个答案:

答案 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;