获取SQL Server

时间:2017-08-04 14:32:56

标签: sql-server date

我希望找到解决这个问题的方法。我有一个名为LogEntry的表,用于存储多个办公室使用的信息,他们必须在任何一天记录进入办公室的任何访问者。如果没有访客进入,他们仍然需要登录"没有访客"为了这一天。如何运行一个查询,提取办公室无法创建的所有日期,即使是“没有访客”#34;登录?

我查看了this question(以及内部链接的文章),但即使调整了该查询,我也只能为办公室遗漏的日期创建一个空白行输入日期,而不是指定未创建条目的实际办公室。有没有办法做我想做的事情?

declare @temp table (

CDate datetime,
loc_id varchar(50)
)

insert into @temp SELECT DISTINCT entryDate, locationID FROM LogEntry WHERE entryDate >= '05/01/2017' AND entryDate <= '07-31-2017' 

;with d(date) as (
  select cast('05/01/2017' as datetime)
  union all
  select date+1
  from d
  where date < '07/31/2017'
  )

select DISTINCT t.loc_id, CONVERT(date, d.date)
    FROM d LEFT OUTER JOIN @temp t ON d.date = t.CDate
    GROUP BY t.loc_id, d.date
    ORDER BY t.loc_id

正如我所说,这个查询返回了日期范围内的日期列表,以及在该日期提交条目的所有位置,但我想找到一种方法来提取相反的信息:如果是office(由locationID指定)没有在给定的一天提交条目,只返回那些locationID和它们错过的日期。

Sample data

EntryID | locationID | entryDate
=================================
1         1            07-01-2017
2         1            07-02-2017
3         2            07-02-2017
4         1            07-04-2017

Expected Result (for date range of 07-01 to 07-04)

locationID | missedEntryDate
============================
1            07-03-2017
2            07-01-2017
2            07-03-2017
2            07-04-2017

2 个答案:

答案 0 :(得分:2)

您的第一步很好,您创建了所有日期的列表,但您还需要所有位置的列表。然后创建一个交叉连接以包含所有组合,然后执行var d = '';以找出缺少的内容。

left join

答案 1 :(得分:0)

如果您的数据集不是太大,可以试试这个:

select t.loc_id, CONVERT(date, d.date)
FROM d
    -- Cross join dates to all available locs
    CROSS JOIN (SELECT DISTINCT loc_id FROM @temp ) AS Locs
    LEFT JOIN
        ( SELECT loc_id, t.CDate
        FROM @temp 
        GROUP BY loc_id, d.date ) AS t ON d.date = t.CDate AND Locs.loc_id = t.loc_id
ORDER BY Locs.loc_id

这应该快一点:

;WITH cte AS (
    SELECT a.LocID, RangeStart.CDate, ( CASE WHEN Input.LocID IS NULL THEN 1 ELSE 0 END ) AS IsMissing
    FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
    CROSS JOIN ( SELECT CONVERT( DATETIME, '2017-05-01' ) AS CDate ) AS RangeStart
    LEFT JOIN
            ( SELECT LocID, MIN( CDate ) AS CDate
            FROM #temp
            WHERE CDate = '2017-05-01'
            GROUP BY LocID ) AS Input ON a.LocID = Input.LocID AND RangeStart.CDate = Input.CDate
    UNION ALL
    SELECT a.LocID, a.CDate + 1 AS CDate,
         ISNULL( ItExists, 0 ) AS IsMissing
    FROM cte AS a
        OUTER APPLY( SELECT LocID, 1 AS ItExists FROM #temp AS b WHERE a.LocID = b.LocID AND a.CDate + 1 = b.CDate ) AS c
    WHERE a.CDate < '2017-07-01'
)
SELECT * FROM cte OPTION( MAXRECURSION 0 )

您还可以添加索引:

CREATE INDEX IX_tmp_LocID_CDate ON #temp( LocID, CDate )

第二个查询的示例数据集:

CREATE TABLE #temp( LocID VARCHAR( 50 ), CDate DATETIME )
INSERT INTO #temp
VALUES
( '1', '2017-05-01' ), ( '1', '2017-05-02' ), ( '1', '2017-05-03' ), ( '1', '2017-05-04' ), ( '1', '2017-05-05' ),
( '2', '2017-05-01' ), ( '2', '2017-05-02' ), ( '2', '2017-05-03' ), ( '2', '2017-05-04' ), ( '2', '2017-05-05' )

;WITH d AS (
    SELECT CAST( '05/01/2017' AS DATETIME ) AS date
    UNION ALL
    SELECT date + 2
    FROM d
    WHERE date < '2018-07-31'
)
INSERT INTO #temp
SELECT LocID, d.date
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
    CROSS JOIN d
OPTION( MAXRECURSION 0 )