T-SQL Pivot Table By Week Missing Value - sql-server

I'm a Transact SQL newbie(SQL Server 2008).
I have a pivot table that is getting these results when I search for values by date range:
Person | 2/10/14 | 2/18/14
-----------+---------+---------
Jane Doe | NULL | QRS
June Jones | NULL | XYZ
Tom Smith | XYZ | HIK
But I need it to look like this, where June Jones has a value for 2/10/14:
Person | 2/10/14 | 2/17/14
-----------+---------+--------
Jane Doe | NULL | QRS
June Jones | XYZ | XYZ
Tom Smith | XYZ | HIK
The problem is that June Jones has an XYZ event which has a start_dt of 2/11/14 and end_date of 2/21/14 which should be in column '2/10/14' and my query seems to be missing it. The column of '2/17/14' is picking up the event.
My code:
WITH Numbers AS
(
SELECT '2/10/14' As n
UNION
SELECT '2/17/14' As n
) ,
Counted AS
(
SELECT s.[FirstName] + ' ' + s.[LastName] AS [Person],
CASE WHEN s.[Event] = 'Thing1' THEN 'HIK'
WHEN s.[Event] = 'Thing2' THEN 'QRS'
WHEN s.[Event] = 'Thing3' THEN 'XYZ'
ELSE NULL END AS [Info],
N.n AS DateWeek
FROM [dbo].[MyTable] s
INNER JOIN Numbers N ON DATEADD(DD,5,N.n) BETWEEN s.start_dt AND s.end_dt
WHERE s.LastName in ('Doe', 'Jones', 'Smith')
GROUP BY s.[FirstName] + ', ' + s.[LastName], s.[Info], N.n
)
SELECT [Person], '02/10/14', '2/17/14'
FROM Counted
PIVOT ( MAX(Info) FOR DateWeek IN ([2/10/14], [2/17/14] ))
AS PVT
I'm probably missing something obvious, but can't see it.

For 10th obiously it show null because your event start on 11th.

Related

SQL Server: how min and max blank the other row if they same result and convert 24 hours to 12 hours without where method

I want the join EmpID to EmployeeNo and combine the last name, first name, and middle name from the second table and I want to the entries separate the O and I with min and max but if they don't have a min or max become blank or null I just want to become blank the certain row because if they don't blank the row the result is the same.
This is the 1st
| Entries | recordDate | Empid | Reference |
+-----------------------+-------------------------+--------+-----------+
| 0016930507201907:35I | 2019-05-07 00:00:00 000 | 001693 | 1693 |
| 0016930507201917:06O | 2019-05-07 00:00:00 000 | 001693 | 1693 |
| 0016930507201907:35I | 2019-05-08 00:00:00 000 | 001693 | 1693 |
| | 2019-05-08 00:00:00 000 | 001693 | 1693 |
2nd table
| LastName | FirstName | middleName | EmployeeNO |
+----------+-----------+------------+------------+
| Cruz | Kimberly | Castillo | 001693 |
I want to join that two table with the second table combine the lastname,FirstName, and middleName . the employeeNo join to Empid but the entries would be separate between I and O with min or max of certain empId but if the entries have not I or O it would be blank like this and also with where
| Name | EmployeeNO | RecordDate | TimeIn | TimeOut |
+-------------------------+------------+-------------------------+--------+---------+
| CRUZ, MA KIMBERLY, CA | 001693 | 2019-05-07 00:00:00 000 | 07:35 | 05:06 |
| CRUZ, MA KIMBERLY,CA | 001693 | 2019-05-08 00:00:00 000 | 07:35 |
If I have a where there have a error please help me this is the error
Conversion failed when converting date and/or time from character string.
The following will do it for you. I use a couple of nested CTEs. The first one doe your data conversions, the second one sets the min and max times, and the final query sets any rows that have missing I records to blank.
The test data uses table variables for your Table1 and Table2.
declare #E table(Entries varchar(50), RecordDate varchar(50), EmpID varchar(6), Ref int)
insert #e values ('0016930507201907:35I','2019-05-07 00:00:00 000','001693',1693)
,('0016930507201917:06O','2019-05-07 00:00:00 000','001693',1693)
,('0016930507201907:35I','2019-05-08 00:00:00 000','001693',1693)
,('','2019-05-08 00:00:00 000','001693',1693)
declare #B table(LastName varchar(50),FirstName varchar(50),middleName varchar(50),EmployeeNO varchar(6))
insert #B values ('Cruz','Kimberly','Castillo','001693')
;with e as (
select right(Entries,1) as InOut,
convert(datetime,left(recorddate,10)) as RecordDate, Entries,
substring(Entries,15,5) as t,
EmpID
from #e
where len(ltrim(Entries))>0
)
, details as (
select B.LastName + ',' + B.FirstName + ',' + B.MiddleName [Name],
EmployeeNO,
RecordDate,
min(case when InOut='I' then T else '99:99' end) as I,
max(case when InOut='O' then T else '' end) as O
from #B B
join e on e.EmpID=B.EmployeeNO
group by B.LastName,B.FirstName,B.MiddleName,EmployeeNO,RecordDate
)
select Name, EmployeeNO, RecordDate, case when I='99:99' then '' else I end as TimeIn, O as TimeOut
from details

How to efficiently match on dates in SQL Server?

I am trying to return the first registration for a person based on the minimum registration date and then return full information. The data looks something like this:
Warehouse_ID SourceID firstName lastName firstProgramSource firstProgramName firstProgramCreatedDate totalPaid totalRegistrations
12345 1 Max Smith League Kid Hockey 2017-06-06 $100 3
12345 6 Max Smith Activity Figure Skating 2018-09-26 $35 1
The end goal is to return one row per person that looks like this:
Warehouse_ID SourceID firstName lastName firstProgramSource firstProgramName firstProgramCreatedDate totalPaid totalRegistrations
12345 1 Max Smith League Kid Hockey 2017-06-06 $135 4
So, this would aggregate the totalPaid and totalRegistrations variables based on the Warehouse_ID and would pull the rest of the information based on the min(firstProgramCreatedDate) specific to the Warehouse_ID.
This will end up in Tableau, so what I've recently tried ignores aggregating totalPaid and totalRegistrations for now (I can get that in another query pretty easily). The query I'm using seems to work, but it is taking forever to run; it seems to be going row by row for >50,000 rows, which is taking forever.
select M.*
from (
select Warehouse_ID, min(FirstProgramCreatedDate) First
from vw_FirstRegistration
group by Warehouse_ID
) B
left join vw_FirstRegistration M on B.Warehouse_ID = M.Warehouse_ID
where B.First in (M.FirstProgramCreatedDate)
order by B.Warehouse_ID
Any advice on how I can achieve my goal without this query taking an hour plus to run?
A combination of the ROW_NUMBER windowing function, plus the OVER clause on a SUM expression should perform pretty well.
Here's the query:
SELECT TOP (1) WITH TIES
v.Warehouse_ID
,v.SourceID
,v.firstName
,v.lastName
,v.firstProgramSource
,v.firstProgramName
,v.firstProgramCreatedDate
,SUM(v.totalPaid) OVER (PARTITION BY v.Warehouse_ID) AS totalPaid
,SUM(v.totalRegistrations) OVER (PARTITION BY v.Warehouse_ID) AS totalRegistrations
FROM
#vw_FirstRegistration AS v
ORDER BY
ROW_NUMBER() OVER (PARTITION BY v.Warehouse_ID
ORDER BY CASE WHEN v.firstProgramCreatedDate IS NULL THEN 1 ELSE 0 END,
v.firstProgramCreatedDate)
And here's a Rextester demo: https://rextester.com/GNOB14793
Results (I added another kid...):
+--------------+----------+-----------+----------+--------------------+------------------+-------------------------+-----------+--------------------+
| Warehouse_ID | SourceID | firstName | lastName | firstProgramSource | firstProgramName | firstProgramCreatedDate | totalPaid | totalRegistrations |
+--------------+----------+-----------+----------+--------------------+------------------+-------------------------+-----------+--------------------+
| 12345 | 1 | Max | Smith | League | Kid Hockey | 2017-06-06 | 135.00 | 4 |
| 12346 | 6 | Joe | Jones | Activity | Other Activity | 2017-09-26 | 125.00 | 4 |
+--------------+----------+-----------+----------+--------------------+------------------+-------------------------+-----------+--------------------+
EDIT: Changed the ORDER BY based on comments.
Try to use ROW_NUMBER() with PARTITIYION BY.
For more information please refer to:
https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql?view=sql-server-2017

Building data sets from multiple multivalue records using applys with missing values

I have a SQL Server 2012 database which was imported from a multi-value environment which causes me more headaches than I care to count, however it is what it is and I have to work with it.
I am trying to build a data set using these multi value records but have hit a stumbling bock. This is my scenario
I have a custom split string TVF that splits a string of "Test,String" into
Rowno | Item
------+---------
1 | Test
2 | String
I have the following data:
Clients Table
Ref | Names | Surname | DOB | IdNo
----+-----------+-----------+---------------------+------
123 |John,Sally |Smith | DOB1,DoB2 | 45,56
456 |Dave,Paul |Jones,Dann| DOB1,DOB2 | 98
789 |Mary,Moe,Al|Lee | DOB1 | NULL
What I need to create is a data set that looks like this:
Ref | Names | Surname | DOB | IdNo
----+-----------+-----------+---------------------+------
123 | John | Smith | DOB1 | 45
123 | Sally | Smith | DOB2 | 56
456 | Dave | Jones | DOB1 | 98
456 | Paul | Dann | DOB2 |
789 | Mary | Lee | DOB1 |
789 | Moe | Lee | |
789 | Al | Lee | |
In the past, to solve similar issues, I would tackle this using a query like this:
SELECT
Ref
, SplitForenames.ITEM names
, SplitSurname.ITEM Surname
, SplitDOB.ITEM dob
, SplitNI.ITEM ID
FROM
Clients
CROSS APPLY
dbo.udf_SplitString(Names, ',') SplitForenames
OUTER APPLY
dbo.udf_SplitString(Surname, ',') SplitSurname
OUTER APPLY
dbo.udf_SplitString(DOB, ',') SplitDOB
OUTER APPLY
dbo.udf_SplitString(ID, ',') SplitNI
WHERE
SplitSurname.RowNo = SplitForenames.RowNo
AND SplitDOB.RowNo = SplitForenames.RowNo
AND SplitNI.RowNo = SplitForenames.RowNo
ORDER BY
REF;
However due to there being examples of differences between the number of surnames to forenames and missing DOB and ID fields i cannot match them in this way.
I need to match where there is a match then otherwise be blank for DOB and ID and use the first instance of the surname. I am just stuck as to how to achieve this.
Anyone have any suggestions as to how i can create my required data-set from the original source.
Thanks in advance
I cant find what are the condition of DOB column to be split or not.
However: with the Split function SpliF as below:
CREATE FUNCTION SplitF(#str AS NVARCHAR(max))
RETURNS #People TABLE
(Rowno INT,Item NVARCHAR(10))
AS
BEGIN
DECLARE #i INT, #pos INT
DECLARE #subname NVARCHAR(max)
SET #I = 0;
WHILE(LEN(#str)>0)
BEGIN
SET #pos = CHARINDEX(',',#str)
IF #pos = 0 SET #pos = LEN(#str)+1
SET #subname = SUBSTRING(#str,1,#pos-1)
SET #str = SUBSTRING(#str, #pos+1, len(#str))
SET #i = #i + 1
INSERT INTO #People VALUES (#i, #subname)
END
RETURN
END
GO
select * from SplitF('test,my,function')
Rowno Item
----------- ----------
1 test
2 my
3 function
and basic data:
select Ref, Names, Surname, DOB, IdNo into #clients
from ( select 123 as Ref, 'John,Sally' as Names, 'Smith' as Surname,
'DOB1,DOB2' as DOB, '45,56' as IdNo
union all select 456, 'Dave,Paul','Jones,Dann','DOB1,DOB2', '98'
union all select 789, 'Mary,Moe,Al', 'Lee', 'DOB1', NULL) A
select * from #clients
Ref Names Surname DOB IdNo
----------- ----------- ---------- --------- -----
123 John,Sally Smith DOB1,DOB2 45,56
456 Dave,Paul Jones,Dann DOB1,DOB2 98
789 Mary,Moe,Al Lee DOB1 NULL
using below code you will get such results:
select
Ref,
RTrim(S_NAM.Item) as Names,
coalesce(S_SURNAM.Item,S_SURNAM_LAST.Item) AS Surname,
coalesce(split_dob.Item, '') as DOB,
coalesce(split_IdNo.Item,'') as IdNo
from
#clients MAIN
outer apply(select Rowno, Item from SplitF(MAIN.Names)) as S_NAM
outer apply(select top 1 Item from SplitF(MAIN.Surname) where Rowno = S_NAM.Rowno) as S_SURNAM
outer apply(select top 1 Item from SplitF(MAIN.Surname) order by Rowno desc) as S_SURNAM_LAST
outer apply(select top 1 Item from SplitF(MAIN.IdNo) where Rowno = S_NAM.Rowno) as split_IdNo
outer apply(select top 1 Item from SplitF(MAIN.DOB) where Rowno = S_NAM.Rowno) as split_dob
order by MAIN.Ref, S_NAM.Rowno
Ref Names Surname DOB IdNo
----------- ---------- ---------- ---------- ----------
123 John Smith DOB1 45
123 Sally Smith DOB2 56
456 Dave Jones DOB1 98
456 Paul Dann DOB2
789 Mary Lee DOB1
789 Moe Lee
789 Al Lee
I think you can handle this using subqueries and doing the RowNo comparison before the OUTER APPLY:
FROM Clients c CROSS APPLY
dbo.udf_SplitString(Names, ',') SplitForenames OUTER APPLY
(SELECT . . .
FROM dbo.udf_SplitString(Surname, ',') SplitSurname
WHERE SplitSurname.RowNo = SplitForenames.RowNo
) SplitSurname OUTER APPLY
(SELECT . . .
FROM dbo.udf_SplitString(DOB, ',') SplitDOB
WHERE SplitDOB.RowNo = SplitForenames.RowNo
) SplitDOB OUTER APPLY
(SELECT . . .
FROM dbo.udf_SplitString(DOB, ',') SplitNI
WHERE SplitNI.RowNo = SplitForenames.RowNo
) SplitNI

SQL Server query to see what changed from month to month

I am struggling with developing a query to compare changes in a single table from month to month, example data -
+-----------------------------------------------------------+
| TaxGroupDetails |
+-----------+--+----------+--+-----------+--+---------------+
| Tax Group | | Tax Type | | Geocode | | EffectiveDate |
+-----------+--+----------+--+-----------+--+---------------+
| 2001 | | 1D | | 440011111 | | 1120531 |
| 2001 | | X1 | | 440011111 | | 1120531 |
| 2001 | | D3 | | 440011111 | | 1120531 |
| 2001 | | DGH | | 440011111 | | 1120531 |
| 2001 | | 1D | | 440011111 | | 1130101 |
| 2001 | | X1 | | 440011111 | | 1130101 |
| 2001 | | D3 | | 440011111 | | 1130101 |
| 2001 | | 1D | | 440011111 | | 1140201 |
| 2001 | | X1 | | 440011111 | | 1140201 |
| 2001 | | D3 | | 440011111 | | 1140201 |
| 2001 | | Z9 | | 440011111 | | 1140201 |
+-----------+--+----------+--+-----------+--+---------------+
I want to see the changes in the table, what was added or removed from a taxgroup, between the top two effective dates.
The results I am trying to obtain based on the sample data would be Z9 (added) if I was running the query in February (1140201) of this year.
If I was running the query in January (1130101) of last year I would expect to see DGH (removed)
I would expect two seperate queries, one to show what was added and another to show what was removed.
I have tried multiple avenues to come up with these two queries but cant seem to obtain the correct results. Can anyone point me in the right direction ?
SELECT
Current.TaxGroup,
Current.TaxType,
Current.GeoCode,
'Added'
FROM
TaxGroupDetails AS Current
WHERE
Current.EffectiveDate = #CurrentPeriod AND
NOT EXISTS
(
SELECT *
FROM TaxGroupDetails As Previous
WHERE
Previous.EffectiveDate = #PreviousPeriod
Current.TaxGroup = Previous.TaxGroup and
Current.TaxType = Previous.TaxType and
Current.GeoCode = Previous.GeoCode
)
UNION ALL
SELECT
Current.TaxGroup,
Current.TaxType,
Current.GeoCode,
'Added'
FROM
TaxGroupDetails AS Previous
WHERE
Previous.EffectiveDate = #PreviousPeriod AND
NOT EXISTS
(
SELECT *
FROM TaxGroupDetails As Current
WHERE
Current.EffectiveDate = #CurrentPeriod
Current.TaxGroup = Previous.TaxGroup and
Current.TaxType = Previous.TaxType and
Current.GeoCode = Previous.GeoCode
)
As you say you need two queries, one to select each of the two groups of data you want to compare.
SELECT [Tax Group], [Tax Type], [Geocode], [EffectiveDate]
FROM TaxGroupDetails
WHERE EffectiveDate = 1120531
SELECT [Tax Group], [Tax Type], [Geocode], [EffectiveDate]
FROM TaxGroupDetails
WHERE EffectiveDate = 1140201
You then need to join these two together using some form of key, the combination of tax group and tax type seems sensible here.
SELECT *
FROM
(
SELECT [Tax Group], [Tax Type], [Geocode], [EffectiveDate]
FROM TaxGroupDetails
WHERE EffectiveDate = 1120531
) AS FirstGroup
FULL OUTER JOIN
(
SELECT [Tax Group], [Tax Type], [Geocode], [EffectiveDate]
FROM TaxGroupDetails
WHERE EffectiveDate = 1140201
) AS SecondGroup
ON FirstGroup.[Tax Group] = SecondGroup.[Tax Group]
AND FirstGroup.[Tax Type] = SecondGroup.[Tax Type]
The FULL OUTER JOIN here tells SQL to include the remaining row when the other doesn't exist.
Finally let's tidy up and order the columns and not use a *:
SELECT COALESCE(FirstGroup.[Tax Group], SecondGroup.[Tax Group]),
COALESCE(FirstGroup.[Tax Type], SecondGroup.[Tax Type]),
FirstGroup.Geocode, SecondGroup.Geocode,
FirstGroup.EffectiveDate, SecondGroup.EffectiveDate
FROM
.
.
.
COALESCE removes the NULLs from the first matched columns and as we are saying these muct be equal there is no point showing both copies.
The set-based solution: take the difference between the whole table and the whole table with all dates projected forward by one time interval. That will eliminate all rows except the ones with "new" codes.
SELECT
[TaxGroup],
[Tax Type],
[EffectiveDate]
FROM TaxGroupDetails t
EXCEPT
SELECT
[TaxGroup],
[Tax Type],
( SELECT MIN([EffectiveDate])
FROM TaxGroupDetails
WHERE [EffectiveDate] > t.[EffectiveDate]
AND [TaxGroup] = t.[TaxGroup]
)
FROM TaxGroupDetails t
To see what got deleted, project backwards instead. Change the subquery to:
SELECT MAX([EffectiveDate])
FROM TaxGroupDetails
WHERE [EffectiveDate] < t.[EffectiveDate]
AND [TaxGroup] = t.[TaxGroup]
If you have SQL2012:
WITH t AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY [TaxGroup], [Tax Type] ORDER BY [EffectiveDate] ASC) rownum
FROM [TaxGroup]
)
SELECT *
FROM t
WHERE rownum = 1
AND [EffectiveDate] = #Date
To get the other query, change ASC to DESC
Try this / you could start from this [partial] solution:
DECLARE #MyTable TABLE (
ID INT IDENTITY PRIMARY KEY,
[Tax Group] SMALLINT NOT NULL,
[Tax Type] VARCHAR(3) NOT NULL,
[Geocode] INT NOT NULL,
[EffectiveDate] INT NOT NULL
);
INSERT #MyTable
SELECT 2001, '1D ', 440011111, 1120531
UNION ALL SELECT 2001, 'X1 ', 440011111, 1120531
UNION ALL SELECT 2001, 'D3 ', 440011111, 1120531
UNION ALL SELECT 2001, 'DGH', 440011111, 1120531
UNION ALL SELECT 2001, '1D ', 440011111, 1130101
UNION ALL SELECT 2001, 'X1 ', 440011111, 1130101
UNION ALL SELECT 2001, 'D3 ', 440011111, 1130101
UNION ALL SELECT 2001, '1D ', 440011111, 1140201
UNION ALL SELECT 2001, 'X1 ', 440011111, 1140201
UNION ALL SELECT 2001, 'D3 ', 440011111, 1140201
UNION ALL SELECT 2001, 'Z9 ', 440011111, 1140201;
DECLARE #Results TABLE (
ID INT NOT NULL,
Rnk INT NOT NULL,
EffectiveYear SMALLINT NOT NULL,
PRIMARY KEY (Rnk, EffectiveYear)
);
INSERT #Results
SELECT x.ID,
DENSE_RANK() OVER(ORDER BY x.[Tax Group], x.[Tax Type], x.[Geocode]) AS Rnk,
x.EffectiveDate / 10000 AS EffectiveYear
FROM #MyTable x;
SELECT
crt.*,
prev.*,
CASE
WHEN crt.ID IS NOT NULL AND prev.ID IS NOT NULL THEN '-' -- No change
WHEN crt.ID IS NULL AND prev.ID IS NOT NULL THEN 'D' -- Deleted
WHEN crt.ID IS NOT NULL AND prev.ID IS NULL THEN 'I' -- Inserted
END AS RowStatus
FROM #Results crt FULL OUTER JOIN #Results prev ON crt.Rnk = prev.Rnk
AND crt.EffectiveYear - 1 = prev.EffectiveYear
ORDER BY ISNULL(crt.EffectiveYear - 1, prev.EffectiveYear), crt.Rnk;
Sample output:
---- ---- ------------- ---- ---- -------------
| Current data | | Previous data |
---- ---- ------------- ---- ---- ------------- ---------
ID Rnk EffectiveYear ID Rnk EffectiveYear RowStatus
---- ---- ------------- ---- ---- ------------- ---------
1 1 112 NULL NULL NULL I -- Current vs. previous: current row hasn't a previous row
3 2 112 NULL NULL NULL I -- the same thing
4 3 112 NULL NULL NULL I -- the same thing
2 4 112 NULL NULL NULL I -- the same thing
NULL NULL NULL 4 3 112 D <-- Deleted: ID 4 = 'DGH'
5 1 113 1 1 112 - -- there is no change
7 2 113 3 2 112 -
6 4 113 2 4 112 -
8 1 114 5 1 113 -
10 2 114 7 2 113 -
9 4 114 6 4 113 -
11 5 114 NULL NULL NULL I <-- Inserted: ID 11 = 'Z9'
NULL NULL NULL 8 1 114 D
NULL NULL NULL 10 2 114 D
NULL NULL NULL 9 4 114 D
NULL NULL NULL 11 5 114 D
Note: I assume that there are no duplicated rows (x.[Tax Group], x.[Tax Type], x.[Geocode]) within a year.

Date-based multiple group by T-SQL query

First of all, execuse the longer question, but I will try to put it as simply as possible...
I'm trying to write a kind of a reporting query, but I'm having a problem getting the desired results. The problem:
Employee table
Id | Name
---------------
1 | John Smith
2 | Alan Jones
3 | James Jones
Task table
Id | Title | StartDate | EmployeeId | Estimate (integer - ticks)
----------------------------------------------------------------------------
1 | task1 | 21.08.2011 | 1 | 90000000000
2 | task2 | 21.08.2011 | 1 | 150000000
3 | task3 | 22.08.2011 | 2 | 1230000000
Question:
How to get the estimate summary per day, grouped, but to include all the employees?
Like this:
Date | EmployeeId | EmployeeName | SummaryEstimate
-------------------------------------------------------------
19.08.2011 | 1 | John Smith | NULL
19.08.2011 | 2 | Alan Jones | NULL
19.08.2011 | 3 | James Jones | NULL
20.08.2011 | 1 | John Smith | NULL
20.08.2011 | 2 | Alan Jones | NULL
20.08.2011 | 3 | James Jones | NULL
21.08.2011 | 1 | John Smith | 90150000000
21.08.2011 | 2 | Alan Jones | NULL
21.08.2011 | 3 | James Jones | NULL
22.08.2011 | 1 | John Smith | NULL
22.08.2011 | 2 | Alan Jones | 1230000000
22.08.2011 | 3 | James Jones | NULL
What I currently do is I have a "dates" table with 30years of days. I left join and group by that table to get other dates included too. Well, here is the query:
SELECT dates.value, employee.Id, employee.Name, sum(task.Estimate)
FROM TableOfDates as dates
left join Tasks as task on (dates.value = convert(varchar(10), task.StartTime, 101))
left join Employees as employee on (employee.Id = task.EmployeeId)
WHERE dates.value >= '2011-08-19' and dates.value < '2011-08-22'
GROUP BY dates.value, employee.Id, employee.Name
ORDER BY dates.value, employee.Id
The convert call is to get the date part of the DateTime column.
The result that I get is:
Date | EmployeeId | EmployeeName | SummaryEstimate
-------------------------------------------------------------
19.08.2011 | NULL | NULL | NULL
20.08.2011 | NULL | NULL | NULL
21.08.2011 | 1 | John Smith | 90150000000
22.08.2011 | 2 | Alan Jones | 1230000000
I am there half of the way, I get dates that are not in the two base joined tables (Employees and Tasks) but I cannot also have all the employees included as in the table shown before this one.
I've tried cross-joining, then subqueries, but little luck there. Any help would be very much appreciated ! Thank you for having the time to go through all of this, I hope I was clear enough...
SELECT DE.DateValue, DE.EmployeeId, DE.EmployeeName, sum(task.Estimate)
FROM
( SELECT
D.value AS DateValue
, E.Id AS EmployeeId
, E.Name AS EmployeeName
FROM
TableOfDates D
CROSS JOIN Employees E ) DE
left join Tasks as task on DE.DateValue = convert(varchar(10), task.StartTime, 101)
AND DE.EmployeeId = task.EmployeeId
WHERE DE.DateValue >= '2011-08-19' and DE.DateValue < '2011-08-22'
GROUP BY DE.DateValue, DE.EmployeeId, DE.EmployeeName
ORDER BY DE.DateValue, DE.EmployeeId
Note that this solution offers the possibility to drop the day-table as you may use a dynamic recursive CTE instead.
The other CTE:s (Employees and Tasks) can be substituted with the real tables.
DECLARE #startDate DATETIME = '2011-08-01'
DECLARE #endDate DATETIME = '2011-09-01'
;WITH Employees(Id,Name)
AS
(
SELECT 1, 'John Smith'
UNION ALL
SELECT 2, 'Alan Jones'
UNION ALL
SELECT 3, 'James Jones'
)
,Tasks (Id, Title, StartDate, EmployeeId, Estimate)
AS
(
SELECT 1, 'task1', '2011-08-21', 1, 90000000000
UNION ALL
SELECT 2, 'task2', '2011-08-21', 1, 150000000
UNION ALL
SELECT 3, 'task3', '2011-08-22', 2, 1230000000
)
,TableOfDates(value)
AS
(
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, #startDate), 0)
UNION ALL
SELECT DATEADD(DAY, 1, value)
FROM TableOfDates
WHERE value < #endDate
)
SELECT dates.value
,employee.Id
,employee.Name
,SUM(task.Estimate) AS SummaryEstimate
FROM TableOfDates dates
CROSS JOIN Employees employee
LEFT JOIN Tasks task
ON dates.value = task.StartDate
AND (employee.Id = task.EmployeeId)
WHERE dates.value >= '2011-08-19'
AND dates.value < '2011-08-26'
GROUP BY
dates.value
,employee.Id
,employee.Name
ORDER BY
dates.value
,employee.Id
use this query:
create table #T_dates (id_date int identity(1,1),inp_date datetime)
create table #T_tasks (id_task int identity(1,1),key_date int, key_emp int, est int)
create table #T_emp (id_emp int identity(1,1),name varchar(50))
insert #T_dates (inp_date) values ('08.19.2011')
insert #T_dates (inp_date) values ('08.20.2011')
insert #T_dates (inp_date) values ('08.21.2011')
insert #T_dates (inp_date) values ('08.22.2011')
insert #T_dates (inp_date) values ('08.23.2011')
insert #T_dates (inp_date) values ('08.24.2011')
--select * from #T_dates
insert #T_emp (name) values ('John Smith')
insert #T_emp (name) values ('Alan Jones')
insert #T_emp (name) values ('James Jones')
--select * from #T_emp
insert #T_tasks (key_date,key_emp,est) values (4,1,900000)
insert #T_tasks (key_date,key_emp,est) values (4,1,15000)
insert #T_tasks (key_date,key_emp,est) values (5,2,123000)
--select * from #T_tasks
select inp_date,id_emp,name,EST
from #T_emp
cross join #T_dates
left join
(
select key_date,key_emp,SUM(est) 'EST' from #T_tasks group by key_date,key_emp
) Gr
ON Gr.key_emp = id_emp and Gr.key_date = id_date
where inp_date >= '2011-08-19' and inp_date <= '2011-08-22'
order by inp_date,id_emp

Resources