如何创建存储过程以组合多个条件结果

时间:2015-07-03 08:54:58

标签: sql sql-server stored-procedures

这是表结构:

TABLE TestTable 
(
       [Id] [uniqueidentifier] NOT NULL, 
       [Quality] [tinyint] NULL,
       [UtcTimeStamp] [datetime2](7) NOT NULL, 
       [Value] [varbinary](max) NULL
)

我想要达到的目标是:对于from的指定时间范围(to - UtcTimeStamp),首先我将查询位于该时间范围内的所有数据。然后,继续在此规则后的to时间范围内查询数据:

  • R0:Quality有三个值:1,2,3
  • R1:如果Quality之后的第一个数据to为3,则只需将此一个查询结果与上述范围结果合并。
  • R2:如果Quality之后的第一个数据to为2,则继续查询UtcTimeStamp为3的下一行(Quality之后)。最后,结合这两个查询结果具有上述范围的结果。
  • R3:如果Quality之后的第一个数据to为1,则继续查询UtcTimeStamp为2和3的下一行(此Quality之后) ,将这三个查询结果与上述范围结果相结合。

以上所有作业都包含在一个存储过程中,并且尽可能具有性能。

我对存储过程语法不是很熟悉,并试图存储一些中间变量并使用IF ELSE语法来组合所有结果,只是未能使句子有效。

已更新

以下是演示内容:

对于R1:

像这样的数据库

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  3       0x...
1s-..    6                  2       0x...

如果我希望查询1到4的时间范围,那么结果应为:

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  3       0x...

对于R2:

像这样的数据库

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  2       0x...
1s-..    6                  2       0x...
1s-..    7                  1       0x...
1s-..    8                  3       0x...

如果我希望查询1到4的时间范围,那么结果应为:

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  2       0x...
1s-..    8                  3       0x...

对于R3:

像这样的数据库

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  1       0x...
1s-..    6                  2       0x...
1s-..    7                  1       0x...
1s-..    8                  3       0x...

如果我希望查询1到4的时间范围,那么结果应为:

Id(fake) UtcTimeStamp(fake) Quality Value
1s-..    1                  1       0x...
1s-..    2                  2       0x...
1s-..    3                  2       0x...
1s-..    4                  3       0x...
1s-..    5                  1       0x...
1s-..    6                  2       0x...
1s-..    8                  3       0x...

程序应该是这样的:

CREATE PROCEDURE [ProcedureName] @Id as uniqueidentifier, @StartTime as datetime2, @EndTime as datetime2 AS ...

2 个答案:

答案 0 :(得分:0)

您可以尝试以下操作。我只是尝试先生成一些演示数据:

-- Create demo data
CREATE TABLE dbo.temp
(
       [Id] [uniqueidentifier] NOT NULL, 
       [Quality] [tinyint] NULL,
       [UtcTimeStamp] [datetime2](7) NOT NULL, 
       [Value] [varbinary](max) NULL
)

INSERT INTO dbo.temp(id, quality,UtcTimeStamp, value)
SELECT NEWID() as id, NTILE(3) OVER(ORDER BY object_id) as quality, 
    DATEADD(day,-NTILE(3) OVER(ORDER BY object_id),GETUTCDATE()) as UtcTimeStamp, 
    HASHBYTES(N'SHA1',CONVERT(nvarchar(36),NEWID())) as value
FROM sys.all_objects

您可以尝试此程序来获取所有内容:

-- Doing the stuff inside the proc
CREATE PROCEDURE dbo.yourProcedure 
    @from datetime2, @to datetime2
AS BEGIN

    ;WITH cte AS(
        -- Prepare data base for the second part of the query
        SELECT t.id, t.Quality, t.UtcTimeStamp, t.Value, ROW_NUMBER() OVER(order by t.UtcTimeStamp) as rn
        FROM dbo.temp as t
        WHERE t.UtcTimeStamp > @to
    )
    -- Get all data based inside the range of @from and @to
    SELECT t.id, t.Quality, t.UtcTimeStamp, t.Value 
    FROM dbo.temp as t
    WHERE t.UtcTimeStamp BETWEEN @from AND @to
    UNION ALL -- You can use union all, as you the following records won't already be in the above result set
    -- Add all rows which are behind the @to date (prepared in the cte)
    SELECT t.id, t.Quality, t.UtcTimeStamp, t.Value 
    FROM cte as t
    WHERE (t.Quality = 3 AND t.rn = 1) -- The first row, if it's quality 3
        -- All rows until quality 3, if the first row is quality 2
        OR ((t.quality = 2 and t.rn = 1) AND (t.rn <= (SELECT TOP 1 rn FROM cte WHERE quality = 3 ORDER By rn)))
        -- All rows until quality 2 or 3, if the first row is quality 1
        OR ((t.quality = 1 and t.rn = 1) AND (t.rn <= (SELECT TOP 1 rn FROM cte WHERE quality IN(2,3) ORDER By rn)))

END
GO

然后调用结果:

DECLARE @from datetime2 = DATEADD(day,-3,GETUTCDATE()), @to datetime2 = DATEADD(day,-2,GETUTCDATE())
SELECT @from, @to, * FROM dbo.temp

EXEC dbo.yourProcedure @from = @from, @to = @to
GO

之后不要忘记清理。

-- Cleanup
DROP TABLE dbo.temp

答案 1 :(得分:0)

好的,这是我的解决方案:

CREATE PROCEDURE [ProcedureName] @Id as uniqueidentifier, @StartTime as datetime2, @EndTime as datetime2 
AS BEGIN
    DECLARE @NextRow TABLE 
    (
        [Id] [uniqueidentifier] NOT NULL, 
        [Quality] [tinyint] NULL,
        [UtcTimeStamp] [datetime2](7) NOT NULL, 
        [Value] [varbinary](max) NULL
    )

    DECLARE @BoundRows TABLE 
    (
        [Id] [uniqueidentifier] NOT NULL, 
        [Quality] [tinyint] NULL,
        [UtcTimeStamp] [datetime2](7) NOT NULL, 
        [Value] [varbinary](max) NULL
    )

    INSERT INTO @NextRow SELECT top 1 * from [JerryTest].[dbo].[TestTable]
        where Id = @Id
        AND UtcTimeStamp > @EndTime

    IF((SELECT TOP 1 Quality FROM @NextRow) = 3)
        begin
        INSERT INTO @BoundRows SELECT * FROM @NextRow
        end
    ELSE IF((SELECT TOP 1 Quality FROM @NextRow) = 2)
        begin
        INSERT INTO @BoundRows SELECT * FROM @NextRow
        INSERT INTo @BoundRows SELECT TOP 1 * FROM [JerryTest].[dbo].[TestTable] 
            WHERE Id = @Id 
            AND UtcTimeStamp > @EndTime
            AND Quality = 3
        end
    ELSE IF((SELECT TOP 1 Quality FROM @NextRow) = 1)
        begin
        INSERT INTO @BoundRows SELECT * FROM @NextRow
        INSERT INTO @BoundRows SELECT TOP 1 * FROM [JerryTest].[dbo].[TestTable]
            WHERE Id = @Id 
            AND UtcTimeStamp > @EndTime  
            AND Quality = 2
        INSERT INTO @BoundRows SELECT TOP 1 * FROM [JerryTest].[dbo].[TestTable] 
            WHERE Id = @Id 
            AND UtcTimeStamp > @EndTime
            AND Quality = 3
        end

    (SELECT * FROM [JerryTest].[dbo].[TestTable] 
        WHERE Id = @Id 
        AND UtcTimeStamp >= @StartTime 
        AND UtcTimeStamp <= @EndTime)
    UNION ALL
    SELECT * FROM @BoundRows
END

希望这可以帮到你。