Query with subquery-count and groupby - sql

Below is the ERD
I want to count number of gender ('Male' and 'Female') for each month irrespective of year.
What I have tried so far is that I can count number of males and females for each month separately like below
Query
Select u.Gender, datename(month, p.EntryDate) month, COUNT(p.User_Id) count
from [HospitalManagement].[dbo].[Patients] p,[HospitalManagement].[dbo].[Users] u
where u.Id = p.User_Id
group by datename(month, p.EntryDate), u.Gender
Result
I want it like below
Expected Result
Month | MaleCount | FemaleCount
June | 0 | 2
November | 1 | 1
To achieve above I try following query
Query
Select datename(month, p.EntryDate) month,
(select count(u.gender) from [HospitalManagement].[dbo].[Users] u
where u.Id = p.User_Id and u.Gender = 'Female'
group by u.Gender) female,
(select count(u.gender) from [HospitalManagement].[dbo].[Users] u
where u.Id = p.User_Id and u.Gender = 'Male'
group by u.Gender) male
from [HospitalManagement].[dbo].[Patients] p
group by datename(month, p.EntryDate)
Error
Column 'HospitalManagement.dbo.Patients.User_Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Below are the create statements of tables (I am using MSSql)
-- Creating table 'Users'
CREATE TABLE [dbo].[Users] (
[Id] bigint IDENTITY(1,1) NOT NULL,
[Email] nvarchar(max) NULL,
[Password] nvarchar(max) NULL,
[UserName] nvarchar(max) NULL,
[Age] bigint NULL,
[Gender] nvarchar(max) NULL,
[NRIC] nvarchar(max) NULL,
[Comments] nvarchar(max) NULL,
[Address] nvarchar(max) NULL,
[ContactNo] nvarchar(max) NULL,
[FullName] nvarchar(max) NULL
);
GO
-- Creating table 'Patients'
CREATE TABLE [dbo].[Patients] (
[Id] bigint IDENTITY(1,1) NOT NULL,
[Disease] nvarchar(max) NULL,
[Occupation] nvarchar(max) NULL,
[EntryDate] datetime NULL,
[EntryTime] time NULL,
[User_Id] bigint NOT NULL
);
GO

As the error says, you are directly using User_Id column in select clause which is not present in the GROUP BY.
You can change the correlated subqueries to faster LEFT JOINs (assuming the User_Id is unique in the Patients table).
select
datename(month, p.EntryDate) mon,
count(case when u.Gender = 'Female' then 1 end) female_cnt,
count(case when u.Gender = 'Male' then 1 end) male_cnt
from [Patients] p left join [Users] u
on p.User_Id = u.Id
group by datename(month, p.EntryDate);
EDIT:
you can use a lookup CTE to generate all months and then do LEFT JOIN with it like this:
;WITH months(mn, mon) AS
(
SELECT 1, DATENAME(MONTH,DATEADD(month,0,GETDATE())) mon
UNION ALL
SELECT mn+1, DATENAME(MONTH,DATEADD(MONTH,mn,GETDATE()))
FROM months
WHERE mn < 12
)
select
m.mon mon,
count(case when u.Gender = 'Female' then 1 end) female_cnt,
count(case when u.Gender = 'Male' then 1 end) male_cnt
from months m left join [Patients] p
on m.mon = datename(month, p.EntryDate)
left join [Users] u
on p.User_Id = u.Id
group by m.mon;

You can try the following query:
Select
datename(month, p.EntryDate) month,
COUNT(IF(u.Gender = 'Male', 1, NULL) AS MaleCount,
COUNT(IF(u.Gender = 'Female', 1, NULL) AS FemaleCount
from
[HospitalManagement].[dbo].[Patients] p,[HospitalManagement].[dbo].[Users] u
where
u.Id = p.User_Id
group by
datename(month, p.EntryDate)

This is how we do in MySQL or Oracle. Have not tried in MSSQL though. But as these are standard SQL function (and is available in MSSQL), this should work in MSSQL as well.
Select datename(month, p.EntryDate) month , SUM(CASE WHEN u.Gender = 'Male' THEN 1 ELSE 0) MaleCount, SUM(CASE WHEN u.Gender = 'Female' THEN 1 ELSE 0) FemaleCount,
from [HospitalManagement].[dbo].[Patients] p,[HospitalManagement].[dbo].[Users] u
where u.Id = p.User_Id
group by datename(month, p.EntryDate)
Typically I am adding 1 in case of the specific gender to get the result.
Note: If there are any syntax errors please let me know with the error, I can correct it.

Related

How to get the quarter date output for the below query

I have the below code, which is giving me the data for each monthend date. I want this to provide me with the quarter end dates only.
DECLARE #LegalName AS VARCHAR(255) = LOWER ('xx')
DECLARE #IndexId AS VARCHAR (255) ='xx'-------------provide the index legal name
; with CTE as (
SELECT DISTINCT ph.[Date],ph.IndexShares, EDD.[Close], EDD.[LocalCurrency], dsm.PerformanceId, ds.SecurityName COLLATE Latin1_General_BIN AS SecurityName, ds.TICKER COLLATE Latin1_General_BIN AS Ticker, icm.SectorName COLLATE Latin1_General_BIN AS SectorName,icm.IndustryName, ds.ISIN COLLATE Latin1_General_BIN AS ISIN, coc.MsCountry,--C.COUNTRY as Country,
ph.MarketValue, ph.ThirdPartyId, ds.SEDOL COLLATE Latin1_General_BIN AS SEDOL, ds.MIC
FROM TimeSeries..PortfolioHoldings ph
INNER JOIN TimeSeries.dbo.EquityDailyData AS EDD
ON PH.ThirdPartyId = EDD.ThirdPartyId
AND PH.[Date] = EDD.[Date]
LEFT JOIN StagingData..DMA_DimSecurityMapping dsm
ON dsm.ThirdPartyId = ph.ThirdPartyId
AND ph.Date BETWEEN dsm.StartDate AND dsm.EndDate
LEFT JOIN StagingData.dbo.DMA_DimCompanyCoC coc
ON dsm.CompanyId=coc.CompanyId
AND ph.Date BETWEEN coc.StartDate AND coc.EndDate
LEFT JOIN StagingData.dbo.DMA_DimCompanyIndustry dci
ON dsm.CompanyId= dci.CompanyId
AND ph.Date BETWEEN dci.StartDate AND dci.EndDate
LEFT JOIN IDW.[dbo].[GECSIndustryMapping]icm
ON dci.IndustryId= icm.IndustryCode
AND ph.Date BETWEEN dsm.StartDate AND dsm.EndDate
LEFT JOIN StagingData.dbo.DMA_DimSecurity ds
ON dsm.PerformanceId = ds.PerformanceId
AND ph.Date BETWEEN ds.StartDate AND ds.EndDate
WHERE ph.PortfolioId in
(
SELECT CAST (PortfolioId AS VARCHAR (16)) COLLATE Latin1_General_BIN
FROM MSTAR_INDEX..Indexidentifier
WHERE Name = #LegalName
AND IndexStatus = 1
)
AND ph.[Date] IN (SELECT MAX([Date]) FROM Timeseries.dbo.IndexLevel WHERE IndexId = #IndexId AND YEAR([Date])>'2020' AND Date <'2022-09-01' GROUP BY EOMONTH([Date]))
)
select t.[Date],SectorName, IndustryName, SUM([Weight]) as Weights
from (select DISTINCT *, 100*CAST (c.MarketValue as FLoat)/sum (c.MarketValue) over (Partition by c.[Date]) as [Weight]
FROM CTE as c
) as t
GROUP BY t.[Date],SectorName,IndustryName
The desired result would be to get the data for each quarterend date for 2022 - March 31 2022, June 30th 2022 , Sept 30th 2022 , December 30th 2022 ( all the quarter end date / date preceding the quarterend which is on weekend )
You can use the following expression to calculate the end of quarter. Looks a bit ugly but works.
DECLARE #date DATETIME = '20230421'
SELECT EOMONTH(#date, CASE DATEPART(MONTH, #date) % 3 WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE 0 END)

SQL Server - Aggregate the number of errors per user per day

The application that I maintain stores user errors in a SQL Server table. When an error occurs it jots down the username of the person that caused it, the time, the error message, and some other housekeeping stuff.
I'm trying to build out a report where we could see the "top 3" errors each day for the past year - the three users with the most errors on each day, the three most common types of errors on each day, etc.
My goal is something like this:
DATE USER1 ERR_COUNT1 USER2 ERR_COUNT2 USER3 ERR_COUNT3
1/1/18 BOB 70 BILL 50 JOE 30
1/2/18 JILL 55 JOY 30 BOB 20
...
I've got a rough loop set up to pull this data from our error logs but when I run it I get the error There is already an object named '#TempErrorLog'in the database. Loop code below:
DECLARE #StartDate AS DATE,
#EndDate AS DATE
SET #StartDate = '2018.1.1'
WHILE #StartDate <= CONVERT(DATE, GETDATE())
BEGIN
SET #EndDate = DATEADD(DAY, 1, #StartDate)
SELECT #StartDate AS date_err,
( u.name_frst+' '+u.name_lst ) AS user_err,
COUNT(e.id_err) AS count_err
INTO dbo.#TempErrLog
FROM err_log AS e
LEFT JOIN users AS u ON e.id_user = u.id_user
WHERE e.dttm_err >= #StartDate AND
e.dttm_err < #EndDate AND
e.id_user <> 'system'
GROUP BY ( u.name_frst+' '+u.name_lst )
ORDER BY count_err DESC;
SET #StartDate = DATEADD(DAY, 1, #StartDate)
CONTINUE
END
SELECT * FROM #TempErrLog
My guess is that it is trying to create a new temporary table each time the loop iterates. Is there a better approach I should be using here?
You can pivot this using conditional aggregation and row_number(). For the results in your question:
with ue as (
select e.*, (u.name_frst + ' ' + u.name_lst ) as user_name,
cast(e.dttm_err as date) as err_date
from err_log e join
users u
on e.id_user = u.id_user
)
select u.err_date,
max(case when u.seqnum = 1 then u.user_name end) as user_1,
max(case when u.seqnum = 1 then u.cnt end) as cnt_1,
max(case when u.seqnum = 2 then u.user_name end) as user_2,
max(case when u.seqnum = 2 then u.cnt end) as cnt_2,
max(case when u.seqnum = 3 then u.user_name end) as user_3,
max(case when u.seqnum = 3 then u.cnt end) as cnt_3
from (select err_date, user_name, count(*) as cnt,
row_number() over (partition by err_date order by count(*) desc) as seqnum
from ul
group by err_date, user_name
) u
group by u.err_date

Multiple Sub-Queries In A SQL Query

I am creating a sample query that'll convert rows to column something as follows:
Person_Id Total Earned Leave Earned Leave Enjoyed Remaining Earned Leave Total Casual Leave Casual Leave Enjoyed Remaining Casual Leave
1001 20 10 10 20 4 16
So above is the output I get and used multiple sub-queries using the following query:
SELECT DISTINCT m.Person_Id, (SELECT k.Leave_Allocation FROM LeaveDetails k WHERE k.Leave_Name = 'Earn Leave'
AND k.Person_Id = 1001 AND k.[Year] = '2017') AS 'Total Earned Leave',
(SELECT o.Leave_Enjoy FROM LeaveDetails o WHERE o.Leave_Name = 'Earn Leave'
AND o.Person_Id = 1001 AND o.[Year] = '2017') AS 'Earned Leave Enjoyed',
(SELECT p.Leave_Remain FROM LeaveDetails p WHERE p.Leave_Name = 'Earn Leave'
AND p.Person_Id = 1001 AND p.[Year] = '2017') AS 'Remaining Earned Leave',
(SELECT k.Leave_Allocation FROM LeaveDetails k WHERE k.Leave_Name = 'Casual Leave'
AND k.Person_Id = 1001 AND k.[Year] = '2017') AS 'Total Casual Leave',
(SELECT o.Leave_Enjoy FROM LeaveDetails o WHERE o.Leave_Name = 'Casual Leave'
AND o.Person_Id = 1001 AND o.[Year] = '2017') AS 'Casual Leave Enjoyed',
(SELECT p.Leave_Remain FROM LeaveDetails p WHERE p.Leave_Name = 'Casual Leave'
AND p.Person_Id = 1001 AND p.[Year] = '2017') AS 'Remaining Casual Leave'
FROM LeaveDetails m WHERE m.Person_Id = 1001 AND m.[Year] = '2017'
I am not sure if I am going to have performance issue here as there will be lots of data and was arguing if this will be better than Pivot or Run-Time Table Creation. I just want to make sure if this is going to be a better choice for the purpose I am trying to accomplish. You can share your ideas as well samples using SQL Server, MySQL or Oracle for better performance issue - Thanks.
Sample Table and Data:
CREATE TABLE [dbo].[LeaveDetails](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Person_Id] [nvarchar](20) NULL,
[Leave_Name] [nvarchar](40) NULL,
[Leave_Allocation] [float] NULL,
[Leave_Enjoy] [float] NULL,
[Leave_Remain] [float] NULL,
[Details] [nvarchar](100) NULL,
[Year] [nvarchar](10) NULL,
[Status] [bit] NULL
)
INSERT [dbo].[LeaveDetails] ([Id], [Person_Id], [Leave_Name], [Leave_Allocation], [Leave_Enjoy], [Leave_Remain], [Details], [Year], [Status]) VALUES (1, N'1001', N'Earn Leave', 20, 10, 10, NULL, N'2017', 1)
INSERT [dbo].[LeaveDetails] ([Id], [Person_Id], [Leave_Name], [Leave_Allocation], [Leave_Enjoy], [Leave_Remain], [Details], [Year], [Status]) VALUES (2, N'1001', N'Casual Leave', 20, 4, 16, NULL, N'2017', 1)
Use conditional aggregation:
SELECT m.Person_Id,
MAX(CASE WHEN m.Leave_Name = 'Earn Leave' THEN k.Leave_Allocation END) as [Total Earned Leave],
MAX(CASE WHEN m.Leave_Name = 'Earn Leave' THEN m.Leave_Enjoy END) as [Earned Leave Enjoyed],
MAX(CASE WHEN m.Leave_Name = 'Earn Leave' THEN m.Leave_Remain END) as [Remaining Earned Leave],
MAX(CASE WHEN m.Leave_Name = 'Casual Leave' THEN k.Leave_Allocation END) as [Total Casual Leave],
MAX(CASE WHEN m.Leave_Name = 'Casual Leave' THEN k.Leave_Remain END) as [Casual Leave Enjoyed],
MAX(CASE WHEN m.Leave_Name = 'Casual Leave' THEN k.Leave_Remain END) as [Remaining Casual Leave]
FROM LeaveDetails m
WHERE m.Person_Id = 1001 AND m.[Year] = '2017'
GROUP BY m.Person_ID;
Note: I do not advocate having special characters (such as spaces) in column aliases. If you do, use the proper escape character (square braces). Only use single quotes for string and date constants.
PIVOT would work, but it looks like this is simply a single row that you want pivoted to a columnar output and the column names are known explicitly. If that's the case, you could just UNION the single column results together:
SELECT 'Person_ID' as col_name, Person_Id as col_value FROM LeaveDetails WHERE Person_Id = 1001 AND [Year] = '2017'
UNION
SELECT 'Leave_Enjoy' as col_name, Leave_Enjoy as col_value FROM LeaveDetails WHERE Person_Id = 1001 AND [Year] = '2017'
UNION
...
It's a lot simpler to write, cleaner to read, and should run a little faster - there is still one table scan for each column. Is the table indexed on Person_ID and Year?
If speed is an issue you could create a temp table of the one row:
SELECT * into #ld_temp FROM LeaveDetails WHERE Person_Id = 1001 AND [Year] = '2017'
then select from the temp table in the SELECT/UNION code:
SELECT 'Person_ID' as col_name, Person_Id as col_value FROM #ld_temp
UNION
SELECT 'Leave_Enjoy' as col_name, Leave_Enjoy as col_value FROM #ld_temp
UNION
...
Now you're down to just a single scan of the big table.
I hope this helps.

SQL Server select with multiple groupings

I have two tables describing users and their payments:
CREATE TABLE test_users
(id int IDENTITY NOT NULL,
name varchar(25),
PRIMARY KEY (id));
CREATE TABLE test_payments
(id int IDENTITY NOT NULL,
user_id int NOT NULL,
money money NOT NULL,
date datetime NOT NULL,
PRIMARY KEY (id));
INSERT INTO test_users (name)
VALUES ('john');
INSERT INTO test_users (name)
VALUES ('peter');
INSERT INTO test_payments (user_id, money, date)
VALUES (1, $1, CONVERT(datetime, '15.12.2012'));
INSERT INTO test_payments (user_id, money, date)
VALUES (1, $2, CONVERT(datetime, '16.12.2012'));
INSERT INTO test_payments (user_id, money, date)
VALUES (2, $1, CONVERT(datetime, '16.12.2012'));
INSERT INTO test_payments (user_id, money, date)
VALUES (2, $3, CONVERT(datetime, '17.12.2012'));
INSERT INTO test_payments (user_id, money, date)
VALUES (1, $1, CONVERT(datetime, '19.12.2012'));
Table test_users:
id name
-------------
1 john
2 peter
Table test_payments:
id user_id money last_activity
---------------------------------------
1 1 1.0000 2012-12-15
2 1 2.0000 2012-12-16
3 2 1.0000 2012-12-16
4 2 3.0000 2012-12-17
5 1 1.0000 2012-12-19
I need to make a users statistic which will show me :
username
total fee for a period of time
the date of the last
user's activity (general, not for a time period).
For example taking the period 15-18.12.12 I expect the following results:
name total last_activity
--------------------------------
peter $4 2012-12-17
john $3 2012-12-19
I've tried the following query:
SELECT u.*, SUM(p.money) total, MAX(p.date) last_activity
FROM test_users u
JOIN test_payments p
ON u.id= p.user_id
WHERE p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
GROUP BY u.id, u.name
ORDER BY total DESC;
but getting wrong result for last_activity as it is also in the date range:
id name total last_activity
--------------------------------
2 peter 4.0000 2012-12-17
1 john 3.0000 2012-12-16
Please suggest a solution.
Looks like a couple of other answers popped up while I worked on mine, but here it is anyhow. There is a working sql fiddle here: http://sqlfiddle.com/#!3/14808/6
Basically, you need a query to pull the max date regardless of the date range. I chose to do this as a correlated subquery.
SELECT
u.id,
u.name,
SUM(IsNull(money,0)) as TotalMoneyInRange,
(SELECT max(date) FROM test_payments where user_id = u.id) AS LastPaymentOverAll
FROM test_users AS u
LEFT JOIN test_payments AS p
ON u.id = p.user_id
WHERE
p.date IS NULL OR
p.date between
CAST('12-11-2012' AS datetime) --range begin
and
CAST('12-16-2012' as datetime) --range end
GROUP BY u.id, u.name
You need to move the condition from the where clause to a case statement:
SELECT u.id, u.name,
SUM(case when p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
then p.money
end) total,
MAX(p.date) last_activity
FROM test_users u JOIN
test_payments p
ON u.id= p.user_id
GROUP BY u.id, u.name
ORDER BY total DESC;
If you only want users who had a payment in that period, then you can include:
having total is not null
If you want the NULL values to appear as 0 instead of NULL, then include else 0 in the case statement.
You can also use subqueries to get the result:
SELECT u.*, total, last_activity
FROM test_users u
JOIN
(
select sum(money) total, user_id
from test_payments
WHERE date BETWEEN CONVERT(datetime, '2012-12-15')
AND CONVERT(datetime, '2012-12-18')
group by user_id
) p
ON u.id= p.user_id
inner join
(
select user_id, max(date) last_activity
from test_payments
group by user_id
) p1
on p.user_id = p1.user_id
ORDER BY total DESC;
See SQL Fiddle with Demo
You could add a sub query for the MAX date that doesn't have the WHERE clause like so:
SELECT
u.*
,SUM(p.money) total
,a.max_date last_activity
FROM test_users u
INNER JOIN test_payments p ON u.id = p.user_id
INNER JOIN (SELECT user_id, MAX(date) AS max_date
FROM test_payments
GROUP BY user_id) a ON u.id = a.user_id
WHERE p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
GROUP BY u.id, u.name, a.max_date
ORDER BY total DESC;

Count entries across three tables based on month in SQL or LINQ

I would like to extract some data from three tables in a SQL Server 2005 database. While this can surely be done in code, it seems like this could be done reasonably well in SQL (bonus points for LINQ!).
Basically, I would like to know for each month how many calls and meetings each employee has held with each of our clients. Something like this:
Employee GUID Customer GUID Jan calls Jan mtgs Feb calls Feb mtgs...
[a guid] [another guid] 5 0 7 3
The data is spread across three tables. For simplicity's sake, let's just show the relevant columns:
Communications Table
[CommunicationId] (PK, uniqueidentifier)
[Type] (nvarchar(1)) ('C' for call, 'M' for meeting, etc.)
[Date] (datetime)
Person-Communication Table
[PersonId] (PK, FK, uniqueidentifier) (Can contain GUIDs for employees or clients, see Person Table below)
[CommunicationId] (PK, FK, uniqueidentifier)
Person Table
[PersonId] (PK, uniqueidentifier)
[Type] (nvarchar(1)) ('E' for employee, 'C' for customer)
So, the questions:
Can this be done in SQL without horrendous code or big performance problems?
If so, how? I'd even settle for a good high-level strategy. I'm guessing pivots will play a big role here (particularly the "Complex PIVOT Example"). DATEPART(MONTH, Date) seems like a good method for partitioning the communications by month along the lines of:
SELECT DATEPART(MONTH, Date), COUNT(*)
FROM [CommunicationTable]
WHERE DATEPART(YEAR, Date) = '2009'
GROUP BY DATEPART(MONTH, Date)
ORDER BY DATEPART(MONTH, Date)
... which gets me the number of communications in each month in 2009:
1 2871
2 2639
3 3654
4 2751
5 1773
6 2575
7 2906
8 2398
9 2621
10 2638
11 1705
12 2290
Non PIVOT, CASE using syntax:
WITH summary AS (
SELECT emp.personid AS emp_guid,
cust.personid AS cust_guid,
DATEPART(MONTH, ct.date) AS mon,
ct.type,
COUNT(*) AS num_count
FROM COMMUNICATIONTABLE ct
LEFT JOIN PERSON_COMMUNICATION pc ON pc.communicationid = ct.communicationid
JOIN PERSON emp ON emp.personid = pc.personid
AND emp.type = 'E'
JOIN PERSON cust ON cust.personid = p.personid
AND cust.type = 'C'
WHERE ct.date BETWEEN '2009-01-01' AND '2009-12-31'
GROUP BY emp.personid, cust.personid, DATEPART(MONTH, ct.ate), ct.type)
SELECT s.emp_guid,
s.cust_guid,
MAX(CASE WHEN s.mon = 1 AND s.type = 'C' THEN s.num_count ELSE 0 END) AS "Jan calls",
MAX(CASE WHEN s.mon = 1 AND s.type = 'M' THEN s.num_count ELSE 0 END) AS "Jan mtgs",
... --Copy/Paste two lines, update the month check... and the col alias
FROM summary s
GROUP BY s.emp_guid, s.cust_guid
Use WHERE ct.date BETWEEN '2009-01-01' AND '2009-12-31' because WHERE DATEPART(YEAR, Date) = '2009' can't use an index if one exists on the date column.
This should get you started I did one month for one year for you, you can also add in the date range restrictions:
SELECT PE.PersonID as EmployeeID,PC2.PersonID as CustomerID,
SUM(CASE WHEN DATEPART(MONTH, C.[Date]) = 1
AND DATEPART(YEAR,C.[Date]) = 2009
AND C.[type] = 'C' THEN 1 ELSE 0 END) AS [Jan 2009 Calls]
FROM PersonTable PE
JOIN PersonCommunicationTable PC ON PE.PersonID = PC.PersonID
JOIN CommunicationsTable C ON PC.CommunicationID = C.CommunicationID
JOIN PersonCommunicationTable PC2 ON PC.CommunicationID = PC2.CommunicationID AND NOT PC2.PersonID = PC.PersonID
WHERE PE.Type = 'E'
Here is a reasonably equivalent solution using Pivot.
Declare #Comm TABLE
(
[CommunicationId] uniqueidentifier PRIMARY KEY DEFAULT NEWID(),
[Type] nvarchar(1), -- ('C' for call, 'M' for meeting, etc.)
[Date] datetime
)
Declare #Person TABLE
(
[PersonId] uniqueidentifier PRIMARY KEY DEFAULT NEWID(),
[Type] Nvarchar(1) -- ('E' for employee, 'C' for customer)
)
Declare #PersonComm TABLE
(
[PersonId] uniqueidentifier, -- (Can contain GUIDs for employees or clients, see Person Table below)
[CommunicationId] uniqueidentifier
)
INSERT INTO #Person(Type)
Select 'C' UNION ALL Select 'E' UNION ALL Select 'C' UNION ALL Select 'E'
INSERT INTO #Comm([Type],[Date])
Select 'C', '01/04/2010' UNION ALL Select 'C', '01/04/2010'
UNION ALL Select 'C', '04/04/2010' UNION ALL Select 'C', '05/01/2010'
UNION ALL Select 'C', '08/04/2009' UNION ALL Select 'C', '09/01/2009'
UNION ALL Select 'M', '01/04/2010' UNION ALL Select 'M', '03/20/2010'
UNION ALL Select 'M', '04/04/2010' UNION ALL Select 'M', '06/01/2010'
UNION ALL Select 'M', '04/10/2009' UNION ALL Select 'M', '04/10/2009'
INSERT INTO #PersonComm
Select E.PersonID , Comm.[CommunicationId]
FROM #Person E
,#Comm Comm
Where E.[Type] = 'E'
INSERT INTO #PersonComm
Select E.PersonID , Comm.[CommunicationId]
FROM #Person E
,#Comm Comm
Where E.[Type] = 'C'
Select EmployeeID,
ClientID,
Year,
[JanuaryC] AS [Jan Calls],
[JanuaryM] AS [Jan Meetings],
[FebruaryC],
[FebruaryM],
[MarchC],
[MarchM],
[AprilC],
[AprilM],
[MayC],
[MayM],
[JuneC],
[JuneM],
[JulyC],
[JulyM],
[AugustC],
[AugustM],
[SeptemberC] ,
[SeptemberM],
[OctoberC] ,
[OctoberM],
[NovemberC],
[NovemberM],
[DecemberC],
[DecemberM]
FROM
(
Select P.PersonId EmployeeID, Client.PersonId ClientID, YEAR(C.Date) Year, DateName(m,C.Date) Month, COUNT(*) Amount, C.Type CommType,
DateName(m,C.Date) + C.Type PivotColumn -- JanuaryC
FROM #Comm C
INNER JOIN #PersonComm PC
ON PC.CommunicationId = C.CommunicationId
INNER JOIN #Person P
ON P.PersonId = PC.PersonId
INNER JOIN #PersonComm PCC
ON PCC.CommunicationId = PC.CommunicationId
INNER JOIN #Person Client
ON Client.PersonId = PCC.PersonId AND Client.Type = 'C'
Where P.Type = 'E'
Group By P.PersonId, CLient.PersonId, YEAR(C.Date), DateName(m,C.Date), C.Type
) SourceTable
PIVOT (
MAX(Amount)
FOR PivotColumn IN
([JanuaryC], [JanuaryM],[FebruaryC], [FebruaryM],[MarchC], [MarchM], [AprilC], [AprilM], [MayC], [MayM], [JuneC], [JuneM], [JulyC], [JulyM],
[AugustC], [AugustM],[SeptemberC] , [SeptemberM],[OctoberC] ,[OctoberM],[NovemberC], [NovemberM], [DecemberC], [DecemberM]
)
)As PivotTable