SQL 查询 - Select 时间跨度之前的最后一个日期,其中时间跨度中不存在任何日期

SQL Query - Select last date before timespan where no dates exists in timespan

在我的 table 中,我有一个日期时间列和一个标记列。如果时间跨度中没有值,我想 select 时间跨度之前的最新值。如果时间跨度中有值,我不想 return 任何值。

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

@StartDate datetime
@EndDate datetime

最重要的查询结果:

  1. 如果@StartDate 和@EndDate 之间存在数据,则查询不应return 任何结果
  2. 查询应该return @StartDate IF 之前每个标签的最新值,并且仅当@StartDate 和@EndDate 之间没有结果时

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

  1. Returns 时间跨度之前的最新结果。
  2. Returns 所有结果都在时间跨度内。

想法是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))

重现步骤(查询):
这不应该 return 任何值,因为时间跨度中有值。

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')  

编辑:在有关预期结果的部分中添加了 "latest value for each Tag"。

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;

以下应该是您要找的大致内容:

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;

@StartTime@EndTime 之间有数据时,0 = (SELECT COUNT(*) ...) 位会导致查询 return 无数据。查询的其余部分只是选择 @StartTime.

之前的第一行

SQL Fiddle

0 = (SELECT COUNT(*) ...)EXISTS(SELECT 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)。