How to Select records that don't exist in Sql Server

后端 未结 3 1006
醉梦人生
醉梦人生 2020-12-19 18:53

This isn\'t about checking if a record exists before selecting it, or viceversa.

The issue is this, I have a database with many records, they are stored by date, by

相关标签:
3条回答
  • 2020-12-19 19:12

    Eric Fan had the right idea, but I thought it might help to see an implementation:

    Here's a function that will return a table of the inclusive dates:

    CREATE FUNCTION dbo.fDatesBetween(@startDate Date, @endDate Date)
        RETURNS @tbl TABLE
            (
                a_date      Date
            )
    AS BEGIN
        DECLARE @thisDt Date
        SET @thisDt = @startDate
    
        WHILE @thisDt <= @endDate
        BEGIN
            INSERT INTO @tbl SELECT @thisDt
            SET @thisDt = DateAdd(day, 1, @thisDt)
        END
        RETURN
    END
    

    Now, if you do an outer join to your table from the function results, you will have what you are looking for:

    SELECT DATES.a_date, SampleTable.value1, SampleTable.value2
      FROM dbo.fDatesBetween(@startDate, @endDate) DATES
           LEFT JOIN SampleTable
              ON DATES.a_date = SampleTable.dt
    

    That should give you just what you asked for.

    0 讨论(0)
  • 2020-12-19 19:17

    This should work:

    WITH
        -- Numbers CTE, courtesy Itzik Ben-Gan
      L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
      L1   AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
      L2   AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
      L3   AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
      L4   AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
      Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L4),
        -- Turn into Dates
      Dates AS(SELECT CAST('18991231' AS DATETIME)+n AS d FROM Nums)
    SELECT * 
    FROM Dates d
    LEFT JOIN myTable t ON d.d = t.[date]
    WHERE d.d>='02/01/2014' AND d.d<='02/03/2014'
    

    Here is a version that is optimized for smaller date ranges:

    DECLARE @startDate datetime = '2/1/2014'
    DECLARE @endDate datetime = '2/3/2014'
    DECLARE @days as INT = DATEDIFF(dd, @startDate, @enddate) + 1
    ;
    WITH
        -- Numbers CTE, courtesy Itzik Ben-Gan
      L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
      L1   AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
      L2   AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
      L3   AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
      L4   AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
      T4   AS(SELECT TOP (@days) 1 AS c FROM L4),
      Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS n FROM T4),
        -- Turn into Dates
      Dates AS(SELECT @startDate+n AS d FROM Nums)
    SELECT * 
    FROM Dates d
    LEFT JOIN myTable t ON d.d = t.[date]
    WHERE d.d>=@startDate AND d.d<=@endDate
    
    0 讨论(0)
  • 2020-12-19 19:27

    You could do this with a recursive CTE. Something like this:

    DECLARE @startDate datetime = '2/1/2014'
    DECLARE @endDate datetime = '2/6/2014'
    
    ;WITH DateRange(RunningDate) AS
    (
        SELECT @startDate AS RunningDate
        UNION ALL
        SELECT RunningDate + 1
        FROM DateRange
        WHERE RunningDate < @endDate
    )
    
    SELECT id, RunningDate date, value1, value2 
    FROM DateRange LEFT JOIN myTable ON myTable.date = DateRange.RunningDate
    

    Edit... IF you do go with this solution (take note of Aaron Bertrand's comment under my answer), note that you'll also have to specify the max recursion if you intend on dealing with ranges greater than 3 months. The default is set to 100. This means that, as the query is currently written, it will only do a maximum of 101 dates (100 levels of recursion).

    You can additionally specify OPTION (MAXRECURSION 0) at the end of the last SELECT to overcome this. 0 means infinite levels of recursion. But again, take note of Aaron's post.

    0 讨论(0)
提交回复
热议问题