How to merge rows to one row in SQL Server - sql-server

I have a table like this:
- ID | CurrencyID | LendID | Price
- 3 | 1 | 1 | 1.2
- 3 | 1 | 2 | 1.3
- 3 | 1 | 3 | 1.4
- 3 | 2 | 1 | 1.5
- 3 | 2 | 2 | 1.6
- 3 | 2 | 3 | 1.7
- 4 | 2 | 3 | 2.0
There are totally 4 currencies 1,2,3,4
There are totally 3 lend 1,2,3
I want to get a result like below:
ID | CurrencyIDLendID_11_Price | CIDID_12_Price | CIDLID_13_Price | CIDLID_21_Price | CIDLID_22_Price | CIDLID_23_Price | CIDLID_31_Price | CIDLID_32_Price | CIDLID_33_Price | CIDLID_41_Price | CIDLID_42_Price | CIDLID_43_Price
3 | 1.2 | 1.3 | 1.4 | 1.5 | 1.6 | 1.7 | 0 | 0 | 0 | 0 | 0 | 0
4 | 0 | 0 | 0 | 0 | 0 | 2.0 | 0 | 0 | 0 | 0 | 0 | 0
I know it is now good description, but what I want to do is to merge many records to one record.

This is called pivoting and one of the ways how to do the pivoting is to use grouping with conditional aggregating:
WITH cidlid AS (
SELECT
ID,
CurrencyIDLendID = CurrencyID * 10 + LendID,
Price
FROM atable
)
SELECT
ID,
CurrencyIDLendID_11_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 11 THEN Price END), 0),
CurrencyIDLendID_12_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 12 THEN Price END), 0),
CurrencyIDLendID_13_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 13 THEN Price END), 0),
CurrencyIDLendID_21_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 21 THEN Price END), 0),
CurrencyIDLendID_22_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 22 THEN Price END), 0),
CurrencyIDLendID_23_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 23 THEN Price END), 0),
CurrencyIDLendID_31_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 31 THEN Price END), 0),
CurrencyIDLendID_32_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 32 THEN Price END), 0),
CurrencyIDLendID_33_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 33 THEN Price END), 0),
CurrencyIDLendID_41_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 41 THEN Price END), 0),
CurrencyIDLendID_42_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 42 THEN Price END), 0),
CurrencyIDLendID_43_Price = COALESCE(SUM(CASE CurrencyIDLendID WHEN 43 THEN Price END), 0)
FROM cidlid
GROUP BY ID
If all the (CurrencyID, LendID) combinations are guaranteed to be unique within same ID groups, you can also use MIN or MAX instead of SUM.

You could use the PIVOT Syntax starting with SQL Server 2005
Declare #Data table (ID int, CurrencyID int, LendID int , price decimal (4,2))
INSERT INTO #Data
SELECT 3 as ID, 1 as CurrencyID, 1 as LendID , 1.2 as price
UNION SELECT 3 , 1 , 1 , 1.2
UNION SELECT 3 , 1 , 2 , 1.3
UNION SELECT 3 , 1 , 3 , 1.4
UNION SELECT 3 , 2 , 1 , 1.5
UNION SELECT 3 , 2 , 2 , 1.6
UNION SELECT 3 , 2 , 3 , 1.7
UNION SELECT 4 , 2 , 3 , 2.0
SELECT
ID,
COALESCE([1_1],0) as CurrencyIDLendID_11_Price ,
COALESCE([1_2],0) as CIDID_12_Price ,
COALESCE([1_3],0) as CIDLID_13_Price ,
COALESCE([2_1],0) as CIDLID_21_Price ,
COALESCE([2_2],0) as CIDLID_22_Price ,
COALESCE([2_3],0) as CIDLID_23_Price ,
COALESCE([3_1],0) as CIDLID_31_Price ,
COALESCE([3_2],0) as CIDLID_32_Price ,
COALESCE([3_3],0) as CIDLID_33_Price ,
COALESCE([4_1],0) as CIDLID_41_Price ,
COALESCE([4_2],0) as CIDLID_42_Price ,
COALESCE([4_3],0) as CIDLID_43_Price
FROM (
SELECT
ID,
cast(CurrencyID as varchar) + '_' + CAST(lendID as varchar) ColumnHeader,
price FROM #Data ) src
PIVOT (SUM(price) for ColumnHeader IN
([1_1], [1_2],[1_3],
[2_1], [2_2],[2_3],
[3_1], [3_2],[3_3],
[4_1], [4_2],[4_3])
) as pivottable
which outputs
ID CurrencyIDLendID_11_Price CIDID_12_Price CIDLID_13_Price CIDLID_21_Price CIDLID_22_Price CIDLID_23_Price CIDLID_31_Price CIDLID_32_Price CIDLID_33_Price CIDLID_41_Price CIDLID_42_Price CIDLID_43_Price
----------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- ---------------------------------------
3 1.20 1.30 1.40 1.50 1.60 1.70 0.00 0.00 0.00 0.00 0.00 0.00
4 0.00 0.00 0.00 0.00 0.00 2.00 0.00 0.00 0.00 0.00 0.00 0.00
Note: I kept your column names

Related

Can this pivot be done more efficiently?

I managed to find a solution for formatting the shown driver table. The result is exactly what i need: One row for every QuoteID with the columns Birthdate and DriverType seperated by DriverIndex. My real drivertable has millions of rows.
Yet i am not convinced that this is the way to go. It just seems odd to me. But i am not a SQL expert. My Question: Can this be done in a more efficent way?
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE driver
([QuoteID] int, [DriverIndex] int,[Birthdate] date,[DriverType] int)
;
INSERT INTO driver
([QuoteID], [DriverIndex],[Birthdate], [DriverType])
VALUES
('72', '1','2022/01/01','11'),
('72', '2','2022/02/01','12'),
('73', '1','2022/03/01','13'),
('74', '1','2022/04/01','13'),
('73', '2','2022/05/01','10'),
('73', '3','2022/06/01','11');
Driver Table:
| QuoteID | DriverIndex | Birthdate | DriverType |
|---------|-------------|------------|------------|
| 72 | 1 | 2022-01-01 | 11 |
| 72 | 2 | 2022-02-01 | 12 |
| 73 | 1 | 2022-03-01 | 13 |
| 74 | 1 | 2022-04-01 | 13 |
| 73 | 2 | 2022-05-01 | 10 |
| 73 | 3 | 2022-06-01 | 11 |
Query:
with sq as(select QuoteID AS QuoteID_sq, [1] AS DriverIndex_1_DriverType , [2] AS DriverIndex_2_DriverType , [3] as DriverIndex_3_DriverType
from
( select [QuoteID], [DriverIndex],[Birthdate], [DriverType] from driver) src
pivot
( max([DriverType]) for DriverIndex in ([1], [2], [3]) ) piv),
sq2 as(select QuoteID as QuoteID_sq2, [1] AS DriverIndex_1_Birthdate , [2] AS DriverIndex_2_Birthdate , [3] as DriverIndex_3_Birthdate
from
( select [QuoteID], [DriverIndex],[Birthdate], [DriverType] from driver) src
pivot
( max([Birthdate]) for DriverIndex in ([1], [2], [3]) ) piv),
sq3 as(Select * from sq,sq2 Where sq.QuoteID_sq=sq2.QuoteID_sq2)
Select QuoteID_sq as QuoteID, max([DriverIndex_1_DriverType]) AS DriverIndex_1_DriverType,MAX([DriverIndex_2_DriverType]) AS DriverIndex_2_DriverType,Max([DriverIndex_3_DriverType]) AS DriverIndex_3_DriverType ,
max([DriverIndex_1_Birthdate]) AS DriverIndex_1_Birthdate , max([DriverIndex_2_Birthdate]) AS DriverIndex_2_Birthdate , max([DriverIndex_3_Birthdate]) as DriverIndex_3_Birthdate
from sq3
group by QuoteID_sq
Results:
| QuoteID | DriverIndex_1_DriverType | DriverIndex_2_DriverType | DriverIndex_3_DriverType | DriverIndex_1_Birthdate | DriverIndex_2_Birthdate | DriverIndex_3_Birthdate |
|---------|--------------------------|--------------------------|--------------------------|-------------------------|-------------------------|-------------------------|
| 72 | 11 | 12 | (null) | 2022-01-01 | 2022-02-01 | (null) |
| 73 | 13 | 10 | 11 | 2022-03-01 | 2022-05-01 | 2022-06-01 |
| 74 | 13 | (null) | (null) | 2022-04-01 | (null) | (null) |
You can simplify this by using only conditional aggregation.
SELECT QuoteID
, MAX(CASE WHEN DriverIndex = 1 THEN DriverType END) AS DriverIndex_1_DriverType
, MAX(CASE WHEN DriverIndex = 2 THEN DriverType END) AS DriverIndex_2_DriverType
, MAX(CASE WHEN DriverIndex = 3 THEN DriverType END) AS DriverIndex_3_DriverType
, MAX(CASE WHEN DriverIndex = 1 THEN Birthdate END) AS DriverIndex_1_Birthdate
, MAX(CASE WHEN DriverIndex = 2 THEN Birthdate END) AS DriverIndex_2_Birthdate
, MAX(CASE WHEN DriverIndex = 3 THEN Birthdate END) AS DriverIndex_3_Birthdate
FROM driver
GROUP BY QuoteID
ORDER BY QuoteID;
QuoteID
DriverIndex_1_DriverType
DriverIndex_2_DriverType
DriverIndex_3_DriverType
DriverIndex_1_Birthdate
DriverIndex_2_Birthdate
DriverIndex_3_Birthdate
72
11
12
null
2022-01-01
2022-02-01
null
73
13
10
11
2022-03-01
2022-05-01
2022-06-01
74
13
null
null
2022-04-01
null
null
Demo on db<>fiddle here

SQL Server Fill Month Gaps in Groups

I have a table for example like below:
declare #test table(Aid int, Bid int, CheckMonth date, Avalue decimal(18,2))
insert into #test (Aid, Bid, CheckMonth, Avalue)
values (1, 4, '2014-07-05', 123.00)
,(1, 4, '2014-08-01', 467.00)
,(1, 4, '2014-11-03', 876.00)
,(1, 4, '2014-12-01', 876.00)
,(2, 6, '2016-01-02', 23.00)
,(2, 6, '2016-03-14', 56.00)
,(2, 6, '2016-04-17', 98.00)
,(2, 6, '2016-07-01', 90.00)
I wish to fill the gaps in months (in CheckMonth column above) with 0.00 values (in Avalue column).
Data is grouped by Aid and Bid columns.
The result should look like below:
Aid Bid CheckMonth Avalue
1 4 '2014-07-05' 123.00
1 4 '2014-08-01' 467.00
1 4 '2014-09-01' 0.00 -->inserted
1 4 '2014-10-01' 0.00 -->inserted
1 4 '2014-11-03' 876.00
1 4 '2014-12-01' 876.00
2 6 '2016-01-02' 23.00
2 6 '2016-02-01' 0.00 -->inserted
2 6 '2016-03-14' 56.00
2 6 '2016-04-17' 98.00
2 6 '2016-05-01' 0.00 -->inserted
2 6 '2016-06-01' 0.00 -->inserted
2 6 '2016-07-01' 90.00
Any help is appreciated.
Thanks.
One option uses a recursive query to generate the month starts for each (aid, bid) tuple; you can then left join the generated resultset with the original table:
with cte as (
select
aid,
bid,
datefromparts(year(min(checkMonth)), month(min(checkMonth)), 1) dt,
datefromparts(year(max(checkMonth)), month(max(checkMonth)), 1) maxDt
from #test
group by aid, bid
union all
select
aid,
bid,
dateadd(month, 1, dt),
maxDt
from cte
where dt < maxDt
)
select c.aid, c.bid, coalesce(t.checkMonth, c.dt) checkMonth, coalesce(t.avalue, 0) avalue
from cte c
left join #test t
on t.aid = c.aid
and t.bid = c.bid
and t.checkMonth >= c.dt
and t.checkMonth < dateadd(month, 1, c.dt)
order by c.aid, c.bid, c.dt
Demo on DB Fiddle:
aid | bid | checkMonth | avalue
--: | --: | :--------- | :-----
1 | 4 | 2014-07-05 | 123.00
1 | 4 | 2014-08-01 | 467.00
1 | 4 | 2014-09-01 | 0.00
1 | 4 | 2014-10-01 | 0.00
1 | 4 | 2014-11-03 | 876.00
1 | 4 | 2014-12-01 | 876.00
2 | 6 | 2016-01-02 | 23.00
2 | 6 | 2016-02-01 | 0.00
2 | 6 | 2016-03-14 | 56.00
2 | 6 | 2016-04-17 | 98.00
2 | 6 | 2016-05-01 | 0.00
2 | 6 | 2016-06-01 | 0.00
2 | 6 | 2016-07-01 | 90.00

How to calculate running balance in SQL Server query?

I have data in given format:
Id | Total_Inv_Amount | Total_Rec_Inv_Amount | Invoice_No Invoice_Amont | Payment_Outstanding | Refund_Outstanding
1 | 25000 | 22000 | 5 | 15000 | 0 | 0
1 | 25000 | 22000 | 6 | 10000 | 0 | 0
2 | 45000 | 48000 | 10| 25000 | 0 | 0
2 | 45000 | 48000 | 15| 20000 | 0 | 0
expected result ....
Id | Total_Inv_Amount | Total_Rec_Inv_Amount | Invoice_No Invoice_Amont | Payment_Outstanding | Refund_Outstanding
1 | 25000 | 22000 | 5 | 15000 | 0 | 0
1 | 25000 | 22000 | 6 | 10000 | 3000 | 0
2 | 45000 | 48000 | 10| 25000 | 0 | 0
2 | 45000 | 48000 | 15| 20000 | 0 | 2000
Calculation :--
invoice No 5 & 6 Total Amount is : 15000+10000 = 25000
And total Received Amount is : 22000
now in field Payment_Outstanding for invoice no is 0
because total_invoice_amt > invoice 5 amount
22000 >15000 than payment outstanding is 0
remain amount is 22000-15000 = 7000
now deduct this amount from next invoice which amount is 10000
10000-7000 = 3000 this is payment outstanding
now please help me how to calculate this in query
recursive queries are great at getting running values.
The first cte adds a rownumber which is used by the recursive part. In the recursive part the calculations are done. In het final result, values below 0 won't be displayed.
Declare #myTable table (Id int, Total_Inv_Amount float,
Total_Rec_Inv_Amount float,Invoice_No int,
Invoice_Amount float, Payment_Outstanding float,
Refund_Outstanding float)
insert into #myTable values
(1 , 25000 , 22000 , 5 , 15000 , 0 , 0 ),
(1 , 25000 , 22000 , 6 , 10000 , 0 , 0 ),
(2 , 45000 , 48000 , 10, 25000 , 0 , 0 ),
(2 , 45000 , 48000 , 15, 20000 , 0 , 0 )
;with first_cte as
(
select *, ROW_NUMBER() over (partition by id order by invoice_no) rn
from #myTable
)
, result_rte as
(
select m.Id, m.Total_Inv_Amount,m.Total_Rec_Inv_Amount,
m.Invoice_No, m.Invoice_Amount, m.Payment_Outstanding,
m.Refund_Outstanding, m.rn, invoice_Amount TotalAmount
from first_cte m
where rn = 1
union all
select m.Id, m.Total_Inv_Amount,m.Total_Rec_Inv_Amount,
m.Invoice_No, m.Invoice_Amount,
r.TotalAmount + m.Invoice_Amount - m.Total_Rec_Inv_Amount ,
m.Total_Rec_Inv_Amount - (r.TotalAmount + m.Invoice_Amount),
m.rn, r.TotalAmount + m.Invoice_Amount
from result_rte r
join first_cte m on r.id = m.id and r.rn+1 = m.rn
)
select Id, Total_Inv_Amount,Total_Rec_Inv_Amount,Invoice_No, Invoice_Amount,
case when Payment_Outstanding > 0 then Payment_Outstanding else 0 end Payment_Outstanding ,
case when Refund_Outstanding > 0 then Refund_Outstanding else 0 end Refund_Outstanding
from result_rte order by invoice_no

TSQL, Fill and remove table rows, purpose of calculating a consistent average per day

So I have intra day data that comes in pretty much randomly during the day 9-17.
I want to calculate a consistent average for each day, by basically aggregating the intra day data to only hourly data, and always the same amount per day (9) so when I calculate the average I don't get weird results just because one day we had lots of data etc.
Say that the values are "current price", so if I have 2 data points one day, say that the first is 0.5 at 09:00 and the second is 0.9 at 16:01, then I wouldn't want (0.5+0.9)/2 obviously, but (9*0.5+0.9)/9
Example:
Current table:
DateIntra DateHour Type Value
2015-09-25 09:34 9 A 0.7
2015-09-25 09:35 9 A 0.6
2015-09-25 10:59 10 A 0.5
2015-09-25 16:59 16 A 0.4
2015-09-25 10:17 10 B 1.2
2015-09-25 11:19 11 B 1.7
2015-09-26 15:34 15 A 0.5
2015-09-26 15:35 15 A 0.6
Result:
Notice that the first value is 0.7.
Logic: When hours need to be filled after the current hour, use the first value for the current hour and the last value to fill the hours to come.
Date DateHour Type Value
2015-09-25 9 A 0.7
2015-09-25 10 A 0.5
2015-09-25 11 A 0.5
2015-09-25 12 A 0.5
2015-09-25 13 A 0.5
2015-09-25 14 A 0.5
2015-09-25 15 A 0.5
2015-09-25 16 A 0.4
2015-09-25 17 A 0.4
2015-09-25 9 B 1.2
2015-09-25 10 B 1.2
2015-09-25 11 B 1.7
2015-09-25 12 B 1.7
2015-09-25 13 B 1.7
2015-09-25 14 B 1.7
2015-09-25 15 B 1.7
2015-09-25 16 B 1.7
2015-09-25 17 B 1.7
2015-09-26 9 A 0.5
2015-09-26 10 A 0.5
2015-09-26 11 A 0.5
2015-09-26 12 A 0.5
2015-09-26 13 A 0.5
2015-09-26 14 A 0.5
2015-09-26 15 A 0.6
2015-09-26 16 A 0.6
2015-09-26 17 A 0.6
Any idea how I could do this efficiently?
My first thought, as a Java programmer, was to do a WHILE loop, but I try to avoid that in SQL for performance reasons. But if that's the best way to go, then I'm open for it of course.
This query works has expected. You can use it to compute the average.
It uses the first available value to fill previous gaps and the last available value to fell following gaps.
First available value is used when hours match.
Data
create table yourData(DateIntra datetime2, DateHour int, Type char(1), Value numeric(10,2))
insert into yourData(DateIntra, DateHour, Type, Value) values
('2015-09-25 09:34', 9, 'A', 0.7)
, ('2015-09-25 09:35', 9, 'A', 0.6)
, ('2015-09-25 10:59', 10, 'A', 0.5)
, ('2015-09-25 16:59', 16, 'A', 0.4)
, ('2015-09-25 10:17', 10, 'B', 1.2)
, ('2015-09-25 11:19', 11, 'B', 1.7)
, ('2015-09-26 15:34', 15, 'A', 0.5)
, ('2015-09-26 15:35', 15, 'A', 0.6);
Query
with hours(DateIntra, DateHour, Type) as(
Select distinct DateIntra = cast(DateIntra as date), n, Type From yourData
Cross Join (values(9), (10), (11), (12), (13), (14), (15), (16), (17)) as h(n)
), data as (
Select DateIntra = cast(DateIntra as date), DateHour, Type, Value
, up = ROW_NUMBER() over(partition by Type, cast(DateIntra as date), DateHour Order by DateIntra)
, down = ROW_NUMBER() over(partition by Type, cast(DateIntra as date), DateHour Order by DateIntra desc)
, rn1 = DENSE_RANK() over(partition by Type, cast(DateIntra as date) Order by DateHour)
, rn2 = DENSE_RANK() over(partition by Type, cast(DateIntra as date) Order by DateHour desc)
From yourData d
)
Select h.DateIntra, h.DateHour, h.Type, d1.Value From hours h
Inner Join data d1 On d1.DateIntra = h.DateIntra and d1.DateHour = h.DateHour and d1.Type = h.Type and d1.up = 1
Union All
Select h.DateIntra, h.DateHour, h.Type, d1.Value From data d1
Inner Join data d2 on d1.DateIntra = d2.DateIntra and d2.DateHour > d1.DateHour and d1.Type = d2.Type and d1.rn1 = d2.rn1-1 and d1.down = 1
Inner Join hours h on d1.DateIntra = h.DateIntra and h.DateHour > d1.DateHour and h.DateHour < d2.DateHour and d1.Type = h.Type
Union All
Select h.DateIntra, h.DateHour, h.Type, d1.Value From data d1
Inner Join hours h on d1.DateIntra = h.DateIntra and d1.DateHour > h.DateHour and d1.Type = h.Type
Where d1.rn1 = 1 and d1.up = 1 and d1.DateHour > 9
Union All
Select h.DateIntra, h.DateHour, h.Type, d1.Value From data d1
Inner Join hours h on d1.DateIntra = h.DateIntra and d1.DateHour < h.DateHour and d1.Type = h.Type
Where d1.rn2 = 1 and d1.down = 1 and d1.DateHour < 17
Order By h.DateIntra, h.Type, h.DateHour
Ouput
DateIntra | DateHour | Type | Value
2015-09-25 | 9 | A | 0.70
2015-09-25 | 10 | A | 0.50
2015-09-25 | 11 | A | 0.50
2015-09-25 | 12 | A | 0.50
2015-09-25 | 13 | A | 0.50
2015-09-25 | 14 | A | 0.50
2015-09-25 | 15 | A | 0.50
2015-09-25 | 16 | A | 0.40
2015-09-25 | 17 | A | 0.40
2015-09-25 | 9 | B | 1.20
2015-09-25 | 10 | B | 1.20
2015-09-25 | 11 | B | 1.70
2015-09-25 | 12 | B | 1.70
2015-09-25 | 13 | B | 1.70
2015-09-25 | 14 | B | 1.70
2015-09-25 | 15 | B | 1.70
2015-09-25 | 16 | B | 1.70
2015-09-25 | 17 | B | 1.70
2015-09-26 | 9 | A | 0.50
2015-09-26 | 10 | A | 0.50
2015-09-26 | 11 | A | 0.50
2015-09-26 | 12 | A | 0.50
2015-09-26 | 13 | A | 0.50
2015-09-26 | 14 | A | 0.50
2015-09-26 | 15 | A | 0.50
2015-09-26 | 16 | A | 0.60
2015-09-26 | 17 | A | 0.60
In you output, you put 0.6 but I think it should be 0.5 for 2015-09-26 15. This value come from 2015-09-26 15:35 which is the last value for 15:
2015-09-26 | 15 | A | 0.50
If you wrap the 4 Selects and the Union All in a subquery with Group By DateIntra, Type and AVG, the average is:
DateIntra Type Average
2015-09-25 A 0.500000
2015-09-26 A 0.522222
2015-09-25 B 1.588888
How about using CTE's like this:
Edited To Correctly calculate average
1.) Ensure that I count the hour from 17-18
2.) Divide by the sum of the hours rather than COUNT(*)
3.) Exclude rows that have the same hour as the previous row and the next row is only one hour in advance,
4.) now uses last value in an hour for subsequent values
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE ValueLog
(
DateIntra DATETIME,
[Type] VARCHAR(10),
[Value] float
)
INSERT INTO ValueLog
VALUES
('2015-09-25 09:34', 'A', 0.7),
('2015-09-25 09:35', 'A', 0.6),
('2015-09-25 10:59', 'A', 0.5),
('2015-09-25 16:59', 'A', 0.4),
('2015-09-25 10:17', 'B', 1.2),
('2015-09-25 11:19', 'B', 1.7),
('2015-09-26 15:34', 'A', 0.5),
('2015-09-26 15:35', 'A', 0.6)
Query 1:
;WITH CTE
AS
(
SELECT DateIntra, CAST(DateIntra As Date) AS [Date], DATEPART(hour,DateIntra) As DateHour, [Type], [Value],
ROW_NUMBER() OVER (PARTITION BY [Type], CAST(DateIntra As Date), DATEPART(hour,DateIntra) ORDER BY DateIntra) AS RN1,
ROW_NUMBER() OVER (PARTITION BY [Type], CAST(DateIntra As Date) ORDER BY DateIntra) AS RN2
FROM ValueLog
),
ValueOrder
As
(
SELECT DateIntra, CAST(DateIntra As Date) As [Date], DateHour, [Type], [Value], RN1, RN2, ROW_NUMBER() OVER (PARTITION BY C1.[Date], C1.[Type] ORDER BY C1.DateINtra) RN
FROM CTE C1
WHERE RN1 = 1 OR (RN1 > 1 AND NOT EXISTS (SELECT * FROM CTE C2 WHERE C1.[Date] = C2.[Date] AND C1.[Type] = C2.[Type] AND C2.RN2 > C1.RN2))
),
NumHours
AS
(
SELECT V1.DateIntra, V1.DateHour, V1.[Type],
COALESCE(V2.DateHour, 18) - (CASE WHEN V1.RN = 1 THEN 9 ELSE V1.DateHour END) as numHours, V1.[Value], V1.[Date], V1.RN
FROM ValueOrder V1
LEFT JOIN ValueOrder V2
ON V1.[Type] = V2.[Type] AND V1.[Date] = V2.[Date] AND V2.RN = V1.RN + 1
)
SELECT [Type], [Date], SUM(numHours * Value) / SUM(numHours) As Average
FROM NumHours
GROUP BY [Type], [Date]
ORDER BY [Type], [Date]
Results:
| Type | Date | Average |
|------|------------|--------------------|
| A | 2015-09-25 | 0.5 |
| A | 2015-09-26 | 0.5333333333333333 |
| B | 2015-09-25 | 1.588888888888889 |

Grouping SQL columns from one table

I am currently having difficulty getting the correct values from my table. Here is my table
NOTE: The column Status has 3 possible values (Cleaned, Unclean, Closed)
+-----------+-------------+--------+------------+
|ApplicantID|ApplicantName| Status | HireDate |
+-----------+-------------+--------+------------+
| 1 | John Smith |Cleaned |08/26/2015 |
| 2 | Alex Murphy |Closed |09/12/2015 |
| 3 | Oliver David|Cleaned |01/11/2015 |
| 4 | Max Payne |Unclean |03/18/2015 |
+-----------+-------------+--------+------------+
The output I'm expecting and it should also be sorted by year.
For example I call all these records for the year 2015 which I get using the variable #Year.
NOTE: The column Total is the SUM of Cleaned and Unclean
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January| 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October| 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
I can't seem to get the right code, for the sql this is my current code.
SELECT Month(HireDate) AS Month, COUNT(*)
FROM Hires
GROUP BY Month(HireDate)
I know my coding is wrong, because it is incomplete.
Generate a list of numbers from 1 to 12 first to hold all months. Then do a LEFT JOIN on Hires to make sure all missing months are accounted for. Then use conditional aggregation for the totals:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
)
SELECT
Month = DATENAME(MONTH, DATEADD(MONTH, N-1,0)),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteMonths m
LEFT JOIN Hires h
ON m.N = MONTH(h.HireDate)
--AND YEAR(h.HireDate) = #year --uncomment this line to filter for year.
GROUP BY m.N
ORDER BY m.N
If you want to include the YEAR:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteYears(yr) AS(
SELECT DISTINCT YEAR(HireDate) FROM Hires
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, y.yr - 1900, 0))
FROM CteMonths m
CROSS JOIN CteYears y
)
SELECT
Year = YEAR(d.dt),
Month = DATENAME(MONTH, d.dt),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteAllDates d
LEFT JOIN Hires h
ON MONTH(d.dt) = MONTH(h.HireDate)
AND YEAR(d.dt) = YEAR(h.HireDate)
GROUP BY YEAR(d.dt), MONTH(d.dt), DATENAME(MONTH, d.dt)
ORDER BY YEAR(d.dt), MONTH(d.dt)
If you want to filter for year, say #year = 2015, you can replace the previous ctes with:
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, #year - 1900, 0))
FROM CteMonths m
)...
I suggest to create TEMP table with values from 1 to 12 (numbers of months) and JOIN your table with TEMP table. To achieve values as columns names you can use PIVOT or CASE. You can do It in following:
INSERT INTO #Months VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
SELECT DATENAME(MONTH, DATEADD(MONTH, m.Id-1, 0)) AS [Month]
, SUM(CASE WHEN [Status] = 'Cleaned' THEN 1 ELSE 0 END) AS [Cleaned]
, SUM(CASE WHEN [Status] = 'Closed' THEN 1 ELSE 0 END ) AS [Closed]
, SUM(CASE WHEN [Status] = 'Unclean' THEN 1 ELSE 0 END) AS [Unclean]
, SUM(CASE WHEN [Status] IN ('Unclean', 'Cleaned') THEN 1 ELSE 0 END) AS [Total]
FROM #Test t
RIGHT JOIN #Months m ON m.Id = MONTH(t.HireDate)
GROUP BY m.Id
OUTPUT
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January | 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October | 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
DEMO
You can test It at: SQL FIDDLE

Resources