Find all instances of a date in a date range - SQL Server - date

I need to find the price for an item for each financial year end date in a date range. In this case the financial year is e.g. 31 March
The table I have for example:
ItemID
Value
DateFrom
DateTo
1
10
'2019/01/01'
'2021/02/28'
1
11
'2021/03/01'
'2021/05/01'
SQL Fiddle
The SQL would thus result in the above table to be:
ItemID
Value
DateFrom
DateTo
1
10
'2019/01/01'
'2019/03/30'
1
10
'2020/03/31'
'2021/02/28'
1
11
'2020/03/01'
'2021/03/30'
1
11
'2020/03/31'
'2021/05/01'

You can solve it, but a prerequisite is the creation of a table called financial_years and filling it with data. This would be the structure of the table:
financial_years(id, DateFrom, DateTo)
Now that you have this table, you can do something like this:
select ItemID, Value, financial_years.DateFrom, financial_years.DateTo
from items
join financial_years
on (items.DateFrom between financial_years.DateFrom and financial_years.DateTo) or
(items.DateTo between financial_years.DateFrom and financial_years.DateTo)
order by financial_years.DateFrom;

The accepted answer is not correct, as it does not split out different parts of the year which have different values.
You also do not need a Year table, although it can be beneficial. You can generate it on the fly using a VALUES table.
Note also a better way to check the intervals overlap, using AND not OR
WITH Years AS (
SELECT
YearStart = DATEFROMPARTS(v.yr, 3, 31),
YearEnd = DATEFROMPARTS(v.yr + 1, 3, 31)
FROM (VALUES
(2015),(2016),(2017),(2018),(2019),(2020),(2021),(2022),(2023),(2024),(2025),(2026),(2027),(2028),(2029),(2030),(2031),(2032),(2033),(2034),(2035),(2036),(2037),(2038),(2039)
) v(yr)
)
SELECT
i.ItemID,
i.Value,
DateFrom = CASE WHEN i.DateFrom > y.YearStart THEN i.DateFrom ELSE y.YearStart END,
DateTo = CASE WHEN i.DateTo > y.YearEnd THEN y.YearEnd ELSE i.DateTo END
FROM items i
JOIN Years y ON i.DateFrom <= y.YearEnd
AND i.DateTo >= y.YearStart;

Related

Is there a way to count days INSIDE a range of dates?

I'm quite a beginner on VB/SQL, I just began my learning few months ago, but I can understand the logic of algorithms as I used to do some Excel VBA .
I'm actually designing a database where I can (wish to) follow up every colleague's activity during the year.
The objective is to have a (Monthly) ratio of =>
Billable days / (Billable + Non Billable - Absent)
The context :
A single person can be : Working internally (Non billable), OR Working Externally (Billable) , OR on Holidays (Absent).
I have a [Planning] Table where it stores the following data : [Consultant_ID] (linked to another table [Consultant], [Activity] (A list with the three choices described above), [Beginning_Date], [End_Date].
Example :
Consultant 1 : Working externally from 01/01/2019 to 01/06/2019,
Working internally from 02/06/2019 to 31/12/2019,
Holidays from 02/03/2019 to 15/03/2019
Is there a way to have the Billable ratio of March for example ?
I created 4 queries (Maybe too much ?)
3 queries : [Consultant_ID] [Activity] [Beginning_Date] [End_Date] [Ratio : Datediff("d";[Beginning_Date];[End_Date]).
For each query : The [Activity criteria] : one Working Internally, one Working Externally, one Absent.
And for the [Beginning_Date] and [End_Date] criterias : <=[Enter beginning date], >=[Enter End date]
And the 4th query [Consultant ID] [Billable] [Non billable] [Absent] (and planning to add the [RATIO]).
Problem is : the Datediff counts the dates of the whole activity of what it finds, and not only the dates between 01/03/2019 and 31/03/2019 as I wish to.
I Expect the output of the ratio to be : Billable days / (Billable + Non Billable - Absent) of the desired period.
The actual output shows the billable, non billable, and absent days of the whole period between the dates which are inputted
So instead of 31 Billable, 0 Non billable, 15 Absent
It shows 180 Billable, 0 Non Billable, 32 Absent
Sorry for the long post, it is actually my first, and thank you very much !
I've been struggling with this for a whole week
We first need to figure out the maxBegin and the minEnd dates for each row
SELECT
*,
(IIF (Beginning_Date > #3/1/2019#, Beginning_Date, #3/1/2019#) ) as maxBegin,
(IIF (End_Date < #3/31/2019#, End_Date, #3/31/2019#) ) as minEnd,
Datediff("d", maxBegin, minEnd) + 1 as theDiff
FROM Planning
Where Beginning_Date <= #3/31/2019# AND End_Date >= #3/1/2019#
Then use that to compute the durations. Note: DateDiff does not count both ends, so we need to add +1.
SELECT
Consultant_ID,
SUM(IIF (Activity = "Working Internally", Datediff("d", maxBegin, minEnd) +1, 0) ) as NonBillable,
SUM(IIF (Activity = "Working Externally", Datediff("d", maxBegin, minEnd) +1, 0) ) as Billable,
SUM(IIF (Activity = "Holidays", Datediff("d", maxBegin, minEnd) +1, 0) ) as Absent
FROM
(
SELECT
*,
(IIF (Beginning_Date > #3/1/2019#, Beginning_Date, #3/1/2019#) ) as maxBegin,
(IIF (End_Date < #3/31/2019#, End_Date, #3/31/2019#) ) as minEnd
FROM Planning
Where Beginning_Date <= #3/31/2019# AND End_Date >= #3/1/2019#
) as z
GROUP BY Planning.Consultant_ID;
Finally, you need to substitute the actual Begin/End dates via params into the sql to run your query. Also note that the Holidays are only 14, not 15.
Also, you can add the Ratio calculation right into this sql, and have only one query.

Syntax for column values for same period of last year in SSRS report

I am generating a report where I SUM price and cost values per month. I would like to show the same defined time period - 1 year. I'm not clear how to nest this. Would I use a case when statement?
My terrible example.
select month, cost, price from table1 where (define months here in SSRS parameter filter)
I'd like to see:
select month, cost, price, lastyearcost, lastyearprice FROM table1 where (define months here in SSRS parameter filter)
I know that I should be using some variant of GETDATE() -1, but will this include the data range parameters passed when running the report? How do I select the column of cost apply the date filter and then get the result of cost for that period last year?
Hopefully that makes sense?
One way is to use DATEPART() with the correct nesting and pairing.
select
month,
cost,
price,
lastyearcost,
lastyearprice
FROM table1
where
--this is limiting the data to the year passed in, and the previous year
(datepart(year,DateColumn) = #YearParam or datepart(year,DateColumn) = #YearParam-1)
and
--this is limiting the months to the two parameters you pass in as integers
(datepart(month,DateColumn) >= #minMonthParam and datepart(month,DateColumn) <= #maxMonthParam)
TEST DATA
See the DEMO HERE
declare #table1 table (DateColumn date)
insert into #table1 (DateColumn)
values
('1/1/2016'),
('2/1/2016'),
('3/1/2016'),
('4/1/2016'),
('5/1/2016'),
('6/1/2016'),
('7/1/2016'),
('8/1/2016'),
('9/1/2016'),
('10/1/2016'),
('11/1/2016'),
('12/1/2016'),
('1/1/2017'),
('2/1/2017'),
('3/1/2017'),
('4/1/2017'),
('5/1/2017')
declare #YearParam int = 2017
declare #minMonthParam int = 2
declare #maxMonthParam int = 5
select
DateColumn
FROM #table1
where
(datepart(year,DateColumn) = #YearParam or datepart(year,DateColumn) = #YearParam-1)
and
(datepart(month,DateColumn) >= #minMonthParam and datepart(month,DateColumn) <= #maxMonthParam)

Bulk insert with variable date field for non existing dates?

Attendence
(
Stu_id int
Att_Id int
Att_Date datetime
Att_Num numeric(15,5)
)
This table basically contains attendence records. I am trying to find the logic to enter the rows for missing dates from 1 Jan 2012 till today.
Assume there is a single attendence record for this period row (1,1,'2012-05-06',1.20000). Then I would like to insert rows for each day from 1 Jan 2012 till today except the existing date with the same values for all the fields except the date field which should be the actual date.
I am trying to bulk insert all the rows but don't know how would I adjust the date field
and check for the existing date.
Thanks.
Try a stored procedure which loops through the dates.
Or better yet, make a dates table and select from that. This will be much faster. Something like:
INSERT INTO Attendence (Stu_id, Att_Id, Att_Date, Att_Num)
SELECT a.Stu_id, a.Att_Id, d.TheDate, a.Att_Num
FROM Attendence a
INNER JOIN Dates d ON d.TheDate BETWEEN '2012-01-01' AND GETDATE()
AND d.TheDate <> a.Att_Date
I would use the DateDiff function to figure out the amount of days inbetween the date in the function and the date in question. Make a DateTime variable and give it the value of 1 Jan 2012. Then set up a while loop that loops the amount of days from the DateDiff function.
In psuedo, the while loop would look something like this.
#DaysTill = DateDiff(days, 1/1/2012, #SomeDate)
While #DaysTill>0
##Check = Select Count(Att_Date) From Attendance where AttDate = DateAdd(days, #DaysTill, Jan 1st 2012)
IF ##Check = 0 THEN Insert Into Attendance VALUES #Stu, #Id, DateAdd(days, #DaysTill, Jan 1st 2012), #numeric
#DaysTill = #DaysTill - 1
END
The main things are understanding DateAdd and DateDiff. After that everything becomes relatively simple.

Problem when extracting year and week number from string in PSQL

Let's say that I have a range of SQL tables that are named name_YYYY_WW where YYYY = year and WW = week number. If I call upon a function that guides a user defined date to the right table.
If the date entered is "20110101":
SELECT EXTRACT (WEEK FROM DATE '20110101') returns 52 and
SELECT EXTRACT (YEAR FROM DATE '20110101') returns 2011.
While is nothing wrong with these results I want "20110101" to either point to table name_2010_52 or name_2011_01, not name_2011_52 as it does now when I concanate the results to form the query for the table.
Any elegant solutions to this problem?
The function to_char() will allow you to format a date or timestamp to output correct the iso week and iso year.
SELECT to_char('2011-01-01'::date, 'IYYY_IW') as iso_year_week;
will produce:
iso_year_week
---------------
2010_52
(1 row)
You could use a CASE:
WITH sub(field) AS (
SELECT CAST('20110101' AS date) -- just to test
)
SELECT
CASE
WHEN EXTRACT (WEEK FROM field ) > 1 AND EXTRACT (MONTH FROM field) = 1 AND EXTRACT (DAY FROM field) < 3 THEN 1
ELSE
EXTRACT (WEEK FROM field)
END
FROM
sub;

How can I compare two datetime fields but ignore the year?

I get to dust off my VBScript hat and write some classic ASP to query a SQL Server 2000 database.
Here's the scenario:
I have two datetime fields called fieldA and fieldB.
fieldB will never have a year value that's greater than the year of fieldA
It is possible the that two fields will have the same year.
What I want is all records where fieldA >= fieldB, independent of the year. Just pretend that each field is just a month & day.
How can I get this? My knowledge of T-SQL date/time functions is spotty at best.
You may want to use the built in time functions such as DAY and MONTH. e.g.
SELECT * from table where
MONTH(fieldA) > MONTH(fieldB) OR(
MONTH(fieldA) = MONTH(fieldB) AND DAY(fieldA) >= DAY(fieldB))
Selecting all rows where either the fieldA's month is greater or the months are the same and fieldA's day is greater.
select *
from t
where datepart(month,t.fieldA) >= datepart(month,t.fieldB)
or (datepart(month,t.fieldA) = datepart(month,t.fieldB)
and datepart(day,t.fieldA) >= datepart(day,t.fieldB))
If you care about hours, minutes, seconds, you'll need to extend this to cover the cases, although it may be faster to cast to a suitable string, remove the year and compare.
select *
from t
where substring(convert(varchar,t.fieldA,21),5,20)
>= substring(convert(varchar,t.fieldB,21),5,20)
SELECT *
FROM SOME_TABLE
WHERE MONTH(fieldA) > MONTH(fieldB)
OR ( MONTH(fieldA) = MONTH(fieldB) AND DAY(fieldA) >= DAY(fieldB) )
I would approach this from a Julian date perspective, convert each field into the Julian date (number of days after the first of year), then compare those values.
This may or may not produce desired results with respect to leap years.
If you were worried about hours, minutes, seconds, etc., you could adjust the DateDiff functions to calculate the number of hours (or minutes or seconds) since the beginning of the year.
SELECT *
FROM SOME_Table
WHERE DateDiff(d, '1/01/' + Cast(DatePart(yy, fieldA) AS VarChar(5)), fieldA) >=
DateDiff(d, '1/01/' + Cast(DatePart(yy, fieldB) AS VarChar(5)), fieldB)
Temp table for testing
Create table #t (calDate date)
Declare #curDate date = '2010-01-01'
while #curDate < '2021-01-01'
begin
insert into #t values (#curDate)
Set #curDate = dateadd(dd,1,#curDate)
end
Example of any date greater than or equal to today
Declare #testDate date = getdate()
SELECT *
FROM #t
WHERE datediff(dd,dateadd(yy,1900 - year(#testDate),#testDate),dateadd(yy,1900 - year(calDate),calDate)) >= 0
One more example with any day less than today
Declare #testDate date = getdate()
SELECT *
FROM #t
WHERE datediff(dd,dateadd(yy,1900 - year(#testDate),#testDate),dateadd(yy,1900 - year(calDate),calDate)) < 0