SQL查询 - 选择时间跨度之前的最后日期,其中时间跨度中不存在日期

时间:2015-01-15 10:01:42

标签: sql sql-server tsql

在我的表中,我有一个datetime列和一个标记列。如果在时间跨度内没有值,我想在时间跨度之前选择最新值。如果时间跨度中有值,我不想返回任何值。

我的查询具有以下输入参数:

@StartDate datetime
@EndDate datetime

最重要的查询结果:

  1. 如果@StartDate和@EndDate之间有数据
  2. ,查询不应返回任何结果
  3. 查询应该在@StartDate IF之前返回每个Tag的最新值,并且只有在@StartDate和@EndDate
  4. 之间没有结果时才会返回

    我的问题,我创建了两个查询:

    1. 返回时间跨度之前的最新结果。
    2. 返回时间跨度内的所有结果。
    3. 这个想法是SELECT [Values before the timespan] WHERE NOT EXISTS IN [Values in the timespan]

      我试图加入这些查询以获得最终结果,但这是我努力的地方。


      重播步骤(设置):

      CREATE TABLE dbo.MyTable(id int IDENTITY(1,1) NOT NULL, Tag nvarchar(200) NOT NULL, StartTime datetime NOT NULL)  
      DECLARE @day int, @month int, @year int
      
      SELECT @day = 15, @month = 1, @year = 2015
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MyTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MySuperTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      SELECT @day = 16, @month = 1, @year = 2015
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MyTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MySuperTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      SELECT @day = 18, @month = 1, @year = 2015
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MyTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MySuperTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      SELECT @day = 19, @month = 1, @year = 2015
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MyTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MySuperTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      SELECT @day = 26, @month = 1, @year = 2015
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MyTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      INSERT INTO dbo.MyTable(Tag, StartTime) VALUES('MySuperTag',dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1))
      

      再现的步骤(查询):
      这不应返回任何值,因为时间跨度中有值。

      DECLARE @day int, @month int, @year int
      DECLARE @StartTime datetime
      DECLARE @EndTime datetime
      SELECT @day = 17, @month = 1, @year = 2015
      SET @StartTime = dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1)
      SET @EndTime = dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1 + 3)
      
      SELECT * FROM (SELECT id, Tag, StartTime FROM dbo.MyTable WHERE StartTime < @StartTime AND Tag NOT IN ( SELECT Tag FROM dbo.MyTable WHERE (StartTime > @StartTime AND StartTime < @EndTime))) as d WHERE EXISTS ( SELECT Tag, StartTime, ROW_NUMBER FROM ( SELECT Tag, StartTime, ROW_NUMBER() OVER(PARTITION BY Tag ORDER BY StartTime DESC) AS ROW_NUMBER FROM dbo.MyTable WHERE StartTime < @StartTime) AS b WHERE ROW_NUMBER = '1')
      

      再现的步骤(QUERY2):
      这应该在时间跨度之前产生最新值,因为在时间跨度中没有值。

      SELECT @day = 21, @month = 1, @year = 2015
      SET @StartTime = dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1)
      SET @EndTime = dateadd(mm, (@year - 1900) * 12 + @month - 1 , @day - 1 + 3)
      
      SELECT * FROM (SELECT id, Tag, StartTime FROM dbo.MyTable WHERE StartTime < @StartTime AND Tag NOT IN ( SELECT Tag FROM dbo.MyTable WHERE (StartTime > @StartTime AND StartTime < @EndTime))) as d WHERE EXISTS ( SELECT Tag, StartTime, ROW_NUMBER FROM ( SELECT Tag, StartTime, ROW_NUMBER() OVER(PARTITION BY Tag ORDER BY StartTime DESC) AS ROW_NUMBER FROM dbo.MyTable WHERE StartTime < @StartTime) AS b WHERE ROW_NUMBER = '1')  
      

      编辑:在预期结果部分添加了“每个标签的最新值”。

3 个答案:

答案 0 :(得分:1)

以下是修改后问题的修订答案:

SELECT [A].* FROM [dbo].[MyTable] AS [A]
INNER JOIN (
  SELECT [Tag], MAX([StartTime]) AS [StartTime]
  FROM [dbo].[MyTable]
  WHERE [StartTime] < @StartTime
  GROUP BY [Tag]
) AS B ON ([A].[Tag] = [B].[Tag] AND [A].[StartTime] = [B].[StartTime])
WHERE
  [A].[StartTime] < @StartTime AND
  0 = (
    SELECT COUNT(*)
    FROM [dbo].[MyTable]
    WHERE [StartTime] BETWEEN @StartTime AND @EndTime
  )
;

已加入的子查询为@StartTime之前的每个标记计算最新日期并加入其自身,以便可以返回整行(使用id)。< / p>

答案 1 :(得分:0)

SELECT * 
FROM MyTable t
WHERE StartTime < @start
  AND id = 
    (SELECT TOP 1 mt.id FROM MyTable mt WHERE mt.Tag = t.Tag ORDER BY StartTime DESC)
  AND NOT EXISTS 
    (SELECT 1 FROM MyTable WHERE StartTime >= @start AND StartTime <= @end)
ORDER BY StartTime DESC;

答案 2 :(得分:0)

以下内容应大致为您所寻找:

SELECT TOP 1 *
FROM [dbo].[MyTable] 
WHERE
  [StartTime] < @StartTime AND
  0 = (
    SELECT COUNT(*)
    FROM [dbo].[MyTable]
    WHERE [StartTime] BETWEEN @StartTime AND @EndTime
  )
ORDER BY [StartTime] DESC;

0 = (SELECT COUNT(*) ...)@StartTime之间存在数据时,@EndTime位会导致查询不返回任何数据。然后,查询的其余部分只是选择@StartTime之前的第一行。

SQL Fiddle

0 = (SELECT COUNT(*) ...)EXISTS(SELECT 1 ...)的查询计划比较 Query Plan Comparison