Conditionally Summing the same Column multiple times in a single select statement? - sql

I have a single table that shows employee deployments, for various types of deployment, in a given location for each month:
ID | Location_ID | Date | NumEmployees | DeploymentType_ID
As an example, a few records might be:
1 | L1 | 12/2010 | 7 | 1 (=Permanent)
2 | L1 | 12/2010 | 2 | 2 (=Temp)
3 | L1 | 12/2010 | 1 | 3 (=Support)
4 | L1 | 01/2011 | 4 | 1
5 | L1 | 01/2011 | 2 | 2
6 | L1 | 01/2011 | 1 | 3
7 | L2 | 12/2010 | 6 | 1
8 | L2 | 01/2011 | 6 | 1
9 | L2 | 12/2010 | 3 | 2
What I need to do is sum the various types of people by date, such that the results look something like this:
Date | Total Perm | Total Temp | Total Supp
12/2010 | 13 | 5 | 1
01/2011 | 10 | 2 | 1
Currently, I've created a separate query for each deployment type that looks like this:
SELECT Date, SUM(NumEmployees) AS "Total Permanent"
FROM tblDeployment
WHERE DeploymentType_ID=1
GROUP BY Date;
We'll call that query qSumPermDeployments. Then, I'm using a couple of joins to combine the queries:
SELECT qSumPermDeployments.Date, qSumPermDeployments.["Total Permanent"] AS "Permanent"
qSumTempDeployments.["Total Temp"] AS "Temp"
qSumSupportDeployments.["Total Support"] AS Support
FROM (qSumPermDeployments LEFT JOIN qSumTempDeployments
ON qSumPermDeployments.Date = qSumTempDeployments.Date)
LEFT JOIN qSumSupportDeployments
ON qSumPermDeployments.Date = qSumSupportDeployments.Date;
Note that I'm currently constructing that final query under the assumption that a location will only have temp or support employees if they also have permanent employees. Thus, I can create the joins using the permanent employee results as the base table. Given all of the data I currently have, that assumption holds up, but ideally I'd like to move away from that assumption.
So finally, my question. Is there a way to simplify this down to a single query or is it best to separate it out into multiple queries - if for no other reason that readability.

SELECT Date,
SUM(case when DeploymentType_ID = 1 then NumEmployees else null end) AS "Total Permanent",
SUM(case when DeploymentType_ID = 2 then NumEmployees else null end) AS "Total Temp",
SUM(case when DeploymentType_ID = 3 then NumEmployees else null end) AS "Total Supp"
FROM tblDeployment
GROUP BY Date

Try this:
SELECT
Date,
SUM(CASE WHEN DeploymentType_ID=1 THEN NumEmployees ELSE 0 END) AS "Total Permanent",
SUM(CASE WHEN DeploymentType_ID=2 THEN NumEmployees ELSE 0 END) AS "Total Temporary",
SUM(CASE WHEN DeploymentType_ID=3 THEN NumEmployees ELSE 0 END) AS "Total Support"
FROM tblDeployment
GROUP BY Date;

Related

Transposing lines containing Text to columns

I have a table just like this one:
+----+---------+-------------+------------+
| ID | Period | Total Units | Source |
+----+---------+-------------+------------+
| 1 | Past | 400 | Competitor |
| 1 | Present | 250 | PAWS |
| 2 | Past | 3 | BP |
| 2 | Present | 15 | BP |
+----+---------+-------------+------------+
And I'm trying to transpose the lines into columns, so that for each ID, I have one unique line that compares past and present numbers and attributes. Like following :
+----+------------------+---------------------+-------------+----------------+
| ID | Total Units Past | Total Units Present | Source Past | Source Present |
+----+------------------+---------------------+-------------+----------------+
| 1 | 400 | 250 | Competitor | PAWS
|
| 2 | 3 | 15 | BP | BP |
+----+------------------+---------------------+-------------+----------------+
Transposing the total units is not a problem, as I use a SUM(CASE WHEN Period = Past THEN Total_Units ELSE 0 END) AS Total_Units.
However I don't know how to proceed with text columns. I've seen some pivot and unpivot clause used but they all use an aggregate function at some point.
You can do conditional aggregation :
select id,
sum(case when period = 'past' then units else 0 end) as unitspast,
sum(case when period = 'present' then units else 0 end) as unitpresent,
max(case when period = 'past' then source end) as sourcepast,
max(case when period = 'present' then source end) as sourcepresent
from table t
group by id;
Assuming you only have two rows per ID, you could also join:
Select a.ID, a.units as UnitsPast, a.source as SourcePast
, b.units as UnitsPresent, b.source as SourcePresent
from MyTable a
left join MyTable b
on a.ID = b.ID
and b.period = 'Present'
where a.period = 'Past'

SQL Group rows for every ID using left outer join

I have a table with almost a million records of claims for 6 different conditions like Diabetes, Hypertension, Heart Failure etc. Every member has a number of claims. He might have claims with the condition as Diabetes or Hypertension or anything else. My goal is to group the conditions they have(number of claims) per every member row.
Existing table
+--------------+---------------+------+------------+
| Conditions | ConditionCode | ID | Member_Key |
+--------------+---------------+------+------------+
| DM | 3001 | 1212 | A1528 |
| HTN | 5001 | 1213 | A1528 |
| COPD | 6001 | 1214 | A1528 |
| DM | 3001 | 1215 | A1528 |
| CAD | 8001 | 1823 | B4354 |
| HTN | 5001 | 3458 | B4354 |
+--------------+---------------+------+------------+
Desired Result
+------------+------+-----+----+----+-----+-----+
| Member_Key | COPD | CAD | DM | HF | CHF | HTN |
+------------+------+-----+----+----+-----+-----+
| A1528 | 1 | | 2 | | | 1 |
| B4354 | | 1 | | | | 1 |
+------------+------+-----+----+----+-----+-----+
Query
select distinct tr.Member_Key,C.COPD,D.CAD,DM.DM,HF.HF,CHF.CHF,HTN.HTN
FROM myTable tr
--COPD
left outer join (select Member_Key,'X' as COPD
FROM myTable
where Condition=6001) C
on C.Member_Key=tr.Member_Key
--CAD
left outer join ( ....
For now I'm just using 'X'. But i'm trying to get the number of claims in place of X based on condition. I don't think using a left outer join is efficient when you are searching 1 million rows and doing a distinct. Do you have any other approach in solving this
You don't want so many sub-queries, this is easy with group by and case statements:
SELECT Member_Key
SUM(CASE WHEN Condition=6001 THEN 1 ELSE 0 END) AS COPD,
SUM(CASE WHEN Condition=3001 THEN 1 ELSE 0 END) AS DM,
SUM(CASE WHEN Condition=5001 THEN 1 ELSE 0 END) AS HTN,
SUM(CASE WHEN Condition=8001 THEN 1 ELSE 0 END) AS CAD
FROM myTable
GROUP BY Member_Key
This is an ideal situation for CASE statments:
SELECT tr.Member_Key,
SUM(CASE WHEN Condition=6001 THEN 1 ELSE 0 END) as COPD,
SUM(CASE WHEN Condition=6002 THEN 1 ELSE 0 END) as OtherIssue,
SUM(CASE etc.)
FROM myTable tr
GROUP BY tr.Member_Key
This should be done with a PIVOT, like:
SELECT *
FROM
(SELECT conditions, member_key
FROM t) src
PIVOT
(COUNT (conditions)
for conditions in ([COPD], [CAD], [DM], [HF], [CHF], [HTN])) pvt

How to count and group by

As you can see in this image below, I need to count how many number '1' is on every column, the number '1' means that the person interviewed feels secure at Home(AP_4_01),Workplace(AP4_4_02) and so on..
Number 2 = Insecure
Number 3 = Doesn't Apply
Number 9 = Didn't Answer
+----------+----------------------+
| Columns | Numbers of persons |
+----------+----------------------+
| AP4_4_01 | 312 |
| AP4_4_02 | 232 |
| AP4_4_03 | 345 |
| AP4_4_0X | XXX |
+----------+----------------------+
You just need to use the SUM function on some case statements
SELECT
SUM(CASE WHEN AP_4_01 = 1 THEN 1 ELSE 0 END)
,SUM(CASE WHEN AP_4_02 = 1 THEN 1 ELSE 0 END)
...etc
FROM Table
To get a result set like the one in your question, you will need to use the UNPIVOT function, or you can transpose it in excel.

SQL Query: Binary Fields on Dates for Time Series

I have a table that looks like the following:
ID | Date
1 | 2010-01-01
2 | 2010-02-01
3 | 2010-02-15
2 | 2010-02-15
4 | 2010-03-01
I am having trouble creating a table with the IDs as rows, and Time periods as columns. For a given time period, I would like to place a 1 if that ID has an associated date in that period, and a 0 if not.
So the output might look like:
ID | Jan-10 | Feb-10 | Mar-10
1 | 1 | 0 | 0
2 | 0 | 1 | 0
3 | 0 | 1 | 0
4 | 0 | 0 | 1
What is the best way to accomplish this?
I have created a new table containing all distinct IDs from the table above in anticipation of a join---but I'm not sure how to handle the 1/0s.
You can do this with a simple group by and conditional aggregation:
select id,
max(case when month(t.date) = 1 and year(t.date) = 2010 then 1 else 0 end) as "Jan-10",
max(case when month(t.date) = 2 and year(t.date) = 2010 then 1 else 0 end) as "Feb-10",
max(case when month(t.date) = 3 and year(t.date) = 2010 then 1 else 0 end) as "Mar-10"
from "table" t
group by id;
Because you have so many time periods, I would generate the code for the column using formulas in Excel (or your favorite spreadsheet).

SQL Query Assistance

I'm having a complete brain fart moment so i figured i'd ask away here.
I have 3 tables that look like this
Equipment Table
EquipmentID | LocationID
-------------------------
1 | 2
2 | 2
3 | 1
4 | 2
5 | 3
6 | 3
Location Table
LocationID | LocationName
--------------------------
1 | Pizza Hut
2 | Giordanos
3 | Lou Malnati's
Service Table
LocationID | EquipmentID | Status
-----------------------------------
2 | 1 | Serviced
2 | 2 | Not Yet Serviced
2 | 4 | Not Yet Serviced
3 | 5 | Serviced
I need a way to list all locations that have had one or more equipment(s) serviced, but not all of the equipments at the location have been serviced yet.
So for the example above it would return the following results
LocationID | ServicedEquipmentID | NotServicedEquipmentIDS | LocationStatus
------------------------------------------------------------------------------
2 | 1 | 2, 4 | Partially Serviced
3 | 5 | 6 | Partially Serviced
Thanks for any help!
This query will give you the location status you desire, although not the individual equipment statuses:
SELECT [LocationId]
,[LocationId]
,CASE ( [IsServiced] + [IsNotServiced] )
WHEN 0 THEN 'Not Serviced'
WHEN 1 THEN 'Partially Serviced'
WHEN 2 THEN 'Serviced'
END [LocationStatus]
FROM ( SELECT [l].[LocationId]
,[e].[LocationId]
,CASE [s].[Status]
WHEN 'Serviced' THEN 1
ELSE 0
END [IsServiced]
,CASE [s].[Status]
WHEN 'Not Yet Serviced' THEN 1
ELSE 0
END [IsNotServiced]
FROM [Location] l
INNER JOIN [Equipment] e ON [l].[LocationId] = [e].[LocationId]
INNER JOIN [Service] s ON [l].[LocationId] = [s].[LocationId]
AND [e].[EquipmentId] = [s].[EquipmentId]
) x
To add a comma-seperated list of equipmentIds that have been/not been serviced to the result set, you will need a CONCAT function of some sort. Either a UDF, CLR, or a recursive CTE (I don't have time to write that right now -- here's a link).