SQL DatePart Week, year to reflect fiscal year - sql

I currently have a report that uses DATEPART to return the week a 'car' was returned. However, the business I work for fiscal year starts first Sunday of the year (this instance commencing 03/01/2016 would be week 1). However, using SQL 'DATEPART wk' would return this date is week 2:
Current outcome using DATEPART (wk, year etc):
CarTitle ReturnDate Year Week
Car 1 30/12/2015 2015 53
Car 2 02/01/2016 2016 1
Car 3 03/01/2016 2016 2
Car 4 05/01/2016 2016 2
Car 5 10/01/2016 2016 3
Car 6 17/01/2016 2016 4
Example of expected outcome:
CarTitle ReturnDate Year Week
Car 1 30/12/2015 2015 53
Car 2 02/01/2016 2015 53
Car 3 03/01/2016 2016 1
Car 4 05/01/2016 2016 1
Car 5 10/01/2016 2016 2
Car 6 17/01/2016 2016 3

I can calculate this with the following SQL:
SELECT
CarTitle,
DATEPART(ISO_WEEK, DATEADD(day, 1, ReturnDate))
FROM
dbo.My_Table
However, this depends on your localization settings (which are probably different from mine given how you express dates) and if you use this code throughout your system and then the business decides to change it's method for calculating weeks then you could end up with a big refactorization headache.
I prefer to use a Calendar table for this sort of thing:
CREATE TABLE dbo.Calendar (
calendar_date DATE NOT NULL,
week_number SMALLINT NOT NULL,
is_holiday BIT NOT NULL,
name VARCHAR(30) NOT NULL, -- i.e. 'January 6th, 2016'
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date)
)
You can expand the table as needed, for example if accounting uses a different definition for week of the year than another department then that's easy to accommodate by adding another column. This then makes your query a simple join to get the week number:
SELECT
MT.CarTitle,
MT.ReturnDate,
CAL.week_number
FROM
My_Table MT
INNER JOIN dbo.Calendar CAL ON CAL.calendar_date = MT.ReturnDate
You'll need to populate the table, but that should be a one time effort (populate it for 50 years in advance and it's still a pretty small table) and you can use code similar to my first statement to generate the values - don't enter each date by hand ;) After that you just need to maintain the table if the business logic changes (which is easier than changing all of your queries that deal with dates) or if you get a new requirement that needs a new column.

Related

How to make a (MDX) calculation in a SSAS cube to get the difference between a value from the current row and a value from the previous row?

I would like to make a calculation to get the difference between the departDate from my current row and the arriveDateNextStop from my previous row. I have a fact table which has multiple columns. The three most important columns are: id, departDate, arriveDateNextStop.
If I have for example these two rows in my fact table:
id departDate arriveDateNextStop
1 01-01-2019 03-01-2019
1 04-01-2019 07-01-2019
Explanation: On 1 January 2019 I depart to the next destination and I arrive there on 3 January 2019. On 4 January 2019 I again depart to the next destination and I arrive there on 7 January 2019.
Now I would like to know how many days the idle time was (the amount of days between the arrival and the next depart). So with this example the idle time would be 1, because between 3 January 2019 and 4 January 2019 is one day.
First, I made this 'calculation' in Management Studio as a SQL query. See query below:
SELECT s.Id, s.departDate as Depart_current_place, s.arriveDateNextStop as Arrival_next_stop, LAG(arriveDateNextStop) OVER (ORDER BY arriveDateNextStop) AS Arrival_current_stop, DATEDIFF(DAY, LAG(arriveDateNextStop) OVER (ORDER BY arriveDateNextStop), departDate) AS Amount_of_days
FROM MyTable s
WHERE Id = 9
GROUP BY s.departDate, s.Id, s.arriveDateNextStop
ORDER BY s.departDate
This query works fine, but how can I do this in my cube as a calculation in MDX?
I don't have the same example, but the similar cube structure with Completed/Received date:
with
member departDate as [Received].[Year Qtr Month].CurrentMember.Member_Key
member arriveDate as [Completed].[Year Qtr Month].CurrentMember.Member_Key
member arriveDateNextStop as [Completed].[Year Qtr Month].CurrentMember.Lead(1).Member_Key
member idleDays as departDate-arriveDateNextStop
SELECT NON EMPTY { departDate,arriveDate,arriveDateNextStop,idleDays } ON 0
, NON EMPTY
{ ([Completed].[Year Qtr Month].[Date].ALLMEMBERS
* [Received].[Year Qtr Month].[Date].ALLMEMBERS ) } ON 1
FROM ( SELECT ( { [Completed].[Year Qtr Month].[Date].&[6213] } ) ON COLUMNS
FROM [MyCube])
I also have integer key for a date dimension (CurrentMember.Member_Key). 1 = 1998-01-01, 2 = 1998-01-02 etc. till today. You need to create a property in a Date dimension if your Date key is classic YYYYMMDD (which you cannot subtract to get days difference, I can do that in my example). And use it like CurrentMember.Properties("property name") instead of Member_Key.
Main formula part: Lag/Lead function to get prev. or next member.
Please update in case of questions.

Forcing rows to appear despite data missing

I have a large data set (called input below) which contains a variety of information such as sales dates, transaction dates, payments, sales.
The user can produce a report by year, quarter or month to show the amount of payment in a particular month/quarter/year of a certain sales year. So you could have a payment 5 years after the initial sale, i.e. a payment in 2016 relating to the sales year of 2011.
The user can decide whether they want to have these payment periods by month/quarter/year at the beginning of the code through the use of macro variables (i.e. %let ReportBasis = Year) and ReportBasis can be called on through the rest of the code without manual adjustments.
The report is produced using:
proc sql;
create table report as
select sales_year, &ReportBasis, Sum(Sales) as Sales
from input
group by sales_year, &ReportBasis;
quit;
Now the issue I am having is that if there is no payment in a particular period for all sales years in question, then there is no row for that period. This produces a layout problem.
I require a solution to this which can be dynamic due to the nature of the macro variable (changing from year to month to quarter).
So I have this (example):
2011 Month 11 100
2011 Month 12 250
2011 Month 13 85
2011 Month 15 90
2011 Month 16 300
But I require this:
2011 Month 11 100
2011 Month 12 250
2011 Month 13 85
2011 Month 14 0
2011 Month 15 90
2011 Month 16 300
where there is no actual payment in month 14 in all of my data (even other years 2012, 2013 etc.), so it doesn't show up in my first table, but the second table still cleverly knows to include it.
Thanks in advance.
Here is one solution that assumes that all years and reports are represented in input, although not all combinations:
create table report as
select sy.sales_year, r.rb, Sum(i.Sales) as Sales
from (select distinct sales_year from input) sy cross join
(select distinct &ReportBasis as rb from input) r left join
input i
on i.sales_year = sy.sales_year and i.&ReportBasis = r.&ReportBasis
group by sy.sales_year, r.rb;

T-SQL: August is the beginning of next years financial year, code to dynamically to change the batch ids

I have a column called batch_id with a list of dates - 2016080184 ie date 2016 08 01 84(84, I believe a time part).
I need to update the batch_id (varchar(25)) to change to 2017010184, based on another column voucher_date (datetime) = 2016-08-01 00:00:00.000
So if the voucher date was 2016-08-02 00:00:00.000, then the batch_id needs to change from 2016080278 to 2017010278 (78 at the end here doesn't matter)
August is the first month for the financial year, so August would effectively become January, September would become February etc.. and the year from Aug needs to indicate the following year ie this year is 2016 therefore batch_id should start with 2017.
Next August batch_id should indicate 2018 etc..
The file I receive is always a day behind to make things more complicated.
I am a little confused about your requirement for the year to change, but this should give you all you need to get started:
declare #BatchID nvarchar(10) = '2016080184'
select convert(nvarchar(8),dateadd(month,-7,cast(left(#BatchID,8) as date)),112) + right(#BatchID,2) as NewBatchID_SameYear
,convert(nvarchar(8),dateadd(month,5,cast(left(#BatchID,8) as date)),112) + right(#BatchID,2) as NewBatchID_NextYear
You can apply the above date changes with a CASE statement on the voucher_date column as required.

MS Access grouping query spanning start and end dates

I would like to get a running tally of how many widgets were/are rented at any one time, by month, by year. Data is held in an MS Access 2003 db;
Table name: rent_table
Fields:
rentid
startdate
enddate
rentfee
rentcost
bookingfee
Something like; Count number of rentid's that fall between month/year, then group them?
e.g. if a widget was rented from 5th Jan 2014 to 8th April 2014 it would appear as a count in Jan, Feb, Mar and April tally's.
Many thanks.
EDIT
More details (sorry);
Access db is fronted by classic ASP.
If possible I don't want to create any new tables.
No input is required in order to run the report.
There are around 350-400 widgets that could be rented at any one time.
Each widget is rented exclusively.
Report output example;
Month | Year | NumRented
Jan 2014 86
Feb 2014 113
...
Can a query pick up dates within dates? So literally do a count of the table where date >Dec 31st 2013 AND <1st Feb 2014 (to grab a count for all of January 2014) and would that include the example of the rent starting on the 5th Jan? So I could just do twelve counts for each year?
create a calendar table, e.g.
table = cal_yyyymm with one column dt_yyyymm as numeric field
populate the table with ... say 5 or 10 years of data
201401 201402 201403 ... 60 or 120 rows, a small table
make a sql
Select
dt_yyyymm,
count(*) as cnt
From cal_yyyymm
Left Join rent_table
On format(startdate,"yyyymm") >= dt_yyyymm
And dt_yyyymm >= format(enddate,"yyyymm")
think about the complications in the data -- --
widget was rented from 5th Jan 2014 to 8th Jan 2014
and again rented from 11th Jan 2014 to 21st Jan 2014
does this count at 1 or 2 in the month?
if it is 1, then the sql gets more complicated because
the rent_table first needs to have its dates converted
to yyyymm format, and second needs to be de-duped on rentid,
and third then joined to cal_ On the dates...

compute total days work using query from vb6 to msaccess

I am making a payroll system using vb6 with ms access as database. I have 3 tables in ms access namely:
tblemployee, tblattendance, tblpayroll
I want to get the payroll of all employees for the month of ex april, year 2013. All these datas will be from tblattendance.
Ex. (Some fields are hidden)
tblattendance:
Empid Day Month Year totalhourworkfor1day
1 1 april 2013 8
2 1 april 2013 5
1 2 april 2013 8
2 2 april 2013 5
tblpayroll:
empid month year Totalhoursworkfor1month
1 april 2013 16
2 april 2013 10
After querying for the month of april year 2013, all the totalhourworkfor1day for 1 employee will be added and the result will save in to Totalhoursworkfor1month.
How to do this? Considering that from my vb6, I only need to select the month and year to generate the payroll and after it will display to the listview
Tried code:
insert into tblpayroll(empid,month,year,hourswork)
select id,month, year, sum(hourswork) from tblattendance where empid=id group by empid;
Does this help?
The dates are just one field. I have used the MinOfWorkDay so as to avoid complications with months ending on 28,29,30,31. But this can be worked around if you want to have "month ending". In any event you could format results to simply show the month/year.
It could further enhanced by the HAVING clause defaulting to the month just ended, or using "from" and "to" dates entered on a form.
INSERT INTO tblPayroll ( Empid, HoursWorked, MonthEnd )
SELECT tblWork.Empid, Sum(tblWork.WorkHours) AS SumOfWorkHours, Min(tblWork.Workday) AS MinOfWorkday
FROM tblWork
GROUP BY tblWork.Empid
HAVING (((Min(tblWork.Workday)) Between #4/1/2013# And #4/30/2013#));
I'd be pleased to give more help if you come back with more questions.
15 Apr: I boobed! I assumed you would be processing your data in MS Access. I don't know vb6 so can't help any more. Could your whole project be done in access?