I have found a lot of questions and answers asking how to convert a date range to records per day, but I need the opposite and can't find anything yet.
So let's say I have this dataset:
User | Available
1 | 01-01-2019
1 | 02-01-2019
1 | 03-01-2019
1 | 04-01-2019
2 | 05-01-2019
2 | 06-01-2019
2 | 07-01-2019
2 | 10-01-2019
2 | 11-01-2019
2 | 12-01-2019
So we have user 1 who is available from 01/01/2019 to 04/01/2019. Then we have user 2 who is available from 05/01/2019 to 07/01/2019 and 10/01/2019 to 12/01/2019.
The result I am looking for should look like this:
User | Start | End
1 | 01-01-2019 | 04-01-2019
2 | 05-01-2019 | 07-01-2019
2 | 10-01-2019 | 12-01-2019
User 1 was fairly easy to calculate using min/max dates, but with the gaps of user 2, I am completely lost. Any suggestions?
I had to do this before somewhere too, this is the solution I used. Basically use a row number split by your grouping columns and ordered by date, and additionally calculate the amount of days from a particular date onwards (any hard-coded day will work).
The key here is that while the row number increases 1 by 1, the anchor difference will only increase 1 by 1 if the days are consecutive. Thus, the rest between the anchor diff and the row number will stay the same only if there are consecutive dates, allowing you to group by and calculate min/max.
IF OBJECT_ID('tempdb..#Availabilities') IS NOT NULL
DROP TABLE #Availabilities
CREATE TABLE #Availabilities (
[User] INT,
Available DATE)
INSERT INTO #Availabilities
VALUES
(1, '2019-01-01'),
(1, '2019-01-02'),
(1, '2019-01-03'),
(1, '2019-01-04'),
(2, '2019-01-05'),
(2, '2019-01-06'),
(2, '2019-01-07'),
(2, '2019-01-10'),
(2, '2019-01-11'),
(2, '2019-01-12')
;WITH WindowFunctions AS
(
SELECT
A.[User],
A.Available,
AnchorDayDifference = DATEDIFF(DAY, '2018-01-01', A.Available),
RowNumber = ROW_NUMBER() OVER (PARTITION BY A.[User] ORDER BY A.Available)
FROM
#Availabilities AS A
)
SELECT
T.[User],
Start = MIN(T.Available),
[End] = MAX(T.Available)
FROM
WindowFunctions AS T
GROUP BY
T.[User],
T.AnchorDayDifference - T.RowNumber
Result:
User Start End
1 2019-01-01 2019-01-04
2 2019-01-05 2019-01-07
2 2019-01-10 2019-01-12
The WindowFunctions values are (added the posterior rest result):
User Available AnchorDayDifference RowNumber GroupingRestResult
1 2019-01-01 365 1 364
1 2019-01-02 366 2 364
1 2019-01-03 367 3 364
1 2019-01-04 368 4 364
2 2019-01-05 369 1 368
2 2019-01-06 370 2 368
2 2019-01-07 371 3 368
2 2019-01-10 374 4 370
2 2019-01-11 375 5 370
2 2019-01-12 376 6 370
This is a "common" Groups and Island question. Provided you're on SQL Server 2012+ (and if you're not, it's time to upgrade) this gets you the result you're after:
USE Sandbox;
GO
WITH VTE AS(
SELECT V.[User],
CONVERT(date,Available,105) AS Available
FROM (VALUES(1,'01-01-2019'),
(1,'02-01-2019'),
(1,'03-01-2019'),
(1,'04-01-2019'),
(2,'05-01-2019'),
(2,'06-01-2019'),
(2,'07-01-2019'),
(2,'10-01-2019'),
(2,'11-01-2019'),
(2,'12-01-2019')) V([User],Available)),
Diffs AS(
SELECT V.[User],
V.Available,
DATEDIFF(DAY, LAG(V.Available,1,DATEADD(DAY, -1, V.Available)) OVER (PARTITION BY V.[User] ORDER BY V.Available), V.Available) AS Diff
FROM VTE V),
Groups AS(
SELECT D.[User],
D.Available,
COUNT(CASE WHEN D.Diff > 1 THEN 1 END) OVER (PARTITION BY D.[User] ORDER BY D.Available
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
FROM Diffs D)
SELECT G.[User],
MIN(G.Available) AS [Start],
MAX(G.Available) AS [End]
FROM Groups G
GROUP BY G.[User],
G.Grp
ORDER BY G.[User],
[Start];
The first CTE Diffs, excluding VTE ("Value Table Expression") for the sample data, gets the difference in days between the different rows. The second CTE Groups then puts the dates into groups (surprise that), base on if the difference was more than 1. Then we can use those groups to get a MIN and MAX for that group in the final SELECT.
I'm reading as MONTHS not DAYS
Example
Select [User]
,[Start] = min([Available])
,[End] = max([Available])
From (
Select *
,Grp = DateDiff(MONTH,'1900-01-01',[Available]) - Row_Number() over (Partition By [User] Order by [Available])
From YourTable
) A
Group By [User],[Grp]
Returns
User Start End
1 2019-01-01 2019-04-01
2 2019-05-01 2019-07-01
2 2019-10-01 2019-12-01
I have a table that looks as follows
Amount Factor Month Customer
1 1 2 A
3 1 2 A
4 -1 2 A
2 1 2 B
2 1 2 B
3 -1 2 B
4 1 3 A
5 1 3 A
6 -1 3 A
I want to aggregate (sum) the column Amount per Month and Customer. The Amounts should be multiplied with the value in the column Factor.
Hence, the result should look as follows (could be an UPDATE to the same table or a new table):
Amount Factor Month Customer
0 1 2 A
1 1 2 B
3 1 3 A
Try below
SELECT SUM(Amount * Factor) as Amount,Month,Customer
FROM tableName
GROUP BY Month,Customer
I think this is what you want:
select month, customer, sum(amount * factor) as sum_amount
from t
group by month, customer;
I'm not sure why you would want factor in the result set.
I have a table with data similar to this:
stat_id account_id discount date_from date_to type
1 1 50 2017-10-01 2017-10-31 1
2 2 40 2017-10-01 2017-10-31 1
3 1 0 2017-01-01 2017-11-30 2
I want to get all distinct account_ids, for a given period (date_from <= '2017-10-01' and date_to >= '2017-10-31'), each one with the highest type (type is either 1 or 2)
account_id discount type
1 0 2
2 40 1
I tried various queries, but I couldn't achieve this. What I get is one row with type = 1 and one row with type = 2 for account_id = 1
account_id discount type
1 50 1
1 0 2
2 40 1
I can filter them in my application, but for pure personal entertainment I want to do it in one query. Any help appreciated :)
Try this -
SELECT account_id, discount, type
FROM YOUR_TABLE
WHERE (account_id, type) IN (SELECT account_id, MAX(type)
FROM YOUR_TABLE
GROUP BY account_id)
AND date_from <= '2017-10-01'
AND date_to >= '2017-10-31'
I have table structure as below,
State Application_count Pending_Days
_________________________________________
TN 10 0
TN 20 1
TN 60 2
TN 10 3
MH 40 1
MH 50 3
MH 20 5
MH 30 8
I want to sum Application_count based on State and Pending_Days period.
I have to group Pending_days 0 to 1 Days, 1 to 3 days, morethan 3 days
Expected Output:
State Application_count
_________________________
TN 30
TN 70
MH 40
MH 50
MH 50
Give an additional column, group_num using a CASE expression based on the condition of Pending_Days column.
Then find the sum group by group_num and state columns.
Query
select t.[state], sum(t.[Application_Count]) as [Application_Count] from(
select [group_num] = (
case when [Pending_Days] between 0 and 1 then 1
when [Pending_Days] between 2 and 3 then 2
when [Pending_Days] > 3 then 3 else 4 end
), *
from [your_table_name]
)t
group by t.[group_num], t.[state];
Find demo here
Note:
For Pending_Days condition, 0 to 1 Days I have taken 0 and 1
for 1 to 3 days I have taken 2 and 3 , for morethan 3 days I have taken the value greater than 3.
How can I add another column that would partition them quarterly (Jan-March, April-June, June-Sep) and then add another counter to keep track of the quarterly and define that Q1 2011 is not the same as Q1 2012. Essentially how would I add the Quarters and Tracker Column. I have looked at ROW_NUMBER(), NTILE functions but not sure how to combine them with months.
--- Period --- Quarters---Tracker
2012-05-06 2 1
2012-05-20 2 1
2012-06-03 2 1
2012-07-01 3 2
2012-08-12 3 2
2012-08-26 3 2
2012-09-09 3 2
2012-10-07 4 3
2012-10-21 4 3
2012-11-04 4 3
2012-11-18 4 3
2012-12-02 4 3
2012-12-16 4 3
2012-12-30 4 3
2013-01-13 1 4
2013-01-27 1 4
REALLY STUCK!
I put the quarter CASE logic in the table definition, but you could also put it in the query so you don't have to modify your table.
Create Table Blah
(
SampleDate Date Default(Convert(Date,Getdate())),
Quarters As ( Case
When Month(SampleDate) Between 1 And 3 Then 1
When Month(SampleDate) Between 4 And 6 Then 2
When Month(SampleDate) Between 7 And 9 Then 3
Else 4 End)
)
Insert Blah (SampleDate)
Select '2012-05-06'
Union All
Select '2012-05-20'
Union All
Select '2012-06-03'
Union All
Select '2012-07-01'
Union All
Select '2012-08-12'
Union All
Select '2012-09-09'
Union All
Select '2012-10-07'
Union All
Select '2012-11-04'
Union All
Select '2012-12-16'
Union All
Select '2013-01-13'
Union All
Select '2013-01-27'
Select *,
Dense_Rank() Over (Order By Year(SampleDate),Quarters) As Tracker
From Blah
So you want a simple column to represent your actual quarter?
2012-Q1, 2011-Q1, 2010-Q1 that you would like to use SQL Partitions on? Or you want 2 columns? One to be partitioned on, and another one to actually indicate the year?
Thinking about it, do you need a counter? Couldn't you just set up the other column to be the year?
so you would have 2 columns. One indicating the quarter, and the other the year
quarter year
1 2011
1 2012
1 2010