Generate custom start and end of months in SQL Server - sql

I'm facing an issue while working with custom dates in T-SQL, we have a client that works with a different methodology of start and end of his month, instead of the default day 01 to start the month and ending in 31, 30 or 29, it's month start at day 26 and ends at 25 of the next month.
E.g., how usually is:
select sum(sales) as sales
from sales
where salesDate between '2020-09-01' and '2020-09-30'
-- or left(salesDate,7) = '2020-09'
Client custom month:
select sum(sales) as sales
from sales
where salesDate between '2020-08-26' and '2020-09-25' -- for setember
So, for this need, I have to calculate how many sales this client did from january until now, month per month, with this custom way... how can I do that?
Example of the query result I want to perform with this month determination:

This is a pretty awful situation. One method is to construct the first date of the month based on the rules:
select mon, sum(sales) as sales
from sales s cross apply
(values (case when day(salesdate) >= 26
then dateadd(month, 1, datefromparts(year(salesdate), month(salesdate), 1))
else datefromparts(year(salesdate), month(salesdate), 1)
) v(mon)
where v.mon >= '2020-01-01'
group by v.mon;
I would recommend adding the fiscal month column as a persisted computed column in the salesDate table so you can add an index and not have to worry about the computation.
Or, better yet, add a calendar table where you can look up the fiscal month for any (reasonable) date.

Related

Get the latest full week's data for analysis in SQL

I was given sales data, where I have items and sales on a particular date. Now, the company wants to analyze the latest full week’s data against the total sales of company.
Item date Sales
Apple 08/25/2020 10
Orange 08/24/2020 20
Orange 08/21/2020 30
Now the full week is defined by a complete week from Sunday-Saturday. In the above made up example, it is clear that, these two data Apple 08/25/2020 10 Orange 08/24/2020 20 are from days Friday and Thursday respectively, so it is not a full week, hence we cannot take this week’s data. We need to check the last week’s data which would be for 08/21/2020
I was given 10 minutes to think on this, my immediate solution was, find the weekday number for the maximum data in the table. And subtract it from 7. If that is equal to 0 then we have a full week, and we can take the max date as the end date of our analysis and use a dateadd() to subtract 7 days from the max date to make it a start date. If I have something other than the 0, for example 6, then I use dateadd to go 6 days prior to my max date and use it as end date, again go 7 days behind this and get the start date.
CREATE TABLE SALES(Item nvarchar(10), dates date, Sales Numeric)
INSERT INTO SALES VALUES('Apple',CAST('08/25/2020' AS DATE),10),
('Orange',CAST('08/24/2020' AS DATE),20),
('Orange',CAST('08/21/2020' AS DATE),30)
WITH end_dates AS
(
SELECT CASE WHEN 7-DATEPART(dw, max(dates))=0 THEN max(dates)
ELSE DATEADD(day,- DATEPART(dw, max(dates)),max(dates)) END AS end_date
FROM SALES
),
Full_Week_Date AS
(
SELECT DATEADD(day,-6,end_date) as start_date ,end_date FROM end_dates
)
SELECT (SELECT SUM(SALES.sales)*100 FROM SALES JOIN Full_Week_Date ON(dates BETWEEN start_date AND end_date))/(SELECT SUM(SALES.sales) FROM SALES) AS revenue_per
This is the best I could think of, but the interviewer said, given a large amount of data, this would run like forever. What would be an optimum solution for this problem? I only want to know, how to get start and end date of the week that I want to analyze. Rest the revenue and % revenue will be fairly easy I believe if I have this in place.
In an actual database the query to use will depend on indexes and other things. For a basic answer, there are a few things to consider here.
They state "a complete week from Sunday-Saturday". Unless you are reporting at 11:59PM on Saturday you never will really have that full weeks sales in the same week. Since that is the case there is no reason to do all the checks you mentioned. They will cause unnecessary processing.
One thing you didn't mention is if the total company sales included the week your sales you are checking are for. I am going to assume they want to exclude that weeks sales.
I am not going to claim this is the most efficient way, but I would do it like this.
INSERT INTO #Sales
VALUES
('Apple', '08/25/2020', 10),
('Orange', '08/24/2020', 20),
('Orange', '08/21/2020', 30),
('Apple', '11/14/2020', 25);
-- Get week to check (last week)
DECLARE #curWeek int = DATEPART(WW, DATEADD(wk, -1, GETDATE()));
-- Get Sales
SELECT
SUM(COALESCE(SalesAmt, 0)) AS CompanySales
, (SELECT SUM(COALESCE(SalesAmt, 0)) FROM #Sales WHERE DATEPART(WW, SalesDate) = #curWeek) AS WeekSales
FROM #Sales
WHERE DATEPART(WW, SalesDate) <= #curWeek;

How to filter the data based on particualr column for the current fiscal year in sql

Fiscal Duration is : July to June I have a [Due Date] column in the
table. Now i need to filter the remaining columns data based on [Due
Date] column which is Current Fiscal Year Data. Here Requirement is i
should not do hardcode for getting the date, because if we entered in
to next fiscal year, we have to get next fiscal year data
automatically.
Please can any one can suggest either the DAX measure or Sql Query for
this logic.
Extract the year and month from your table by creating a view (ViewStep1) that uses those attributes:
TO_NUMBER (TO_CHAR ((DUE_DATE), 'YYYY')) AS YEAR
TO_NUMBER (TO_CHAR ((DUE_DATE), 'MM')) AS MONTH
Then you can use those columns to filter for each fisical year. Or even better, create another view that has a column FISICAL_YEAR which gets it´s data from a subselect of your previous view. Add a simple case in that subselect and your done:
select YEAR as YEAR, MONTH as MONTH, DUE_DATE,
(case
when MONTH >= 7 then YEAR
else (YEAR - 1) END) as FISICAL_YEAR
from ViewStep1 order by YEAR desc, MONTH desc, DUE_DATE desc;
If you have issues creating a view then I would advise to look into the documentation of your database system.
To get the fiscal year, just subtract six months. So the current fiscal year would be something like this:
where year(dateadd(month, -6, duedate)) = year(dateadd(month, -6, duedate))
You should be able to adapt this idea to your code.

sql to find the weekdays in the month from the current day

please help me with this. using SQL server 2008
I need to find the number of sales done on the current day.
then find the weekday from current date and based on that find the average of the sales on all those particular weekdays in the last month
ex:
select count(sales) from salestable where orderdate= getdate()
where it gives the count of the sales done on the current date
then I need to find out the average of the sales done on the same weekday for ex if today is Sunday find the average of the sales done in the last month on all Sundays in that month.
I recommend that you borrow the data warehousing technique of creating a Calendar table that you pre-populate with 1 row for every date within the range you might need. You can add to it basically any column that is useful - in this case DayOfWeek and MonthID. Then you can eliminate date math entirely and use joins - sort of like this (not complete but points you in the right direction):
select count(salestable.sales) as salescount, a.salesavg
from salestable
join calendar on salestable.orderdate = calendar.calendardate
join (
select monthid, dayofweek, avg(salestable.sales) as salesavg
from salestable
join calendar on salestable.orderdate = calendar.calendardate
group by monthid, dayofweek) as a
on calendar.monthid = a.monthid and calendar.dayofweek = a.dayofweek
where calendar.calendardate = getdate()
You create and populate the calendar table once and reuse it every time you need to do date operations. Once you get used to this technique, you will NEVER go back to date math.
For this kind of queries are Common Table Expressions very usefull. Then you can use DATEPART function to get day of week.
This solution is also untested and intended to just point you in the right direction.
This solution uses a co-related sub-query to get the average sales.
select
order_date,
count(sales) total_sales,
(select avg(sales)
from sales_table
where order_date between dateadd(day,-30,#your_date) and #your_date
and datepart(WEEKDAY,order_date) = datepart(WEEKDAY,#your_date)
) avg_sales_mth
from sales_table
where order_date = #your_date

Createing a report using financial periods

I have created a report for management that will total everything up by month with in a date range. Management has now decided that rather than by month they would like to go by period. We have 13 periods in a year each is 28 days except the last one is 29 or 30 depending on if its a leap year. The beginning of the first period is always 1-1-YYYY. So now I will need to figure out what the beginning and end of each period is and total up each period. I am not really sure how to do this since every year the dates will change and they may want to look at periods from the previous year through the current period. The code and results I am currently using are enclosed
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, finspecteddate), 0) AS 'Date'
,COUNT(*) AS Lots
,sum(flotSize) as 'Lot Size'
,sum(LReject) 'Lots Rejected'
,sum(fnumreject) as Rejected
,sum(fsampleSize) as 'Sample Size'
,sum(BDueDate) as 'Before Due Date'
FROM
ReportData
WHERE
finspecteddate >= '01-01-2014'
AND finspecteddate <= '10-15-2014'
GROUP BY
DATEADD(MONTH, DATEDIFF(MONTH, 0, finspecteddate), 0)
ORDER BY
date
Modify the following queries to suit your needs:
;WITH Period AS (
SELECT 1 AS ReportingPeriod,
CAST('2013-01-01' AS datetime) AS PeriodStartDate,
CAST('2013-01-28' AS datetime) AS PeriodEndDate
UNION ALL
SELECT CASE
WHEN p.ReportingPeriod = 13 THEN 1
ELSE p.ReportingPeriod + 1
END,
CASE
WHEN p.ReportingPeriod = 13 THEN DATEADD(YEAR,YEAR(p.PeriodStartDate)-1899,'1900-01-01')
ELSE DATEADD(DAY,28,p.PeriodStartDate)
END,
CASE
WHEN p.ReportingPeriod = 12 THEN DATEADD(YEAR,YEAR(p.PeriodStartDate)-1900,'1900-12-31')
ELSE DATEADD(DAY,28,p.PeriodEndDate)
END
FROM Period p
WHERE p.PeriodStartDate < '2017-12-03'
)
SELECT
P.PeriodStartDate
,P.PeriodEndDate
,COUNT(*) AS Lots
,sum(flotSize) as 'Lot Size'
,sum(LReject) 'Lots Rejected'
,sum(fnumreject) as Rejected
,sum(fsampleSize) as 'Sample Size'
,sum(BDueDate) as 'Before Due Date'
FROM
ReportData R
INNER JOIN Period P ON R.finspecteddate >= P.PeriodStartDate AND R.finspecteddate <= P.PeriodEndDate
WHERE
finspecteddate >= '01-01-2014'
AND finspecteddate <= '10-15-2014'
GROUP BY
P.PeriodStartDate
,P.PeriodEndDate
ORDER BY
P.PeriodStartDate
It uses a recursive CTE to build a period table, which is then joined to ReportData to aggregate asccording to your requirements. I don't have SQL Server 2005 to test it on. It works with 2008. Post a SQL Fiddle if you need help in 2005.
If you haven't got one, create a period calendar table with a year, period number, start date and end date columns. Then when you need to refer to periods, you can refer to the table. When they change the definition of what a period is, you can change the table. When they decide that February 29 doesn't count as one of the 28 days, you can change the table. When they decide to use the first Monday instead of the first Thursday as the start of the year, you just change the table. And best of all, changing how next year works won't change how last year works.
Then you just join to the table to determine which period you're in.

Sql daily comparison by month

I am writing a report for work which requires that I compare the amount of students dropped on a daily basis, what I mean is the report needs to show that on today the 5th of august X amounts of students dropped from the 1st to the 5th compared to X amount which dropped within the 1st to the 5th of July and so on for each Month of the year. Can anyone please help me by providing me with a query which I can use to have that info? thanks.
You want to compare the first number of days from a month. The following query gives you an example:
select yr, mon, count(*)
from (select extract(year from date) as yr, extract(month from date) as mon,
extract(day from date) as day
from t
) t
where day <= extract(day from now())
group by yr, mon
However, the exact syntax may depend on your database. For instance, the current date may be now(), getdate(), system_date or something else. Some databases don't support the extract, but most have a way to get the month and day of month.