Creating a new column for -ve values from the existing rows - sql

How to write logic for this in SQL Server:
Voucher # Name Amount
-------------------------
123 ABC 910
123 ABC -910
224 XYZ 600
Expected output
Voucher # Name Amount - (Amount)
-------------------------------------------
123 ABC 910 -910
224 XYZ 600 -

Using conditional aggregation is really simple here. No need to query the same table over and over.
select Voucher
, Name
, Amount = sum(case when Amount > 0 then Amount else 0 end)
, [-Amount] = sum(case when Amount < 0 then Amount else 0 end)
from YourTable
group by Voucher
, Name

Here is the code, try it
Group by negative and positive amount and make a left join
DECLARE #tbl TABLE
(
Voucher varchar(10),
Name varchar(100),
Amount int
)
INSERT INTO #tbl
(
Voucher,
Name,
Amount
)
VALUES
(123,'ABC',910),
(123,'ABC',-910),
(224,'XYZ',600)
SELECT t1.Voucher, t1.Name, t1.Amount , ISNULL(t2.Amount,0) AS [(-Amount)] FROM (
SELECT t.Voucher, Name, Sum(t.Amount) Amount
FROM #tbl t
WHERE t.Amount > 0
GROUP BY t.Voucher, t.Name) t1 Left JOIN
(SELECT t.Voucher, Name, Sum(t.Amount) Amount
FROM #tbl t
WHERE t.Amount < 0
GROUP BY t.Voucher, t.Name) t2 ON t1.Voucher = t2.Voucher

If for each (Voucher, Name) there exists only 1 positive Amount and 0 or 1 negative Amount then with a self join:
select t.*, tt.amount NegativeAmount
from tablename t left join tablename tt
on tt.voucher = t.voucher and tt.name = t.name and tt.amount < 0
where t.amount > 0
See the demo.
Results:
> voucher | name | amount | NegativeAmount
> ------: | :--- | -----: | -------------:
> 123 | ABC | 910 | -910
> 224 | XYZ | 600 |

Related

display 3 or more consecutive rows(Sql)

I have a table with below data
+------+------------+-----------+
| id | date1 | people |
+------+------------+-----------+
| 1 | 2017-01-01 | 10 |
| 2 | 2017-01-02 | 109 |
| 3 | 2017-01-03 | 150 |
| 4 | 2017-01-04 | 99 |
| 5 | 2017-01-05 | 145 |
| 6 | 2017-01-06 | 1455 |
| 7 | 2017-01-07 | 199 |
| 8 | 2017-01-08 | 188 |
+------+------------+-----------+
now what i am trying to do is to display 3 consecutive rows where people were >=100 like this
+------+------------+-----------+
| id | date1 | people |
+------+------------+-----------+
| 5 | 2017-01-05 | 145 |
| 6 | 2017-01-06 | 1455 |
| 7 | 2017-01-07 | 199 |
| 8 | 2017-01-08 | 188 |
+------+------------+-----------+
can anyone help me how to do this query using oracle database. I am able to display rows which are above 100 but not in a consecutive way
Table creation(reducing typing time for people who will be helping)
CREATE TABLE stadium
( id int
, date1 date, people int
);
Insert into stadium values (
1,TO_DATE('2017-01-01','YYYY-MM-DD'),10);
Insert into stadium values
(2,TO_DATE('2017-01-02','YYYY-MM-DD'),109);
Insert into stadium values(
3,TO_DATE('2017-01-03','YYYY-MM-DD'),150);
Insert into stadium values(
4,TO_DATE('2017-01-04','YYYY-MM-DD'),99);
Insert into stadium values(
5,TO_DATE('2017-01-05','YYYY-MM-DD'),145);
Insert into stadium values(
6,TO_DATE('2017-01-06','YYYY-MM-DD'),1455);
Insert into stadium values
(7,TO_DATE('2017-01-07','YYYY-MM-DD'),199);
Insert into stadium values(
8,TO_DATE('2017-01-08','YYYY-MM-DD'),188);
Thanks in advance for the help
Assuming you mean >= 100, there are a couple of ways. One method just uses lead() and lag(). But a simple method defines each group >= 100 by the number of values < 100 before it. Then it uses count(*) to find the size of the consecutive values:
select s.*
from (select s.*, count(*) over (partition by grp) as num100pl
from (select s.*,
sum(case when people < 100 then 1 else 0 end) over (order by date) as grp
from stadium s
) s
) s
where num100pl >= 3;
Here is a SQL Fiddle showing that the syntax works.
You can use the following sql script to get the desired output.
WITH partitioned AS (
SELECT *, id - ROW_NUMBER() OVER (ORDER BY id) AS grp
FROM stadium
WHERE people >= 100
),
counted AS (
SELECT *, COUNT(*) OVER (PARTITION BY grp) AS cnt
FROM partitioned
)
select id , visit_date,people
from counted
where cnt>=3
I'm assuming that both the id and date columns are sequential and correspond to each other (there will need to be additional ROW_NUMBER() if the ids are not sequential with the dates, and more complex logic included if the dates are not necessarily sequential).
SELECT
*
FROM
(
SELECT
*
,COUNT(date) OVER (PARTITION BY sequential_group_num) AS num_days_in_sequence
FROM
(
SELECT
*
,(id - ROW_NUMBER() OVER (ORDER BY date)) AS sequential_group_num
FROM
stadium
WHERE
people >= 100
) AS subquery1
) AS subquery2
WHERE
num_days_in_sequence >= 3
That produces the following output:
id date people sequential_group_num num_days_in_sequence
----------- ---------- ----------- -------------------- --------------------
5 2017-01-05 145 2 4
6 2017-01-06 1455 2 4
7 2017-01-07 199 2 4
8 2017-01-08 188 2 4
By using joins we can display the consecutive rows like this
SELECT id, date1, people FROM stadium a WHERE people >= 100
AND (SELECT people FROM stadium b WHERE b.id = a.id + 1) >= 100
AND (SELECT people FROM stadium c WHERE c.id = a.id + 2) >= 100
OR people >= 100
AND (SELECT people FROM stadium e WHERE e.id = a.id - 1) >= 100
AND (SELECT people FROM stadium f WHERE f.id = a.id + 1) >= 100
OR people >= 100
AND (SELECT people FROM stadium g WHERE g.id = a.id - 1) >= 100
AND (SELECT people FROM stadium h WHERE h.id = a.id - 2) >= 100
order by id;
select distinct
t1.*
from
stadium t1
join
stadium t2
join
stadium t3
where
t1.people >= 100
and t2.people >= 100
and t3.people >= 100
and
(
(t1.id + 1 = t2.id
and t2.id + 1 = t3.id)
or
(
t2.id + 1 = t1.id
and t1.id + 1 = t3.id
)
or
(
t2.id + 1 = t3.id
and t3.id + 1 = t1.id
)
)
order by
id;
SQL script:
SELECT DISTINCT SS.*
FROM STADIUM SS
INNER JOIN
(SELECT S1.ID
FROM STADIUM S1
WHERE 3 = (
SELECT COUNT(1)
FROM STADIUM S2
WHERE (S2.ID=S1.ID OR S2.ID=S1.ID+1 OR S2.ID=S1.ID+2)
AND S2.PEOPLE >= 100
)) AS SS2
ON SS.ID>=SS2.ID AND SS.ID<SS2.ID+3
select *
from(
select * , count(*) over (partition by grp) as total
from
(select * , Sum(case when people < 100 then 1 else 0 end) over (order by date) as grp
from stadium) T -- inner Query 1
where people >=100 )S--inner query 2
where total >=3 --outer query
I wrote the following solution for this similar leetcode problem:
with groupVisitsOver100 as (
select *,
sum(
case
when people < 100 then 1
else 0
end
) over (order by date1) as visitGroups
from stadium
),
filterUnder100 as (
select
*
from groupVisitsOver100
where people >= 100
),
countGroupsSize as (
select
*,
count(*) over (partition by visitGroups) as groupsSize
from filterUnder100
)
select id, date1, people from countGroupsSize where groupsSize >= 3 order by date1

Formatting multiple SELECT statements with PIVOT

Currently have a script that unions around a dozen SELECT statements, and example of two of these along with an example of the results is shown below.
DECLARE #Age TABLE (name VARCHAR(30), total FLOAT, percentage FLOAT)
INSERT INTO #Age
SELECT '0-18', (SELECT COUNT(*) FROM tblPerson p
INNER JOIN tblClient c ON c.intPersonID = p.intPersonID
WHERE ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') >= 0 AND ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') <= 18), ''
UPDATE #Age
SET percentage = ROUND((SELECT total FROM #Age WHERE name = '0-18')/(SELECT SUM(total) FROM #Age) * 100, 2)
FROM #Age
WHERE name = '0-18'
Etc.
SELECT
g.nvhGenderName,
COUNT(*),
ROUND(COUNT(*) * 1.0 / SUM(COUNT(*)) OVER () * 100, 2)
FROM
tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
GROUP BY g.nvhGenderName
UNION ALL
SELECT * FROM #Age
Results example below:
Name | Total | % |
---------------------------------
Male | 6514 | 60.32 |
Female | 4285 | 39.68 |
0-18 | 279 | 1.58 |
19-24 | 1748 | 9.93 |
25-34 | 5423 | 30.80 |
35-64 | 9546 | 54.21 |
65+ | 614 | 3.50 |
I would like to display these results horizontally as opposed to vertically, I think it is possible to do this with PIVOT but have never really used them. An example of how I want the data to be displayed is shown below:
Gender | Total | % | Age | Total | % |
-------------------------------------------------------------
Male | 6514 | 60.32 | 0-18 | 279 | 1.58 |
Female | 4285 | 39.68 | 19-24 | 1748 | 9.93 |
| | | 25-34 | 5423 | 30.80 |
| | | 35-64 | 9546 | 54.21 |
| | | 65+ | 614 | 3.50 |
In particular I am not sure how I would use a pivot to combine the multiple (12) SELECT statements that require it.
Any help on how to format this would be much appreciated.
Whilst I believe the layout really should be achieved elsewhere, the following may work for you. Clearly I cannot test it so, without the benefit of testing here goes:
WITH myCTE AS (
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN p.intPersonID ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN p.intPersonID ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN p.intPersonID ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN p.intPersonID ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN p.intPersonID ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN p.intPersonID ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Female' THEN p.intPersonID ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
OUTER APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
)
SELECT
ca.Gender, ca.Total2, ca.Pct, ca.Age, ca.Total2, ca.Pct2
FROM myCTE
CROSS APPLY (
VALUES
(1, 'Male' , t.cmale , (cmale * 100.0 / ctotal), '0-18', c0019, (c0019 * 100.0 / ctotal))
, (2, 'Female',t.cfemale,(cfemale * 100.0 / ctotal), '19-24', c1925, (c1925 * 100.0 / ctotal))
, (3, NULL,NULL,NULL, '25-34', c2535, (c2535 * 100.0 / ctotal))
, (4, NULL,NULL,NULL, '35-64', c3565, (c3565 * 100.0 / ctotal))
, (5, NULL,NULL,NULL, '65+' , c65on, (c65on * 100.0 / ctotal))
) AS ca (rn, Gender, Total2, Pct, Age, Total2, Pct2)
ORDER BY ca.rn
;
The second (lower) part of the query above uses a technique for unpivoting data that combines cross apply with values and this allows us to "layout" each row of the wanted final result row by row.
Using a CTE (the upper part of the query above) isn't essential, it could be moved into a subquery instead, but the query inside the CTE should be trialed standalone first and it should produce all the numbers you need in one pass of the data (assuming that the gender table doesn't disturb the count results). Note that using count(case expression here) removes the need for multiple separate queries. I suspect you don't need left joins by the way, and if that is true you could also change the outer apply to a cross apply. Note that the apply is used to execute your function, and by doing it ths way we are able to reference the result of that function by an alias in the remainder of the query (I used "age" as that alias).
I am unsure why you involve a client table when counting the persons table. I suspect it isn't needed. If my suspicions are correct the CTE detail could be replaced by this:
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN 1 ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN 1 ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN 1 ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN 1 ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN 1 ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN 1 ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Feale' THEN 1 ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblPerson p
CROSS APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
INNER JOIN tblGender g ON g.intGenderID = p.intGenderID
Create VIEW view_name AS
SELECT * FROM (
select ColumnName1,ColumnName2 from TableName
) as s
PIVOT (
Sum(ColumnName2)
FOR ColumnName3 in ("Row1","Row2","Row3"
)
) As pvt
DROP VIEW view_name;
Select * from view_name

Getting the wrong values after SUM() query

I'm executing the following query:
SELECT
MAX(table1.id) as id,
clients.Username as account,
table1.clientid,
SUM(table1.symbols) as symbols,
SUM(table1.tickets) as tickets,
SUM(table1.cash) as cash,
(SUM(CASE WHEN table2.memo = 'Withdraw' THEN amount ELSE 0 END)) AS withdraw,
(SUM(CASE WHEN table2.memo = 'Depos' THEN amount ELSE 0 END)) AS depos,
FROM
table1
LEFT JOIN
(
clients
LEFT JOIN
table2
ON
clients.Fidx = table2.clientid
AND
table2.date >= '01-09-2016'
AND
table2.date <= '01-09-2017'
)
ON
clients.Fidx = table1.clientid
WHERE
table1.tradedate >= '01-09-2016'
AND
table1.tradedate <= '01-09-2017'
GROUP BY
table1.clientid, clients.Username, table2.clientid
ORDER BY
clients.Username;
And I want to get a simple result table combined of three tables:
+---------+--------+---------+
| account |withdraw| depos |
+---------+--------+---------+
| adaf | 300 | 0 |
| rich | 1000 | 355 |
| call | 0 | 45 |
| alen | 0 | 0 |
| courney| 0 | 106 |
| warren | 0 | 0 |
+---------+--------+---------+
What's the problem? - I'm getting the wrong values in the result table. Exactly in withdraw and depos. They're in 4 times more, than they should be. For example, for some client SUM(depos) should be 500, but in my result table this value gets 2000. I guess, the problem is in a GROUP BY method, cause when I'm executing the following query, the result seems to look OK:
SELECT clientid, SUM(case when memo = 'Withdraw' then amount else 0 end) as withdraw, SUM(case when memo = 'Depos' then amount else 0 end) as depos
from clients
LEFT JOIN
table2
ON
clients.Fidx = table2.clientid
WHERE table2.date >= '01-09-2016' and table2.date<='01-09-2017' GROUP BY clientid ORDER BY clientid;
What can be a reason of such a wrong result? I'm in trouble and need your help, guys.
You pretty much answered this yourself with the bottom query. You need to do the summing before the join, in a derived table or subquery, just like you have in the bottom query. This will ensure you join on a many-to-one relationship, instead of a many-to-many, which must be causing your current duplication (or 'multiplied' sums).
SELECT
MAX(table1.id) as id,
clients.Username as account,
table1.clientid,
SUM(table1.symbols) as symbols,
SUM(table1.tickets) as tickets,
SUM(table1.cash) as cash,
withdraw,
depos,
FROM
table1
LEFT JOIN
(SELECT clientid,
SUM(case when memo = 'Withdraw' then amount else 0 end) as withdraw,
SUM(case when memo = 'Depos' then amount else 0 end) as depos
FROM clients
LEFT JOIN table2 ON clients.Fidx = table2.clientid
AND table2.date >= '01-09-2016'
AND table2.date<='01-09-2017'
GROUP BY clientid) clients
ON
clients.clientID = table1.clientid
WHERE
table1.tradedate >= '01-09-2016'
AND
table1.tradedate <= '01-09-2017'
GROUP BY
table1.clientid, clients.Username, table2.clientid, clients.depos, clients.withdraw
ORDER BY
clients.Username;
Further example:
Table1
id | someInfo
1 | a
1 | b
1 | c
Table2
id | value
1 | 5
1 | 10
This query:
SELECT t1.id, SUM(t2.Value)
FROM table1 t1
JOIN table2 t2 on t1.id = t2.id --This will be many-to-many
GROUP BY t1.id
Will result in this:
Results
id | value
1 | 45 --sum of 45 because the `table2` values are triplicated from the join
Where this query:
SELECT DISTINCT t1.id, Value
FROM table1 t1
JOIN (SELECT id, SUM(Value) value
FROM table2
GROUP BY id) t2 on t1.id = t2.id --This will be many-to-one
Will result in this:
Results
id | value
1 | 15 --sum of 15 because the `table2` values are not triplicated from the join
Aggregate before joining:
select
t1.id,
c.Username as account,
c.clientid,
t1.symbols,
t1.tickets,
t1.cash,
coalesce(t2.withdraw, 0) as withdraw,
coalesce(t2.depos, 0) as depos
from clients c
join
(
select
clientid,
max(id) as id,
sum(symbols) as symbols,
sum(tickets) as tickets,
sum(cash) as cash
from table1
where date >= '20160901' and date <= '20170901'
group by clientid
) t1 on t1.clientid = c.fidx
left join
(
select
clientid,
sum(case when memo = 'Withdraw' then amount end) as withdraw,
sum(case when memo = 'Depos' then amount end) as depos
from table2
where date >= '20160901' and date <= '20170901'
group by clientid
) t2 on t2.clientid = c.fidx;

In T-SQL, What is the best way to find % of male customers by area

Support I have a table with area, customer and customer's sex info and I want to find out % of male customers in each area. Whats the best way to come up with that?
create table temp(area_id varchar(10),customer_id varchar(10),customer_sex varchar(10))
insert into temp select 1,1,'male'
insert into temp select 1,1,'male'
insert into temp select 1,1,'female'
insert into temp select 1,1,'female'
insert into temp select 2,1,'male'
insert into temp select 2,1,'female'
insert into temp select 2,1,'female'
insert into temp select 3,1,'male'
insert into temp select 3,1,'female'
insert into temp select 4,1,'male'
insert into temp select 5,1,'female'
select * from temp
The result should be like below:
; WITH x AS
(
select
area_id
, count(*) AS total_customers
, SUM(CASE WHEN customer_sex = 'male' THEN 1 ELSE 0 END) AS total_male_customers
FROM temp
GROUP BY area_id
)
SELECT
area_id
, total_customers
, total_male_customers
, CASE WHEN total_male_customers > 0 THEN CAST( (total_male_customers * 100.0) / total_customers AS DECIMAL(6,2)) ELSE 0 END AS Male_percentage
From x
Group by and case will provide your results:
SELECT area_id, count(customer_id) as Total_Customers, Total_Male_Customers = sum(case when customer_sex = 'male' then 1 else 0 end),
Format(sum(case when customer_sex = 'male' then 1 else 0 end)/(count(customer_id)*1.0),'P') as MaleCustomers
FROM dbo.temp
GROUP BY area_id
HAVING sum(case when customer_sex = 'male' then 1 else 0 end) > 0
Here if it is smaller dataset format is better else it has performance issues you can go with custom multiplication and concatenating % symbol.
Output as below:
+---------+-----------------+----------------------+---------------+
| area_id | Total_Customers | Total_Male_Customers | MaleCustomers |
+---------+-----------------+----------------------+---------------+
| 1 | 4 | 2 | 50.00 % |
| 2 | 3 | 1 | 33.33 % |
| 3 | 2 | 1 | 50.00 % |
| 4 | 1 | 1 | 100.00 % |
+---------+-----------------+----------------------+---------------+
Use IIF (Sqlserver 2012+) otherwise CASE, group by and sum of males /count all customers * 100
+ 0.0 to treat sum of males and all customer as float or decimal to get the correct result.
select area_id,count(customer_id) [Total Customers],
sum(iif(customer_sex='male',1,0)) [Total Males],
cast(cast(((sum(iif(customer_sex='male',1,0)) + 0.0) / (count(customer_sex) + 0.0)) * 100 as decimal(18,1)) as varchar(10)) + '%' [percentage of males]
from temp
group by area_id
This will do:
select x.area_id, x.total, x.m, cast(CONVERT(DECIMAL(10,2), x.m * 100.0 / x.total) as nvarchar(max)) + '%'
from
(
select t.area_id, count(1) total, sum(iif(t.customer_sex = 'male', 1, 0)) m
from #temp t
group by t.area_id
)x

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