views:

45

answers:

5

Hi,

I have some data on my table like:

DAY      | QTY | Name
1/1/2010 |  1  | jack
5/1/2010 |  5  | jack
2/1/2010 |  3  | wendy
5/1/2010 |  2  | wendy

my goal is to have a SP requesting a period of time (example: '2010-1-1' to '2010-1-5'), and get no gaps. Output example:

DAY      | QTY | Name
1/1/2010 |  1  | jack
2/1/2010 |  0  | jack
3/1/2010 |  0  | jack
4/1/2010 |  0  | jack
5/1/2010 |  5  | jack
1/1/2010 |  3  | wendy
2/1/2010 |  0  | wendy
3/1/2010 |  0  | wendy
4/1/2010 |  2  | wendy
5/1/2010 |  0  | wendy

Any gaps is filled with 0- I know that I can create a loop to will solve me the problem, but is very slow.

Does anyone have any ideas how to optimize this?

A: 

Here's another way:

DECLARE @output TABLE (
    DateValue datetime,
    Qty varchar(50),
    LastName varchar(25)
    PRIMARY KEY (DateValue, LastName)
)
DECLARE @minMonth datetime, @maxMonth datetime, @lastName varchar(25)
-- whatever your business logic dictates for these
    SET @minMonth = '01/01/2010' 
    SET @maxMonth = '12/01/2010';

with cte as (
    SELECT @minMonth AS DateValue
    UNION ALL
    SELECT DATEADD(month, 1, DateValue)
    FROM cte
    WHERE DATEADD(month, 1, DateValue) <= @maxMonth
) 
INSERT INTO @output (DateValue, Qty, LastName)
SELECT cte.DateValue, 
   ISNULL(tbl.Alias,0), 
   tbl.Name
FROM cte LEFT JOIN dbo.YourTable tbl ON tbl.[Day] = cte.Mth

UPDATE @output SET 
    LastName = CASE WHEN LastName IS NULL THEN @lastName ELSE LastName END,
    @lastName = LastName
FROM @output

SELECT * FROM @output
Tahbaza
Hi Tahbaza, I'm getting this error in line @lastName = tbl.Name. A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations. Can you help me? PS - I'm using MSSQL 2005
muek
I hadn't ever tried this trick with a straight select, just an update. I've updated the answer to something that should be free of said restriction.
Tahbaza
Please check my answer/question
muek
A: 
WITH DateRangeCTE([d]) AS 
( 
    SELECT 
        CONVERT(DATETIME, '2010-01-01') AS [d] 
    UNION ALL 
    SELECT 
        DATEADD(d, 1, [d]) AS [d] 
    FROM 
        DateRangeCTE 
    WHERE [d] < DATEADD(d, -1, CONVERT(DATETIME, '2010-1-31')) 
) 
SELECT 
   DateRangeCTE.d, YourTable.Qty, YourTable.Name
FROM DateRangeCTE
LEFT JOIN YourTable ON DateRangeCTE.d = YourTable.DAY

If you get the error "The statement terminated. The maximum recursion 100 has been exhausted before statement completion." then use the maxrecursion hint.

Dave Barker
A: 

Here's a solution that you can use if you don't know the date range in advance. It derives the date range based on the data. The solution uses a numbers table, which uses an existing table in the master database (spt_values).

WITH MinMax AS
    ( SELECT DISTINCT [Name],
             MIN([DAY]) OVER () AS min_day, MAX([DAY]) OVER () AS max_day
        FROM mytable
    )
 , DateRange AS
    ( SELECT MinMax.[Name], DATEADD(mm, n.number, MinMax.min_day) AS [Date]
    FROM MinMax
    JOIN master.dbo.spt_values n ON n.type = 'P'
             AND DATEADD(mm, n.number, MinMax.min_day) <= MinMax.max_day
    )

SELECT dr.[Name], COALESCE(mt.[qty], 0) AS [QTY], dr.Date
FROM DateRange dr
LEFT OUTER JOIN MyTable mt ON dr.Name = mt.Name AND mt.Day = dr.Date
ORDER BY dr.Name, dr.Date ;
bobs
A: 

Hi Tahbaza,

check my code, I can't make it work

declare @table table
(
    DAY datetime,
    QTY int,
    Name nvarchar  (500) NULL
)
insert @table values('2010-1-1',  1, 'jack')
insert @table values('2010-1-3',  5,  'jack')
insert @table values('2010-1-2',  3 , 'wendy')
insert @table values('2010-1-4',  2 , 'wendy')

DECLARE @output TABLE (
    DateValue datetime,
    Qty varchar(50),
    LastName varchar(25) NULL --PRIMARY KEY (DateValue, LastName)
)
DECLARE @minMonth datetime, @maxMonth datetime, @lastName varchar(25)
SET @minMonth = '01/01/2010' 
SET @maxMonth = '12/01/2010';

WITH cte AS (
        SELECT @minMonth AS DateValue
    UNION ALL
        SELECT DATEADD(month, 1, DateValue)
        FROM cte
        WHERE DATEADD(month, 1, DateValue) <= @maxMonth
) 
INSERT INTO @output (DateValue, Qty, LastName)
    SELECT 
        cte.DateValue, 
        ISNULL(tbl.qty,0), 
        tbl.Name
    FROM 
        cte LEFT JOIN 
        @table tbl ON tbl.[Day] = cte.DateValue

UPDATE @output SET 
    @lastName = LastName,
    LastName =  CASE WHEN LastName IS NULL THEN @lastName 
                    ELSE LastName 
                END
FROM @output

SELECT * FROM @output
muek
A: 

I leave were the correct answer based on the help of everyone

-- dummy data
declare @table table
(
    DAY datetime,
    QTY int,
    Name nvarchar  (500) NULL
)
insert @table values('2010-1-1',  1, 'jack')
insert @table values('2010-1-3',  5,  'jack')
insert @table values('2010-1-2',  3 , 'wendy')
insert @table values('2010-1-6',  2 , 'wendy')


-- algorithm
DECLARE @output TABLE (
    DAY datetime,
    Qty int,
    Name varchar(25)
)
DECLARE @minMonth datetime, @maxMonth datetime, @lastName varchar(25)
SET @minMonth = '2010-1-1' 
SET @maxMonth = '2010-1-6';

WITH cte AS (
        SELECT @minMonth AS DateValue
    UNION ALL
        SELECT DATEADD(day, 1, DateValue)
        FROM cte
        WHERE DATEADD(day, 1, DateValue) <= @maxMonth
) 
INSERT INTO @output 
    SELECT 
        cte.DateValue, 
        ISNULL(tbl.qty,0), 
        tbl.Name
    FROM 
        cte cross JOIN 
        @table tbl

update @output
set qty = 0
where cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name in 
(
    select cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name from @output
    except
    select cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name from @table
)

SELECT DAY, sum(qty) as qty, Name
FROM @output
GROUP BY DAY, Name
order by 3,1

and the output that I pretend

2010-01-01 00:00:00.000 1   jack
2010-01-02 00:00:00.000 0   jack
2010-01-03 00:00:00.000 5   jack
2010-01-04 00:00:00.000 0   jack
2010-01-05 00:00:00.000 0   jack
2010-01-06 00:00:00.000 0   jack
2010-01-01 00:00:00.000 0   wendy
2010-01-02 00:00:00.000 3   wendy
2010-01-03 00:00:00.000 0   wendy
2010-01-04 00:00:00.000 0   wendy
2010-01-05 00:00:00.000 0   wendy
2010-01-06 00:00:00.000 2   wendy

Although the solution is correct, doesn't fit my need because recursion limitation.

Hopefully this script will help anyone with similar questions

Thank you to all

muek