SQL Statement To Group Different Date Ranges as New Columns - sql

SQL beginner here. Looking at a table of items in an Oracle DB and wanted to export items by year (each in a separate column), group them by a userid, and then sum a total field.
I can export them individually with date ranges like
WHERE DATE > '01-JAN-13'
AND DATE < '31-DEC-13'
My table 'CUSTOMER_ORDERS' looks like this Here is how my table looks
Customer Name | Customer ID | Date | Sale
_________________________________________
Customer 1 | CUS01 | 05-JAN-13 | 110.00
Customer 2 | CUS02 | 06-JAN-11 | 110.00
Customer 3 | CUS03 | 07-JAN-12 | 70.00
Customer 1 | CUS01 | 05-JAN-12 | 10.00
Customer 2 | CUS02 | 05-JAN-11 | 210.00
Ideally I want to export something like this
Customer Name | Customer ID | 2011 Total | 2012 Total | 2013 Total
_________________________________________
Customer 1 | CUS01 | 0 | 10 | 110
Customer 2 | CUS02 | 320 | 0 | 0
Customer 3 | CUS03 | 0 | 70 | 0
I'm sure this is super simple, I just can't figure out the right way to do it.

You can use an aggregate function with a CASE expression to PIVOT the data from rows into columns:
select
CustomerName,
CustomerID,
sum(case when to_char(dt, 'YYYY') = 2011 then Sale else 0 end) Total_2011,
sum(case when to_char(dt, 'YYYY') = 2012 then Sale else 0 end) Total_2012,
sum(case when to_char(dt, 'YYYY') = 2013 then Sale else 0 end) Total_2013
from CUSTOMER_ORDERS
group by CustomerName, CustomerID;
See SQL Fiddle with Demo.
Depending on your version of Oracle, you might be able to use the PIVOT function if you are using Oracle 11g+:
select *
from
(
select CustomerName, CustomerId,
'Total_'||to_char(dt, 'YYYY') year, sale
from CUSTOMER_ORDERS
)
pivot
(
sum(sale)
for year in ('Total_2011', 'Total_2012', 'Total_2013')
);
See SQL Fiddle with Demo

Use the power of self-joins to subset the data in the way you need. Try something like
select c.ID , c.Name , sum(c2011.Sale) , sum(c2012.Sale) , sum( c2013.Sale )
from ( select distinct c.ID , c.Name from customer_order ) c
left join customer_order c2011 on c2011.id = c.id and year(c.Date) = 2011
left join customer_order c2012 on c2012.id = c.id and year(c.Date) = 2012
left join customer_order c2013 on c2013.id = c.id and year(c.Date) = 2013
group by c.ID , c.Name
order by c.ID , c.Name
To get the desired result. Alternatively...
select c.ID , c.Name ,
sum(case when year(c.Date) = 2011 then c.Sale else 0 end) ,
sum(case when year(c.Date) = 2012 then c.Sale else 0 end) ,
sum(case when year(c.Date) = 2013 then c.Sale else 0 end)
from customer_order c
group by c.ID , c.Name
order by c.ID , c.Name

Related

Count Visits by Source for 30 Day Prior Period for each Purchase

I have a table that logs website activity with the following Columns and Data
ID Date Source Revenue
1 2013-10-01 A 0
2 2013-10-01 A 0
3 2013-10-01 B 10
1 2013-10-02 A 40
4 2013-10-03 B 0
3 2013-10-03 B 0
4 2013-10-04 A 10
I am trying to create a table that takes each transaction (Revenue > 0) and counts all of the visits by source in individual columns for the last 30 days. It should look something like this.
ID Date Source Revenue Count_A Count_B
3 2013-10-01 B 10 0 1
1 2013-10-02 A 40 2 0
4 2013-10-04 A 10 1 1
I have tried using a subquery for each of these columns, but the Counts are way off and I am not sure why.
Select ID,
Date,
Source,
Revenue,
(SELECT Count(*)
FROM table t2
WHERE t2.Date between t.Date-30 and t.Date and Source = 'A') AS Count_A
(SELECT Count(*)
FROM table t3
WHERE t3.Date between t.Date-30 and t.Date and Source = 'B') AS Count_B
FROM table t
Where Revenue > 0
Order By WMEID
I am using Microsoft SQL Server.
Use a lateral join:
Select l.*, l2.*
from logs l outer apply
(select sum(case when l2.source = 'A' then 1 else 0 end) as count_a,
sum(case when l2.source = 'B' then 1 else 0 end) as count_b
from logs l2
where l2.id = l.id and
l2.date >= dateadd(day, -30, l.date) and
l2.date <= l.date
) l2
where l.Revenue > 0
order By l.WMEID;
I think the issue with your approach is that you are not matching the ids.
Your counts are off because your sub-selects aren't correlated to the outer query, so the totals are coming up independent of the other data in the row. Also, there's no GROUP BY in the sub-selects, so you're getting a total table count. And I'm not so sure about that date logic.
You could correct all this by adding the correlation to each sub-select (WHERE...t2.ID = t.ID AND t2.Date = t.Date, etc) and including an appropriate GROUP BY clause for each of those queries. But that's rather a lot of typing, hard to maintain, and hard to read. It will also probably generate multiple table scans, so it could become a performance issue.
Instead, I'd opt for conditional aggregation:
Select t.ID,
t.Date,
t.Source,
SUM(t.Revenue) AS Revenue,
SUM(CASE WHEN t.Source = 'A' THEN 1 ELSE 0 END) AS Count_A,
SUM(CASE WHEN t.Source = 'B' THEN 1 ELSE 0 END) AS Count_B
FROM mytable t
Where Revenue > 0
AND t.Date >= DATEADD(DAY, -30, CAST(GETDATE() AS date))
AND t.Date < CAST(GETDATE() AS date)
GROUP BY
t.ID,
t.Date,
t.Source
Order By t.Date
Results (Based on the structure in the question, not the data):
+----+------------+--------+---------+---------+---------+
| ID | Date | Source | Revenue | Count_A | Count_B |
+----+------------+--------+---------+---------+---------+
| 3 | 2020-05-01 | B | 60 | 0 | 2 |
| 1 | 2020-05-02 | A | 40 | 1 | 0 |
| 4 | 2020-05-04 | A | 10 | 1 | 0 |
+----+------------+--------+---------+---------+---------+
Here's a SQL Fiddle.

Is there a better way to flatten out a table to take up fewer rows by moving fields in rows with duplicate keys into empty (NULL) fields?

I have a table with the recorded date, time and quantity of each item a child was given. My end goal is to pivot on that data, but preserve each individual quantity being given out according to date/time and child.
This is easy to achieve without a pivot, but it still takes up an entire row for each instance. What I want, is to flatten out the results to take up fewer rows. There isn't a huge functional difference, I'm just doing this to take up less real estate in the report that will end up using this data.
Updated to include a query for sample data:
DECLARE #Items TABLE (Name VARCHAR(10), Date DATETIME, ItemID INT, Quantity INT)
INSERT INTO #Items VALUES ('Jimmy', '01/23/2017 10:00:00', 1, 2),
('Jimmy', '01/23/2017 12:00:00', 1, 1),
('Jimmy', '01/23/2017 15:00:00', 2, 2),
('Billy', '01/23/2017 09:00:00', 1, 1),
('Billy', '01/23/2017 10:00:00', 2, 3)
This is what my starting table looks like:
Name Date ItemID Quantity
Jimmy 2017-01-23 10:00:00.000 1 2
Jimmy 2017-01-23 12:00:00.000 1 1
Jimmy 2017-01-23 15:00:00.000 2 2
Billy 2017-01-23 09:00:00.000 1 1
Billy 2017-01-23 10:00:00.000 2 3
I use a join to sum up the quantities for each day, sort the quantities into their own respective columns, and then drop the time:
SELECT d.Name, CAST(d.Date AS DATE) AS Date,
SUM(CASE WHEN s.ItemID = 1 THEN s.Quantity ELSE NULL END) AS SumBooks,
SUM(CASE WHEN s.ItemID = 2 THEN s.Quantity ELSE NULL END) AS SumPencils,
MAX(CASE WHEN d.ItemID = 1 THEN d.Quantity ELSE NULL END) AS Books,
MAX(CASE WHEN d.ItemID = 2 THEN d.Quantity ELSE NULL END) AS Pencils
FROM #Items d
INNER JOIN #Items s ON s.Name = d.Name AND CAST(s.Date AS DATE) = CAST(d.Date AS DATE)
GROUP BY d.Name, d.Date
This is the resulting data:
Name Date SumBooks SumPencils Books Pencils
Billy 2017-01-23 1 3 1 NULL
Billy 2017-01-23 1 3 NULL 3
Jimmy 2017-01-23 3 2 2 NULL
Jimmy 2017-01-23 3 2 1 NULL
Jimmy 2017-01-23 3 2 NULL 2
This is the structure I am trying to achieve:
Name Date SumBooks SumPencils Books Pencils
Billy 2017-01-23 1 3 1 3
Jimmy 2017-01-23 3 2 2 2
Jimmy 2017-01-23 3 2 1 NULL
I was able to do this using a cursor to iterate over each row and check a new table for any matches of Date, Name, and Books = NULL. If a match was found, I update that row with the quantity. Else, I insert a new row with the Books quantity and a NULL value in the Pencils field, later to be updated with a Pencils quantity, and so on.
So, I am able to get the results I need, but this check has to be done for every item column. For just a couple items, it isn't a big deal. When there's a dozen or more items and the result has 30+ columns, it ends up being a lot of declared variables and large, repeating IF/ELSE statements.
I'm not sure if this is commonly done, but if it is, I'm lacking the proper verbiage to find out on my own. Thanks in advance for any Suggestions.
If we trade the inner join for an outer apply() or a left join
and include those values to the group by we can get the results you are looking for based on the test data provided.
;with cte as (
select
i.Name
, [Date] = convert(date,i.[Date])
, SumBooks = sum(case when ItemId = 1 then Quantity else null end)
, SumPencils = sum(case when ItemId = 2 then Quantity else null end)
, Books = b.Books
, Pencils = max(case when ItemId = 2 then Quantity else null end)
, rn = row_number() over (
partition by i.Name, convert(varchar(10),i.[Date],120)
order by b.booksdate
)
from #Items i
outer apply (
select Books = Quantity, BooksDate = b.[Date]
from #Items b
where b.ItemId = 1
and b.Name = i.Name
and convert(date,b.[Date])=convert(date,i.[Date])
) as b
group by
i.Name
, convert(date,i.[Date])
, b.Books
, b.BooksDate
)
select
Name
, Date
, SumBooks
, SumPencils
, Books
, Pencils = Pencils + case when rn > 1 then null else 0 end
from cte
alternate left join for b:
left join (
select Books = Quantity, BooksDate = b.[Date], Name, Date
from Items b
where b.ItemId = 1
) as b on b.Name = i.Name and convert(date,b.[Date])=convert(date,i.[Date])
test setup: http://rextester.com/IXHU81911
create table Items (
Name varchar(64)
, Date datetime
, ItemID int
, Quantity int
);
insert into Items values
('Jimmy','2017-01-23 10:00:00.000',1,2)
, ('Jimmy','2017-01-23 12:00:00.000',1,1)
, ('Jimmy','2017-01-23 13:00:00.000',1,1) /* Another 1 Book */
, ('Jimmy','2017-01-23 15:00:00.000',2,2)
, ('Billy','2017-01-23 09:00:00.000',1,1)
, ('Billy','2017-01-23 10:00:00.000',2,3)
, ('Zim' ,'2017-01-23 10:00:00.000',2,1) /* No books */
query:
;with cte as (
select
i.Name
, [Date] = convert(varchar(10),i.[Date],120)
, SumBooks = sum(case when ItemId = 1 then Quantity else null end)
, SumPencils = sum(case when ItemId = 2 then Quantity else null end)
, Books = b.Books
, Pencils = max(case when ItemId = 2 then Quantity else null end)
, rn = row_number() over (
partition by i.Name, convert(varchar(10),i.[Date],120)
order by b.booksdate
)
from Items i
outer apply (
select Books = Quantity, BooksDate = b.[Date]
from Items b
where b.ItemId = 1
and b.Name = i.Name
and convert(date,b.[Date])=convert(date,i.[Date])
) as b
group by
i.Name
, convert(varchar(10),i.[Date],120)
, b.Books
, b.BooksDate
)
select
Name
, Date
, SumBooks
, SumPencils
, Books
, Pencils = Pencils + case when rn > 1 then null else 0 end
from cte
note: convert(varchar(10),i.[Date],120) is used on rextester to override default formatting of date. Use convert(date,i.[Date]) or cast(i.[Date] as date) outside of rextester.
results:
+-------+------------+----------+------------+-------+---------+
| Name | Date | SumBooks | SumPencils | Books | Pencils |
+-------+------------+----------+------------+-------+---------+
| Billy | 2017-01-23 | 1 | 3 | 1 | 3 |
| Jimmy | 2017-01-23 | 4 | 2 | 1 | 2 |
| Jimmy | 2017-01-23 | 4 | 2 | 1 | NULL |
| Jimmy | 2017-01-23 | 4 | 2 | 2 | NULL |
| Zim | 2017-01-23 | NULL | 1 | NULL | 1 |
+-------+------------+----------+------------+-------+---------+

Which product sales is Increasing year wise?

Table name is SALES
**PROD_ID** **YEAR** **QUANTITY**
P1 2012 50
P1 2013 40
P1 2014 30
P2 2012 20
P2 2013 30
P2 2014 40
Output should be P2 but how..?
How about this?
select prod_id
from sales
group by prod_id
having (sum(case when year = 2014 then quantity else 0 end) >
sum(case when year = 2012 then quantity else 0 end)
);
A slightly complex way to accomplish this with ctes.
Fiddle with sample data
with diff as (
select prod_id ,
case when quantity - nvl(lag(quantity) over(partition by prod_id order by yr),0) > 0
then 1 else 0 end as df
from sales
)
,totdiff as (select prod_id, sum(df) totdf from diff group by prod_id)
, totals as (select prod_id, count(*) cnt from sales group by prod_id)
select d.prod_id
from totdiff d join totals t on t.prod_id = d.prod_id and d.totdf = t.cnt
Edit: as suggested by #shawnt00 in the comments..the query could be simplified to
with diff as (
select prod_id ,
case when quantity - nvl(lag(quantity) over(partition by prod_id order by yr),0) > 0
then 1 else 0 end as df
from sales
)
select prod_id
from diff
group by prod_id
having count(*) = sum(df)
This question can be approached with 2 steps
First, create a column to calculate difference of current year sales from previous year using lag function from windows, and then another column to calculate the distinct number of years for each PROD_ID
Second, Group the data using a group by clause on PROD_ID and filter the correct products only if all the distinct years had a positive sales compared to last year.
Data Table -
+---------+------+-------+
| PROD_ID | Year | Sales |
+---------+------+-------+
| P1 | 2012 | 50 |
| P1 | 2013 | 40 |
| P1 | 2014 | 30 |
| P2 | 2012 | 20 |
| P2 | 2013 | 30 |
| P2 | 2014 | 40 |
+---------+------+-------+
Query -
select PROD_ID
from
(
select
PROD_ID, sales,
sales - LAG(sales,1,0) over (partition by PROD_ID order by year asc) as diff,
count(year) over (partition by PROD_ID) as num_of_years
from sales
) inner_tbl
group by PROD_ID,num_of_years
having SUM(CASE WHEN diff > 0 THEN 1 ELSE 0 END) = num_of_years
Innner query output -
+---------+--------+------+--------------+
| PROD_ID | sales | diff | num_of_years |
+---------+--------+------+--------------+
| P1 | 50 | 50 | 3 |
| P1 | 40 | -10 | 3 |
| P1 | 30 | -10 | 3 |
| P2 | 20 | 20 | 3 |
| P2 | 30 | 10 | 3 |
| P2 | 40 | 10 | 3 |
+---------+--------+------+--------------+
Final output -
+---------+
| PROD_ID |
+---------+
| P2 |
+---------+
I know its a very old question, posting answer since i was able to solve it in a different way.
create table sales (prod_id varchar(10), yr int, quantity int);
insert into sales values ('P1',2012 , 50);
insert into sales values ('P1', 2013, 40);
insert into sales values ('P1', 2014, 30);
insert into sales values ('P2', 2012, 20);
insert into sales values ('P2', 2013, 30);
insert into sales values ('P2', 2014, 40);
with next_year_sales as
(
select s.prod_id, s.yr, nvl(s1.yr,0) as prev_yr, s.quantity, nvl(s1.quantity,0) as prev_qty from sales s
left outer join sales s1 on s.prod_id = s1.prod_id and s.yr = s1.yr+1
),
flag_high_sales as
(
select prod_id, yr, case when prev_yr=0 then 1 when quantity > prev_qty then 1 else 0 end as flag from next_year_sales A
)
select prod_id, min(flag) from flag_high_sales group by prod_id having min(flag)=1;
I can think of 3 ways of doing it :
select a.prod_id from
(
select
prod_id,
CASE WHEN quantity > coalesce(lag(quantity) over(partition by prod_id order by year asc),0) THEN 1 ELSE 0 END as val
FROM
sales
) a
group by a.prod_id
having sum(a.val) = count(prod_id)
;
select a.prod_id from
(
select
prod_id,
quantity - coalesce(lag(quantity) over(partition by prod_id order by year asc),0) as val
FROM
sales
) a
group by a.prod_id
having min(a.val) >=0
;
select a.prod_id from
(
select
prod_id,
year - dense_rank() over(partition by prod_id order by quantity asc) as cal
FROM
sales
) a
group by a.prod_id
having count(distinct cal)=1
;
The following solution uses CTE and OVER CLAUSE -
WITH Sales_CTE
AS (
SELECT n1.Prod_ID AS n1Prod_ID
,COUNT(n1.Year) OVER (PARTITION BY n1.Prod_ID) AS #CountYn1
,COUNT(n2.Year) OVER (PARTITION BY n1.Prod_ID) AS #CountYn2
FROM #Q2 n1
LEFT JOIN #Q2 n2 ON n1.Prod_ID = n2.Prod_ID
AND (n1.Year + 1) = n2.Year
AND n1.Quantity < n2.Quantity
)
SELECT DISTINCT n1Prod_ID AS [Product ID]
FROM Sales_CTE
WHERE #CountYn1 = (#CountYn2 + 1);

SQL condition sum from two joined tables

I have two tables as below:
Invoice
InvId | Amount | Name
-----------------------
1 | 50 | John
2 | 30 | Mike
3 | 20 | John
Detail
MetalType| Weight | InvId
-------------------------
Gold | 2 | 2
Silver | 4 | 3
Silver | 3 | 3
Gold | 5 | 1
I would like to have the following output, but my query will only provide the total for silver and gold for John. How can I build a query that will also include the total invoice amount for John.
Total Invoice Amount For John = 70
Total Silver Weight = 7
total Gold Weith = 5
SELECT
SUM(IFF(D.MetalType=”Gold”, D.Weight, 0)) AS TotGold,
SUM((IFF(D.MetalType=”Silver”, D.Weight, 0)) AS TotSilver
FROM Invoice I INNER JOIN Detail D ON I.InvId = D.InvId WHERE I.Name = “John”
Try this:
For Sql-Server:
SELECT
SUM(TotalAmount) AS TotalAmount,
SUM(TotGold) AS TotGold,
SUM(TotSilver) AS TotSilver
FROM(
SELECT
SUM (I.Amount) OVER (Partition by D.Invid) AS TotalAmount,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I INNER JOIN Detail D ON I.InvId = D.InvId
WHERE I.Name = 'John'
GROUP BY D.InvId, I.Amount) n
Here is an SQL Fiddle - now it kills the duplicate detail and counts it only once.
EDITED for Access:
SELECT
n.Name,
MAX(TotalAmount),
SUM(TotGold) AS TotGold,
SUM(TotSilver) AS TotSilver
FROM(
SELECT
I.Name,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I
INNER JOIN Detail D ON I.InvId = D.InvId
GROUP BY I.Name, D.InvId, I.Amount) n
INNER JOIN (
SELECT
I.Name, SUM (I.Amount) AS TotalAmount
FROM Invoice I
GROUP BY I.Name) m ON m.Name = n.Name
GROUP BY n.Name
Try with this:
With tbl3 (Amt,Gold,Silver)
as
(
SELECT
SUM (I.Amount) OVER (Partition by D.Invid) AS TotalAmount,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I Right JOIN Detail D ON I.InvId = D.InvId
WHERE I.Name = 'John' Group by D.InvId, I.Amount
)
Select SUM(Amt) as Total_Invoice_Amount_For_John,
SUM(Gold) as Total_Silver_Weight,
SUM(Silver) as Total_Gold_Width from tbl3
SQL Fiddle
I havent tried out the other queries already posted and they might already be suitable for what you want but here's my take on it :-
SELECT X.NAME, X.METALTYPE, X.WEIGHT, Y.TOTAL
FROM
(SELECT NAME, METALTYPE, SUM(Weight) AS WEIGHT
FROM INVOICE i
INNER JOIN DETAIL d ON i.InvId = d.InvId
GROUP BY NAME, METALTYPE) X
INNER JOIN
(SELECT SUM(AMOUNT) AS Total, NAME
FROM INVOICE
GROUP BY NAME)Y
ON X.NAME = Y.NAME
ORDER BY NAME, TOTAL, METALTYPE
select name, sum(Amount) as 'total invoice',sum(Gold) as 'Gold',sum(Silver) as Silver from(
select aa.Name,aa.Amount,
sum(case when bb.MetalType='Gold' then bb.Weight else 0 end) as 'Gold',
sum(case when bb.MetalType='Silver' then bb.Weight else 0 end) as 'Silver'
from a aa left outer join b bb on aa.InvID=bb.InvID group by aa.InvID) as c group by c.name

Aggregating Several Columns in SQL

Suppose I have a table that looks like the following
id | location | dateHired | dateRehired | dateTerminated
1 | 1 | 10/1/2011 | NULL | 12/1/2011
2 | 1 | 10/3/2011 | 11/1/2011 | 12/31/2011
3 | 5 | 10/5/2011 | NULL | NULL
4 | 5 | 10/5/2011 | NULL | NULL
5 | 7 | 11/5/2011 | NULL | 12/1/2011
6 | 10 | 11/2/2011 | NULL | NULL
and I wanted to condense that into a summary table such that:
location | date | hires | rehires | terms
1 | 10/1/2011 | 1 | 0 | 0
1 | 10/3/2011 | 1 | 0 | 0
1 | 11/1/2011 | 0 | 1 | 0
1 | 12/1/2011 | 0 | 0 | 1
1 | 12/31/2011 | 1 | 0 | 0
5 | 10/5/2011 | 2 | 0 | 0
etc.
-- what would that SQL look like? I was thinking it would be something to the effect of:
SELECT
e.location
, -- ?
,SUM(CASE WHEN e.dateHired IS NOT NULL THEN 1 ELSE 0 END) AS Hires
,SUM(CASE WHEN e.dateRehired IS NOT NULL THEN 1 ELSE 0 END) As Rehires
,SUM(CASE WHEN e.dateTerminated IS NOT NULL THEN 1 ELSE 0 END) As Terms
FROM
Employment e
GROUP BY
e.Location
,--?
But I'm not real keen if that's entirely correct or not?
EDIT - This is for SQL 2008 R2.
Also,
INNER JOIN on the date columns assumes that there are values for all three categories, which is false; which is the original problem I was trying to solve. I was thinking something like COALESCE, but that doesn't really make sense either.
I am sure there is probably an easier, more elegant way to solve this. However, this is the simplest, quickest that I can think of this late that works.
CREATE TABLE #Temp
(
Location INT,
Date DATETIME,
HireCount INT,
RehireCount INT,
DateTerminatedCount INT
)
--This will keep us from having to do an insert if does not already exist
INSERT INTO #Temp (Location, Date)
SELECT DISTINCT Location, DateHired FROM Employment
UNION
SELECT DISTINCT Location, DateRehired FROM Employment
UNION
SELECT DISTINCT Location, DateTerminated FROM Employment
UPDATE #Temp
SET HireCount = Hired.HireCount
FROM #Temp
JOIN
(
SELECT Location, DateHired AS Date, SUM(*) AS HireCount
FROM Employment
GROUP BY Location, DateHired
) AS Hired
UPDATE #Temp
SET RehireCount= Rehire.RehireCount
FROM #Temp
JOIN
(
SELECT Location, DateRehired AS Date, SUM(*) AS RehireCount
FROM Employment
GROUP BY Location, DateRehired
) AS Rehire
ON Rehire.Location = #Temp.Location AND Rehire.Date = #Temp.Date
UPDATE #Temp
SET DateTerminatedCount = Terminated.DateTerminatedCount
FROM #Temp
JOIN
(
SELECT Location, DateTerminated AS Date, SUM(*) AS DateTerminatedCount
FROM Employment
GROUP BY Location, DateTerminated
) AS Terminated
ON Terminated.Location = #Temp.Location AND Terminated.Date = #Temp.Date
SELECT * FROM #Temp
How about something like:
with dates as (
select distinct location, d from (
select location, dateHired as [d]
from tbl
where dateHired is not null
union all
select location, dateRehired
from tbl
where dateRehired is not null
union all
select location, dateTerminated
from tbl
where dateTerminated is not null
)
)
select location, [d],
(
select count(*)
from tbl
where location = dates.location
and dateHired = dates.[d]
) as hires,
(
select count(*)
from tbl
where location = dates.location
and dateRehired = dates.[d]
) as rehires,
(
select count(*)
from tbl
where location = dates.location
and dateTerminated = dates.[d]
) as terms
from dates
I don't have a SQL server handy, or I'd test it out.
SELECT * FROM
(SELECT location, dateHired as date, COUNT(1) as hires FROM mytable GROUP BY location, date) H
INNER JOIN
(SELECT location, dateReHired as date, COUNT(1) as rehires FROM mytable GROUP BY location, date) R ON H.location = R.location AND H.dateHired = R.dateRehired
INNER JOIN
(SELECT location, dateTerminated as date, COUNT(1) as terminated FROM mytable GROUP BY location, date) T
ON H.location = T.location AND H.dateHired = T.dateTerminated