Prepare data at ID,Month level in SQL - sql

I have a table that is something like this:
ID Date
1 10/04/2015
1 28/04/2015
1 14/07/2015
1 30/07/2015
1 30/08/2015
2 10/04/2016
2 28/04/2016
2 14/05/2016
2 30/05/2016
but i am trying to achieve like:
ID Date
1 28/04/2015
1 30/07/2015
1 30/08/2015
2 28/04/2016
2 30/05/2016
Could you please help me .

Try this:
select * from (
select id,
[Date],
row_number() over (partition by id, datepart(month, [date]) order by [Date] desc) [rn]
from (
select id,
--date convrsion, 103 - British/French - your style
convert(date, [date], 103) [Date]
from #MyTable
) a
) b where rn = 1

I don't really get the logic of expected result but the query below would work.
SELECT *
FROM yourTable
WHERE DAY([Date])>=28;
See How to get Day, Month and Year Part from DateTime in Sql Server

Related

Minimum and maximum dates within continuous date range grouped by name

I have a data ranges with start and end date for a persons, I want to get the continuous date ranges only per persons:
Input:
NAME | STARTDATE | END DATE
--------------------------------------
MIKE | **2019-05-15** | 2019-05-16
MIKE | 2019-05-17 | **2019-05-18**
MIKE | 2020-05-18 | 2020-05-19
Expected output like:
MIKE | **2019-05-15** | **2019-05-18**
MIKE | 2020-05-18 | 2020-05-19
So basically output is MIN and MAX for each continuous period for the person.
Appreciate any help.
I have tried the below query:
With N AS ( SELECT Name, StartDate, EndDate
, LastStop = MAX(EndDate)
OVER (PARTITION BY Name ORDER BY StartDate, EndDate
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) FROM Table ), B AS ( SELECT Name, StartDate, EndDate
, Block = SUM(CASE WHEN LastStop Is Null Then 1
WHEN LastStop < StartDate Then 1
ELSE 0
END)
OVER (PARTITION BY Name ORDER BY StartDate, LastStop) FROM N ) SELECT Name
, MIN(StartDate) DateFrom
, MAX(EndDate) DateTo FROM B GROUP BY Name, Block ORDER BY Name, Block
But its not considering the continuous period. It's showing the same input.
This is a type of gap-and-islands problem. There is no need to expand the data out by day! That seems very inefficient.
Instead, determine the "islands". This is where there is no overlap -- in your case lag() is sufficient. Then a cumulative sum and aggregation:
select name, min(startdate), max(enddate)
from (select t.*,
sum(case when prev_enddate >= dateadd(day, -1, startdate) then 0 else 1 end) over
(partition by name order by startdate) as grp
from (select t.*,
lag(enddate) over (partition by name order by startdate) as prev_enddate
from t
) t
) t
group by name, grp;
Here is a db<>fiddle.
Here is an example using an ad-hoc tally table
Example or dbFiddle
;with cte as (
Select A.[Name]
,B.D
,Grp = datediff(day,'1900-01-01',D) - dense_rank() over (partition by [Name] Order by D)
From YourTable A
Cross Apply (
Select Top (DateDiff(DAY,StartDate,EndDate)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),StartDate)
From master..spt_values n1,master..spt_values n2
) B
)
Select [Name]
,StartDate= min(D)
,EndDate = max(D)
From cte
Group By [Name],Grp
Returns
Name StartDate EndDate
MIKE 2019-05-15 2019-05-18
MIKE 2020-05-18 2020-05-19
Just to help with the Visualization, the CTE generates the following
This will give you the same result
SELECT subquery.name,min(subquery.startdate),max(subquery.enddate1)
FROM (SELECT NAME,startdate,
CASE WHEN EXISTS(SELECT yt1.startdate
FROM t yt1
WHERE yt1.startdate = DATEADD(day, 1, yt2.enddate)
) THEN null else yt2.enddate END as enddate1
FROM t yt2) as subquery
GROUP by NAME, CAST(MONTH(subquery.startdate) AS VARCHAR(2)) + '-' + CAST(YEAR(subquery.startdate) AS VARCHAR(4))
For the CASE WHEN EXISTS I refered to SQL CASE
For the group by month and year you can see this GROUP BY MONTH AND YEAR
DB_FIDDLE

Get latest entry in each week over several years period

I have the following table to store history for entities:
Date Id State
-------------------------------------
2017-10-10 1 0
2017-10-12 1 4
2018-5-30 1 8
2019-4-1 2 0
2018-3-6 2 4
2018-3-7 2 0
I want to get last entry for each Id in one week period e.g.
Date Id State
-------------------------------------
2017-10-12 1 4
2018-5-30 1 8
2019-4-1 2 0
2018-3-7 2 0
I'd try to use Partition by:
select
ID
,Date
,State
,DatePart(week,Date) as weekNumber
from TableA
where Date = (
select max(Date) over (Partition by Id Order by DatePart(week, Date) Desc)
)
order by ID
but it still gives me more than one result per week.
You can use ROW_NUMBER():
SELECT a.*
FROM (SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.id, DATEPART(WK, a.Date) ORDER BY a.Date DESC) AS Seq
FROM tablea a
) a
WHERE seq = 1
ORDER BY id, Date;

To subtract a previous row value in SQL Server 2012

This is SQL Query
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) [Sno],
_Date,
SUM(Payment) Payment
FROM
DailyPaymentSummary
GROUP BY
_Date
ORDER BY
_Date
This returns output like this
Sno _Date Payment
---------------------------
1 2017-02-02 46745.80
2 2017-02-03 100101.03
3 2017-02-06 140436.17
4 2017-02-07 159251.87
5 2017-02-08 258807.51
6 2017-02-09 510986.79
7 2017-02-10 557399.09
8 2017-02-13 751405.89
9 2017-02-14 900914.45
How can I get the additional column like below
Sno _Date Payment Diff
--------------------------------------
1 02/02/2017 46745.80 46745.80
2 02/03/2017 100101.03 53355.23
3 02/06/2017 140436.17 40335.14
4 02/07/2017 159251.87 18815.70
5 02/08/2017 258807.51 99555.64
6 02/09/2017 510986.79 252179.28
7 02/10/2017 557399.09 46412.30
8 02/13/2017 751405.89 194006.80
9 02/14/2017 900914.45 149508.56
I have tried the following query but not able to solve the error
WITH cte AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) [Sno],
_Date,
SUM(Payment) Payment
FROM
DailyPaymentSummary
GROUP BY
_Date
ORDER BY
_Date
)
SELECT
t.Payment,
t.Payment - COALESCE(tprev.col, 0) AS diff
FROM
DailyPaymentSummary t
LEFT OUTER JOIN
t tprev ON t.seqnum = tprev.seqnum + 1;
Can anyone help me?
Use a order by with column(s) to get consistent results.
Use lag function to get data from previous row and do the subtraction like this:
with t
as (
select ROW_NUMBER() over (order by _date) [Sno],
_Date,
sum(Payment) Payment
from DailyPaymentSummary
group by _date
)
select *,
Payment - lag(Payment, 1, 0) over (order by [Sno]) diff
from t;
You can use lag() to get previous row values
coalesce(lag(sum_payment_col) OVER (ORDER BY (SELECT 1)),0)

How can I dynamically create dates between a specific timespan and weeks?

I have the following customer table:
ID | StartDate | WeekCount
1 | 01.12.2015 | 2
2 | 03.12.2015 | 4
3 | 06.06.2014 | 8
The Startdate represents the date the customer gets the first visit, WeekCount is for the next visit (every X Weeks)
I want to query the next visit dates for a timespawn.
Lets say the first visit is 03.12.2015 then I query for March 2016 so the expected date should be 03.03.2015.
So basically StartDate+WeekCount and then the Date between filter.
I think recursive CTE will help you to solve your problem.
DECLARE #to_date DATETIME
SET #to_date = N'2016.03.01'
;WITH test_data AS(
SELECT 1 AS id, CAST(N'2015.12.01' AS DATETIME) AS startDate, 2 AS weekCount
UNION ALL
SELECT 2 AS id, CAST(N'2015.12.03' AS DATETIME) AS startDate, 4 AS weekCount
UNION ALL
SELECT 3 AS id, CAST(N'2014.06.06' AS DATETIME) AS startDate, 8 AS weekCount
),
result_tbl AS(
SELECT id, startDate, weekCount FROM test_data
UNION ALL
SELECT id, DATEADD(ww, R.weekCount, R.startDate), weekCount FROM result_tbl AS R
WHERE R.startDate < #to_date
)
SELECT * FROM result_tbl
ORDER BY id
Provided the datatype is date/datetime
Select columns from your_table
where StartDate>='20160301' and StartDate<'20160401'

TSQL Calendar table, count 10 workings days from date

I have a calendar table which stores rows of dates and an indication of wether that date is a holiday or working day.
How can I select the date that is 5 working days into the future from the 2014-12-22 so the selected date will be 2014-12-31
Date_Id Date_Date Date_JDE Is_WorkingDay
20141222 2014-12-22 114356 1
20141223 2014-12-23 114357 1
20141224 2014-12-24 114358 1
20141225 2014-12-25 114359 0
20141226 2014-12-26 114360 0
20141227 2014-12-27 114361 0
20141228 2014-12-28 114362 0
20141229 2014-12-29 114363 1
20141230 2014-12-30 114364 1
20141231 2014-12-31 114365 1
You can use a CTE like this...
;WITH cteWorkingDays AS
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'rowNum'
FROM TableName
WHERE Is_WorkingDay = 1
and Date_Date > '20141222' -- this will be a param I suppose
)
SELECT Date_Date
FROM cteWorkingDays
WHERE rowNum = 5 -- this can be changed to 10 (title value
This is hand typed, but it will be close enough.
EDIT: Based on comment.
Declare #DateToUse TYPE -- unsure if you're using a string or a date type.
SELECT #DateToUse = Date_Date
FROM cteWorkingDays
WHERE rowNum = 5
...;
WITH DatesCTE AS
(
SELECT Date_Id,
Date_Date,
Date_JDE,
Is_WorkingDay,
ROW_NUMBER() OVER(ORDER BY Date_Date) AS rn
FROM DatesTable
WHERE Is_WorkingDay = 1
AND Date_Date > '2014-12-22'
)
SELECT Date_Date
FROM DatesCTE
WHERE rn = 5
SQL Fiddle Demo
with Derived Tables
select * from
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'RowNum'
FROM Table_calendar
WHERE Is_WorkingDay = 1
and CAST(Date_Date as DATE) > '2014-12-22'
)d
where d.RowNum=5
You can Try Like This:
with calender as
(select top 5 date_id,date_date,date_jde from calender
where date_date>='2014-12-22' and is_workingday='1)calender
select top 1 * from calender order by date_date desc