SQL Pivot data by time - sql

I'm trying to get a pivot of my data, based on the timestamp. I want to group them into half-hour "buckets". For example, with the data below:
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
I would like the result to be something like
Time | A | B | C |
-----------------------------------------------------------------
08:00 - 08:30 | 100.5 | | |
08:30 - 09:00 | | | |
09:00 - 09:30 | | 82.32 | |
09:30 - 10:00 | | | |
10:00 - 10:30 | | | |
10:30 - 11:00 | | | 128.00 |
11:00 - 11:30 | | | 55.77 |
11:30 - 12:00 | 592.11 | | |
12:00 - 12:30 | 758.23 | 95.00 | |
12:30 - 13:00 | | | |
-----------------------------------------------------------------
Do I have to create an empty table with the timeslots in order to do this? Is there a method of doing this without using CASE WHEN?
Thanks!

Maybe I'm try to solve a non-existent problem but I'd like to offer an alternative solution which is dynamic in respect to the number of employees (it adds columns for extra employees) and sums the amounts sold in a time slot per employee if there's more than one sale in that slot; if you don't aggregate the amount you'll end up with multiple rows for every time slot where someone has sold more than one time.
The time slot generation is borrowed from the excellent solution in Nenads answer.
First the the test data with a couple of extra rows to illustrate the difference:
DROP TABLE #test;
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
,('D', '09:40', '28.00')
,('E', '11:40', '50.00')
,('E', '11:35', '20.00')
The query build a SQL statement dynamically and executes it with the EXECUTE statement:
DECLARE #Headers VARCHAR(MAX)
SELECT #Headers = COALESCE(#Headers + ',[' + Employee + ']', '[' + Employee + ']')
FROM (SELECT DISTINCT Employee FROM #test) Emp
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
WITH CTE_TimeSlots AS
(
SELECT CAST(''8:00'' AS TIME) AS StartTime, CAST(''8:30'' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= ''12:00''
)
SELECT * FROM (
SELECT CONVERT(NVARCHAR(10),StartTime) + '' - '' + CONVERT(NVARCHAR(10),EndTime) AS [Time], Amount, Employee
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
) innerQuery
PIVOT (SUM(Amount) FOR Employee IN (' + #Headers + ')
) AS PivotTable
'
--PRINT #SQL -- Uncomment to see the query which will be run
EXECUTE(#SQL)

You can use recursive CTE to create your time slots 'on the fly' and then join it to your data. There is a way to avoid using CASE, you can use PIVOT command instead, but I think this is much simpler:
WITH CTE_TimeSlots AS
(
SELECT CAST('8:00' AS TIME) AS StartTime, CAST('8:30' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= '12:00'
)
SELECT CONVERT(NVARCHAR(10),StartTime) + ' - ' + CONVERT(NVARCHAR(10),EndTime) AS [Time]
, CASE WHEN Employee = 'A' THEN Amount END AS A
, CASE WHEN Employee = 'B' THEN Amount END AS B
, CASE WHEN Employee = 'C' THEN Amount END AS C
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
SQLFiddle DEMO

Related

SQL: How to 'split' rows in a table by the values of certain columns? [duplicate]

This question already has answers here:
SQL split values to multiple rows
(12 answers)
How to generate minute intervals between two dates in T-SQL?
(4 answers)
Closed 3 years ago.
I have a table with rows like this:
Name | date_from | date_to | age
------+------------+------------+-----
Alice | 01.12.2004 | 03.04.2008 | 35
Bob | 04.02.2013 | 04.11.2014 | 43
I would like to make a table that splits each row into one-year intervals by the
date_from and date_to columns, keeping the Name, and updating the age, like this:
Name | date_from | date_to | age
------+------------+------------+-----
Alice | 01.12.2004 | 01.12.2005 | 35
Alice | 01.12.2005 | 01.12.2006 | 36
Alice | 01.12.2006 | 01.12.2007 | 37
Alice | 01.12.2007 | 01.12.2008 | 38
Alice | 01.12.2008 | 03.04.2008 | 39
Bob | 04.02.2013 | 04.02.2014 | 43
Bob | 04.02.2014 | 04.11.2014 | 44
Is this possible to do in SQL?
One solution would be to generate a list of numbers and join it with the original table, adding years to the starting date until the end date is reached.
The following query handles up to 5 years span (to support more years, you would need to extend the subquery with more VALUESs)
SELECT
name,
DATEADD(year, x.n, t.date_from) date_from,
CASE
WHEN DATEADD(year, x.n + 1, t.date_from) > t.date_to
THEN date_to
ELSE DATEADD(year, x.n + 1, t.date_from)
END date_to,
t.age + x.n age
FROM
mytable t
INNER JOIN (
VALUES(0), (1), (2), (3), (4), (5)
) x(n) ON DATEADD(year, x.n, t.date_from) <= t.date_to
ORDER BY name, age
This demo on DB Fiddle with your sample data returns:
name | date_from | date_to | age
:---- | :------------------ | :------------------ | --:
Alice | 01/12/2004 00:00:00 | 01/12/2005 00:00:00 | 35
Alice | 01/12/2005 00:00:00 | 01/12/2006 00:00:00 | 36
Alice | 01/12/2006 00:00:00 | 01/12/2007 00:00:00 | 37
Alice | 01/12/2007 00:00:00 | 03/04/2008 00:00:00 | 38
Bob | 04/02/2013 00:00:00 | 04/02/2014 00:00:00 | 43
Bob | 04/02/2014 00:00:00 | 04/11/2014 00:00:00 | 44
here's your query.
;with cte as (
select 1 as ctr, DATEDIFF(year, cast(date_from as datetime), cast(date_to as datetime)) as ct
,cast(date_from as date) as dt, cast(date_from as date) as dt2, date_to, cast(age as int) as age, [name] from test
union all
select ctr + 1, ct, dateadd(year, 1, dt), dt2, date_to, age + 1, [name] from cte
where ctr + 1 <= ct+1)
select [name], dt as date_from, case when ctr - 1 != ct then dt else date_to end as date_to, age from cte order by dt2, age
output:
Another possible solution using SQL Server
-- data preparation
create table test1
(
name varchar(20),
date_from date,
date_to date ,
age int
)
insert into test values ('alice' , '01-2-2008' , '11-3-2014' , 35 )
insert into test values ('bob' , '06-2-2005' , '7-10-2016' , 20)
create table test2
(
name varchar(20),
date_from date,
date_to date ,
age int
)
-- query
declare #name varchar(20)
declare #date_from date
declare #date_to date
declare #age int
declare #date_step as date
declare #sql_st as nvarchar(max)
declare cur cursor for select name, date_from , date_to , age from test
open cur;
fetch next from cur into #name , #date_from , #date_to , #age
while ##FETCH_STATUS = 0
begin
set #date_step = dateadd(year,1,#date_from)
while #date_to > #date_step
begin
set #sql_st = concat('insert into test2 values (''',#name , ''' , ''' , #date_from , ''' , ''',#date_step,''',',#age , ' )')
print(#sql_st)
exec sp_executesql #sql_st
set #date_from = #date_step
set #date_step = dateadd(year,1,#date_step)
set #age = #age + 1
end
set #sql_st = concat('insert into test2 values (''',#name , ''' , ''' , #date_from , ''' , ''',#date_to,''',',#age , ' )')
exec sp_executesql #sql_st
--print(#sql_st)
fetch next from cur into #name , #date_from , #date_to , #age
end
close cur;
deallocate cur;

Display data for all date ranges including missing dates

I'm having a issue with dates. I have a table with given from and to dates for an employee. For an evaluation, I'd like to display each date of the month with corresponding values from the second sql table.
SQL Table:
EmpNr | datefrom | dateto | hours
0815 | 01.01.2019 | 03.01.2019 | 15
0815 | 05.01.2019 | 15.01.2019 | 15
0815 | 20.01.2019 | 31.12.9999 | 40
The given employee (0815) worked during 01.01.-15.01. 15 hours, and during 20.01.-31.01. 40 hours
I'd like to have the following result:
0815 | 01.01.2019 | 15
0815 | 02.01.2019 | 15
0815 | 03.01.2019 | 15
0815 | 04.01.2019 | NULL
0815 | 05.01.2019 | 15
...
0815 | 15.01.2019 | 15
0815 | 16.01.2019 | NULL
0815 | 17.01.2019 | NULL
0815 | 18.01.2019 | NULL
0815 | 19.01.2019 | NULL
0815 | 20.01.2019 | 40
0815 | 21.01.2019 | 40
...
0815 | 31.01.2019 | 40
as for the dates, we have:
declare #year int = 2019, #month int = 1;
WITH numbers
as
(
Select 1 as value
UNion ALL
Select value + 1 from numbers
where value + 1 <= Day(EOMONTH(datefromparts(#year,#month,1)))
)
SELECT b.empnr, b.hours, datefromparts(#year,#month,numbers.value) Datum FROM numbers left outer join
emptbl b on b.empnr = '0815' and (datefromparts(#year,#month,numbers.value) >= b.datefrom and datefromparts(#year,#month,numbers.value) <= case b.dateto )
which is working quite well, yet I have the odd issue, that this code is only shoes the dates between 01.01.2019 and 03.01.2019
thank you very much in advance!
Did you check, if datefrom and dateto is in correct range?
Minimum value of DateTime field is 1753-01-01 and maximum value is 9999-12-31.
Look at your source table to check initial values.
The recursive CTE needs to begin with MIN(datefrom) and MAX(dateto):
DECLARE #t TABLE (empnr INT, datefrom DATE, dateto DATE, hours INT);
INSERT INTO #t VALUES
(815, '2019-01-01', '2019-01-03', 15),
(815, '2019-01-05', '2019-01-15', 15),
(815, '2019-01-20', '9999-01-01', 40),
-- another employee
(999, '2018-01-01', '2018-01-31', 15),
(999, '2018-03-01', '2018-03-31', 15),
(999, '2018-12-01', '9999-01-01', 40);
WITH rcte AS (
SELECT empnr
, MIN(datefrom) AS refdate
, ISNULL(NULLIF(MAX(dateto), '9999-01-01'), CURRENT_TIMESTAMP) AS maxdate -- clamp year 9999 to today
FROM #t
GROUP BY empnr
UNION ALL
SELECT empnr
, DATEADD(DAY, 1, refdate)
, maxdate
FROM rcte
WHERE refdate < maxdate
)
SELECT rcte.empnr
, rcte.refdate
, t.hours
FROM rcte
LEFT JOIN #t AS t ON rcte.empnr = t.empnr AND rcte.refdate BETWEEN t.datefrom AND t.dateto
ORDER BY rcte.empnr, rcte.refdate
OPTION (MAXRECURSION 1000) -- approx 3 years
Demo on db<>fiddle
It could be in your select, try:
SELECT b.empnr, b.hours, datefromparts(#year,#month,numbers.value) Datum
FROM numbers
LEFT OUTER JOIN emptbl b ON b.empnr = '0815' AND
datefromparts(#year,#month,numbers.value) BETWEEN b.datefrom AND b.dateto
Your CTE produces only 31 number and therefore it is showing only January dates.
declare #year int = 2019, #month int = 1;
WITH numbers
as
(
Select 1 as value
UNion ALL
Select value + 1 from numbers
where value + 1 <= Day(EOMONTH(datefromparts(#year,#month,1)))
)
SELECT *
FROM numbers
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a24e58ef4ce522d3ec914f90907a0a9e
You can try below code,
with t0 (i) as (select 0 union all select 0 union all select 0),
t1 (i) as (select a.i from t0 a ,t0 b ),
t2 (i) as (select a.i from t1 a ,t1 b ),
t3 (srno) as (select row_number()over(order by a.i) from t2 a ,t2 b ),
tbldt(dt) as (select dateadd(day,t3.srno-1,'01/01/2019') from t3)
select tbldt.dt
from tbldt
where tbldt.dt <= b.dateto -- put your condition here
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b16469908b323b8d1b98d77dd09bab3d

Times / Rotas on SQL

Hello I am trying to show a rota in sql but now stuck
I have tried a few things but seem way off the mark!
Essentially my data returned is
Name | Role | RotaDate | StartTime | EndTime
Joe Bloggs |Cleaner |2017-01-09 00:00:00.000|1900-01-01 19:00:00.000|1900-01-02 07:15:00.000
Joe Bloggs |Cleaner |2017-01-11 00:00:00.000|1900-01-01 19:00:00.000|1900-01-02 07:15:00.000
So this is a shift on 9th Jan from 7pm - 7:15am. There are other dates / times etc but this is just a snippet.
I would like it returned as the following (assuming he has shifts on the 11th as well for example
Name-------|Role-------| 09/01----------| 10/01 | 11/01
Joe Bloggs | Cleaner | 19:00-07:15 | |19:00-0715
Any help would be appreciated (both this and the layout!
My attempt was
select name, role, date,convert(datetime, r.timeFrom+' - '+convert(datetime,r.timeTo) as Shift
max (CASE when r.rotadate='2017-01-09' THEN timeFrom else 0 END) as '2017-01-09',
max (CASE when r.rotadate='2017-01-10' THEN timeFrom else 0 END) as '2017-01-10'
from rota
where rota date between '2017-01-09' and '2017-01-13'
Firstly i created sample data and converted the data in to required format
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
;With cte(Name,[Role],RotaDate, StartTime ,EndTime )
AS
(
SELECT 'Joe Bloggs','Cleaner' ,'2017-01-09 00:00:00.000','1900-01-01 19:00:00.000','1900-01-02 07:15:00.000' Union all
SELECT 'Joe Bloggs','Cleaner' ,'2017-01-11 00:00:00.000','1900-01-01 19:00:00.000','1900-01-02 07:15:00.000'
)
SELECT NAME
,[Role]
,LEFT(RotaDate, 10) AS RotaDate
,StartTime
,SUBSTRING(EndTime, CHARINDEX(' ', EndTime, 1) + 1, LEN(EndTime)) AS EndTime
,STUFF((
SELECT DISTINCT '- ' + CAST(SUBSTRING(StartTime, CHARINDEX(' ', StartTime, 1) + 1, 5) AS VARCHAR(19))
+ '-' + CAST(SUBSTRING(EndTime, CHARINDEX(' ', EndTime, 1) + 1, 5) AS VARCHAR(19))
FROM cte
FOR XML PATH('')
), 1, 1, '') AS StartTimeENDTime
INTO #temp
FROM cte
Then using pivot we get desired result
SELECT NAME
,[Role]
,ISNULL([2017-01-09],'') AS [09/01]
,ISNULL([2017-01-10],'') AS [10/01]
,ISNULL([2017-01-11],'') AS [11/01]
FROM (
SELECT *
FROM #temp
) AS Src
PIVOT(MIN(StartTimeENDTime) FOR RotaDate IN (
[2017-01-09]
,[2017-01-10]
,[2017-01-11]
)) AS PVT
Result
NAME Role 09/01 10/01 11/01
-----------------------------------------------------------
Joe Bloggs Cleaner 19:00-07:15 19:00-07:15

SQL Server grouping a timestamp by hour but keep as date format (don't want to pull hour out)

When I want to group a bunch of time stamps by day, by
CONVERT (datetime, CONVERT (varchar, dbo.MEASUREMENT_Battery.STAMP, 101))
it produces for me a "day" stamp that SQL Server still views as a date and can be sorted and used as such.
What I'm trying to figure out is if it's possible to do the same thing by hour. I tried
CAST(DATEPART(Month, STAMP) AS varchar) + '/' + CAST(DATEPART(Day, STAMP) AS varchar) + '/' + CAST(DATEPART(Year, STAMP) AS varchar) + ' ' + CAST(DATEPART(Hour, STAMP) AS varchar) + ':00:00.000'
and this "works" but SQL Server doesn't view this as a date anymore so I can't sort properly.
The end result I want is right though: ex: 9/9/2015 9:00:00.000
Do NOT convert into a string, until you absolutely have to "present" the result.
CONVERT() or FORMAT() return string representations of temporal information
The following method returns a datetime value truncated to the hour without resorting to string manipulation (and hence fast).
select
dateadd(hour, datediff(hour,0, dbo.MEASUREMENT_Battery.STAMP ), 0)
, count(*)
from dbo.MEASUREMENT_Battery
group by
dateadd(hour, datediff(hour,0, dbo.MEASUREMENT_Battery.STAMP ), 0)
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE MEASUREMENT_Battery
([STAMP] datetime)
;
INSERT INTO MEASUREMENT_Battery
([STAMP])
VALUES
('2015-11-12 07:40:15'),
('2015-11-12 08:40:15'),
('2015-11-12 09:40:15'),
('2015-11-12 10:40:15'),
('2015-11-12 11:40:15'),
('2015-11-12 12:40:15'),
('2015-11-12 13:40:15'),
('2015-11-12 14:40:15')
;
NOTE: the output below for column [Stamp] is the default display
Results:
| | |
|----------------------------|---|
| November, 12 2015 07:00:00 | 1 |
| November, 12 2015 08:00:00 | 1 |
| November, 12 2015 09:00:00 | 1 |
| November, 12 2015 10:00:00 | 1 |
| November, 12 2015 11:00:00 | 1 |
| November, 12 2015 12:00:00 | 1 |
| November, 12 2015 13:00:00 | 1 |
| November, 12 2015 14:00:00 | 1 |
If you absolutely insist on dipay of a date/time value a paricular way, then you may add the display format in the select clause (but not needed in the group by clause!)
select
FORMAT(dateadd(hour, datediff(hour,0, dbo.MEASUREMENT_Battery.STAMP ), 0) , 'MM/dd/yyyy HH')
, count(*)
from dbo.MEASUREMENT_Battery
group by
dateadd(hour, datediff(hour,0, dbo.MEASUREMENT_Battery.STAMP ), 0)
What happens is that when you use the DateTime Style 101 (at the end of the second CONVERT) the Date will be converted to mm/dd/yyyy and the time to 00:00:00.000 always as stated here an:
https://msdn.microsoft.com/en-us/library/ms187928.aspx
Now, from what I understand from your question is that you would like to include the hour as well and this can be done like this:
SELECT FORMAT(STAMP , 'MM/dd/yyyy HH') + ':00:00.000'
Note:
':00:00.000' is optional and is just for a nicer output.
This only works in SQL Server 2012 and later version.
Testing with some test date we will see that we get the expected result:
-- Drop temp table if it exists
IF OBJECT_ID('tempdb..#T') IS NOT NULL DROP TABLE #T
-- Create temp table
CREATE TABLE #T ( myDate DATETIME )
-- Insert dummy values
INSERT INTO #T VALUES ( '2015-12-25 14:00:00.000' ) -- 14
INSERT INTO #T VALUES ( '2015-12-25 14:00:00.000' ) -- 14
INSERT INTO #T VALUES ( '2015-12-25 15:00:00.000' )
INSERT INTO #T VALUES ( '2015-12-25 16:00:00.000' )
INSERT INTO #T VALUES ( '2015-12-25 17:00:00.000' ) -- 17
INSERT INTO #T VALUES ( '2015-12-25 17:00:00.000' ) -- 17
-- Select query
SELECT COUNT( myDate ), MAX( FORMAT( myDate , 'MM/dd/yyyy HH') + ':00:00.000' ) FROM #T
GROUP BY DATEPART( hour, myDate )
Output:
2 12/25/2015 14:00:00.000
1 12/25/2015 15:00:00.000
1 12/25/2015 16:00:00.000
2 12/25/2015 17:00:00.000

T-SQL between periods gaps

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?
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.
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 ;
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
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