Show missing date

  • hi all,

    i am working on time attendance application. i need to show records for whole month and if there is no data for a particular day then it should consider it as absent. how do i achieve this.

    data

    id date timein timeout status

    1 1/1/2002 07:00 14:00

    1 2/1/2002 07:10 14:20

    1 3/1/2002 null null absent

    1 4/1/2002 07:00 14:00

    1 5/1/2002 null null absent

    any help is highly appreciated

  • LEFT JOIN your data table to a calendar table.

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Use a calendar table, left join from the calendar table to your data table (because you want all the rows from the calendar and matching rows from the data table)

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • using calendar table is the only way???

  • Well you need something that has all dates in it that you can join to.

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • imfairozkhan (10/29/2014)


    using calendar table is the only way???

    you could dynamically create calendar table with recursive cte but I am not sure it would scale well on a large data set.

    CREATE TABLE #temp(id INT, [date] DATE, timein TIME, [timeout] TIME, [status] VARCHAR(10));

    INSERT INTO #temp

    VALUES(1,'2002-1-1','07:00','14:00',''),

    (1,'2002-1-2','07:10','14:20',''),

    (1,'2002-1-4','07:00','14:00','');

    WITH cal(dt) AS(

    SELECT CAST('2002-01-01' AS DATE) AS dt

    UNION ALL

    SELECT CAST(DATEADD(dd,1,dt) AS DATE)FROM cal WHERE dt < '2002-01-05')

    SELECT a.dt,b.*FROM cal a

    LEFT OUTER JOIN #temp b

    ON a.dt = b.[date]

    --OPTION(MAXRECURSION 365);

    recursive CTEs have a max recursion of 100 so if you time range is large than that you will need to use the maxrecursion option

    Bob
    -----------------------------------------------------------------------------
    How to post to get the best help[/url]

  • Robert klimes (10/29/2014)


    imfairozkhan (10/29/2014)


    using calendar table is the only way???

    you could dynamically create calendar table with recursive cte but I am not sure it would scale well on a large data set.

    ...

    recursive CTEs have a max recursion of 100 so if you time range is large than that you will need to use the maxrecursion option

    Carefull now... rCTEs that count are bad even for something so small. Please see the following article for more information

    http://www.sqlservercentral.com/articles/T-SQL/74118/

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • imfairozkhan (10/29/2014)


    using calendar table is the only way???

    Here's a quick and dirty calendar generator using a tally table (you can find plenty of example here of how to build one).

    declare @startDate datetime = '2014-01-01', -- example date

    @endDate datetime = '2015-12-31', -- example date

    @startDateKey int,

    @totalNumberOfDays int

    select @startDateKey = cast(@startDate as int)

    select @totalNumberOfDays = cast(@endDate as int) - @startDateKey

    declare @calendar table (dateKey int, date_time datetime, [Date] date)

    ;with tt (dateKey, date_time, [Date])

    as (select TallyTable.i + @startDateKey,

    cast(TallyTable.i + @startDateKey as datetime),

    cast(cast(TallyTable.i + @startDateKey as datetime) as date)

    from dbo.TalleyTable(@totalNumberOfDays) TallyTable)

    insert @calendar

    (dateKey, date_time, [Date])

    select tt.dateKey, tt.date_time, tt.[Date] from tt

    As noted above, left join your data to something like this (@calendar).

    Don Simpson



    I'm not sure about Heisenberg.

  • Robert klimes (10/29/2014)


    imfairozkhan (10/29/2014)


    using calendar table is the only way???

    you could dynamically create calendar table with recursive cte but I am not sure it would scale well on a large data set.

    CREATE TABLE #temp(id INT, [date] DATE, timein TIME, [timeout] TIME, [status] VARCHAR(10));

    INSERT INTO #temp

    VALUES(1,'2002-1-1','07:00','14:00',''),

    (1,'2002-1-2','07:10','14:20',''),

    (1,'2002-1-4','07:00','14:00','');

    WITH cal(dt) AS(

    SELECT CAST('2002-01-01' AS DATE) AS dt

    UNION ALL

    SELECT CAST(DATEADD(dd,1,dt) AS DATE)FROM cal WHERE dt < '2002-01-05')

    SELECT a.dt,b.*FROM cal a

    LEFT OUTER JOIN #temp b

    ON a.dt = b.[date]

    --OPTION(MAXRECURSION 365);

    recursive CTEs have a max recursion of 100 so if you time range is large than that you will need to use the maxrecursion option

    Great article. thanks. I knew my solution was not great but I was trying to provide something that didn't require materializing a table as sometime that isn't an option

    I have adjusted my query to just use row_number() to build the calendar. For the same result set it reduces reads from 31 to 2. For the full result set of sys.allobjects(2180) the difference is much greater (13081 to 29).

    WITH cal(dt)

    AS (SELECT TOP 5 CAST(DATEADD(dd, ROW_NUMBER()OVER(ORDER BY(SELECT NULL)), '2001-12-31')AS date)

    FROM sys.all_objects)

    SELECT a.dt,

    b.*

    FROM

    cal a

    LEFT OUTER JOIN #temp b

    ON a.dt = b.[date];

    Thanks Jeff, I'm always happy to improve my skills.

    Bob
    -----------------------------------------------------------------------------
    How to post to get the best help[/url]

  • What I'll sometimes use as a poor man's tally table (we don't have one) is to combine ROW_NUMBER() and TOP from some existing table that has enough records to do what I want.

    SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY KEY_COLUMN)

    FROM SOME_TABLE

    You can combine that with DATEADD in this situation. A hundred thousand days covers 273 years worth of days.

    with TallyTable as (

    -- Subtract 1 so you start counting from 0

    select TOP 100000 ROW_NUMBER() OVER (ORDER BY KEY_COLUMN) - 1 as Row_Num

    from SOME_TABLE

    order by Row_Num

    )

    select dateadd(day,Row_Num,cast('1900-01-01' as date)) as Day

    from TallyTable

    This code sample covers all days from 1900-01-01 to 2173-10-15. Increase your TOP number if you need more.

  • The CTE has a default max recusrion of 100. I can easily create a on the fly set of 100 consecutive days from a start date with something like :

    (using my ol favorite Northwind database)

    declare @startdate datetime='1996-07-01';

    WITH SET1 AS(

    SELECT clm1 FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS rs(clm1) /* TEN ELEMENTS */

    ),

    SET2 AS(

    SELECT a.clm1 FROM SET1 a, SET1 b /* 10 rows x 10 rows = 100 rows */

    )

    ,

    SET3 as

    (

    SELECT DATEADD( d, ROW_NUMBER() OVER( ORDER BY ( SELECT '1')) - 1, @startdate) AS [Date]

    fromset2

    )

    selects3.[date],

    count(orderID) numOrders

    FROMSET3 as s3

    left join Orders as o ON s3.[date] = o.orderDate

    GRoup

    Bys3.[date]

    Order by [Date];

    Here you can just join to your attendance table instead of the orders table. If you need more days create another set that takes a cross of the previous CTE (set2 in this example). Hope this makes sense.

    ----------------------------------------------------

Viewing 11 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic. Login to reply