I have a table which contains info on customer purchases per year and month respectively. Here is a simplified version.
id
year
month
nb_purch
1
2001
1
1
1
2001
2
4
1
2001
3
7
...
...
...
...
1
2001
12
3
1
2003
1
3
1
2003
2
2
1
2003
3
5
1
2003
4
7
...
...
...
...
1
2003
12
3
2
2001
1
3
2
2001
2
2
2
2001
3
5
2
2001
4
7
Basically there are several constraints. The database contains only the years when the client has made a purchase. If the client has made a purchase within the year X then X will be divided into 12 rows according to months. The months with no purchases have the value 0.
What I am trying to do is to retrieve the number of purchases per certain "windows". Currently its value sits at 3 years. For example i want to retrieve the sum of nb_purch within the last 3 years starting from 2003 march. This means i need to add all values from
march 2001 to march 2003.
SELECT SUM(nb_purch) OVER (PARTITION BY id ORDER BY year, month ASC ROWS BETWEEN 36 PRECEDING AND CURRENT ROW) AS LAST_3_YEARS FROM T
The issue i am facing here is that the table does not contain all years and therefore in my example of purchases between (2001 and 2003) if the year 2002 is missing then i am getting false results. I would like to avoid having to add all missing years and fill them with NULL values for each customer.
I am trying to group by dataset in three month groups, or quarters, but as I'm starting from an arbitrary date, I cannot use the quarter function in sas.
Example data below of what I have and quarter is the column I need to create in SAS.
The start date is always the same, so my initial quarter will be 3rd Sep 2018 - 3rd Dec 2018 and any active date falling in that quarter will be 1, then quarter 2 will be 3rd Dec 2018 - 3rd Mar 2019 and so on. This cannot be coded manually as the start date will change depending on the data, and the number of quarters could be up to 20+.
The code I have attempted so far is below
data test_Data_op;
set test_data end=eof;
%let j = 0;
%let start_date = start_Date;
if &start_Date. <= effective_dt < (&start_date. + 90) then quarter = &j.+1;
run;
This works and gives the first quarter correctly, but I can't figure out how to loop this for every following quarter? Any help will be greatly appreciated!
No need for a DO loop if you already have the start_date and actual event dates. Just count the number of months and divide by three. Use the continuous method of the INTCK() function to handle start dates that are not the first day of a month.
month_number=intck('month',&start_date,mydate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
Based on the comment by #Lee. Edited to match the data from the screenshot.
The example shows that May 11 would be in the 3rd quarter since the seed date is September 3.
data have;
input mydate :yymmdd10.;
format mydate yymmddd10.;
datalines;
2018-09-13
2018-12-12
2019-05-11
;
run;
%let start_date='03sep2018'd;
data want;
set have;
quarter=floor(mod((yrdif(&start_date,mydate)*4),4))+1;
run;
If you want the number of quarters to extend beyond 4 (e.g. September 4, 2019 would be in quarter 5 rather than cycle back to 1), then remove the "mod" from the function:
quarter=floor(yrdif(&start_date,mydate)*4)+1;
The traditional use of quarter means a 3 month time period relative to Jan 1. Make sure your audience understands the phrase quarter in your data presentation actually means 3 months relative to some arbitrary starting point.
The funky quarter can be functionally computed from a months apart derived using a mix of INTCK for the baseline months computation and a logical expression for adjusting with relation to the day of the month of the start date. No loops required.
For example:
data have;
do startDate = '11feb2019'd ;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor(
( intck ('month', startDate, effectiveDate)
-
(day(effectiveDate) < day(startDate))
)
/ 3
);
format qtr 4.;
run;
Extra
Comparing my method (qtr) to #Tom (qtr_number) for a range of startDates:
data have;
retain seq 0;
do startDate = '01jan1999'd to '15jan2001'd;
seq + 1;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor( ( intck ('month', startDate, effectiveDate)
- (day(effectiveDate) < day(startDate))
) / 3 );
month_number=intck('month',startDate,effectiveDate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
format qtr: month: 4.;
run;
options nocenter nodate nonumber;title;
ods listing;
proc print data=want;
where qtr ne qtr_number;
run;
dm 'output';
-------- OUTPUT ---------
effective month_ qtr_
Obs seq startDate Date qtr number number
56820 31 1999-01-31 1999-04-30 1 4 2
57186 31 1999-01-31 2000-04-30 5 16 6
57551 31 1999-01-31 2001-04-30 9 28 10
57916 31 1999-01-31 2002-04-30 13 40 14
58281 31 1999-01-31 2003-04-30 17 52 18
168391 90 1999-03-31 1999-06-30 1 4 2
168483 90 1999-03-31 1999-09-30 2 7 3
168757 90 1999-03-31 2000-06-30 5 16 6
168849 90 1999-03-31 2000-09-30 6 19 7
169122 90 1999-03-31 2001-06-30 9 28 10
169214 90 1999-03-31 2001-09-30 10 31 11
169487 90 1999-03-31 2002-06-30 13 40 14
169579 90 1999-03-31 2002-09-30 14 43 15
169852 90 1999-03-31 2003-06-30 17 52 18
169944 90 1999-03-31 2003-09-30 18 55 19
280510 149 1999-05-29 2001-02-28 7 22 8
280875 149 1999-05-29 2002-02-28 11 34 12
281240 149 1999-05-29 2003-02-28 15 46 16
282035 150 1999-05-30 2000-02-29 3 10 4
282400 150 1999-05-30 2001-02-28 7 22 8
282765 150 1999-05-30 2002-02-28 11 34 12
I am trying to find the week number of a particular month given a date, so I want to know which week is that for given month
Example if I enter
2016 Feb 2 ---> Week 1
2016 Feb 9 ---> Week 2
2016 June 2 ---> week 1
2016 Jan 25 ---> week 5
Can I achieve this in a T-SQL query?
I have seen the following option
DATEPART(wk, BookingTimeStamp)
But that gives the week number of the year, not the month
The idea is to build result per week for a given month
The first result using "SQL Server and Weeks in Month" returned this article. It shows two ways using DATEPART along with other date parsing functions. Here is one solution:
DECLARE #MyDate DATETIME =GETDATE()
SELECT DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, #MyDate), 0), #MyDate) +1
I ran into the same problem when I wanted to get not the number of the week in the year, but the number of the week in relation to the month.
In my solution, you can divide every day of date by 7. Then all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
This is a simple example of a practice query where you can use the case construct to find the number of the week.
Solution 1 (a bit confusing)
SELECT
case
when CAST(strftime('%d', date) as real) / 7 BETWEEN 0 AND 1 then 'week_1'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 1 AND 2 then 'week_2'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 2 AND 3 then 'week_3'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 3 AND 4 then 'week_4'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 4 AND 5 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Solution 2 (simple)
SELECT
case
when CAST(strftime('%d', date) as integer) BETWEEN 1 AND 7 then 'week_1'
when CAST(strftime('%d', date) as integer) BETWEEN 7 AND 14 then 'week_2'
when CAST(strftime('%d', date) as integer) BETWEEN 14 AND 21 then 'week_3'
when CAST(strftime('%d', date) as integer) BETWEEN 21 AND 28 then 'week_4'
when CAST(strftime('%d', date) as integer) BETWEEN 28 AND 31 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Deduction of solution 1
If you're just looking for a solution to an issue, then you don't need to read any further. I just want to tell you how I arrived at this solution and why you can trust it.
This solution can be visualized using the Python programming language.
from matplotlib import pyplot as plt
days = [x for x in range(1, 32)]
days_on_seven = [x / 7 for x in range(1, 32)] # Divide each day by 7
print(days_on_seven)
## Result is below
[0.14285714285714285,
0.2857142857142857,
0.42857142857142855,
0.5714285714285714,
0.7142857142857143,
0.8571428571428571,
1.0,
1.1428571428571428,
1.2857142857142858,
1.4285714285714286,
1.5714285714285714,
1.7142857142857142,
1.8571428571428572,
2.0,
2.142857142857143,
2.2857142857142856,
2.4285714285714284,
2.5714285714285716,
2.7142857142857144,
2.857142857142857,
3.0,
3.142857142857143,
3.2857142857142856,
3.4285714285714284,
3.5714285714285716,
...
3.857142857142857,
4.0,
4.142857142857143,
4.285714285714286,
4.428571428571429]
Visualisation
As you can see, we get an array of increasing numbers. Let's graph the resulting numbers for each day of the month.
from matplotlib import pyplot as plt
import seaborn as sns
sns.lineplot(x=days, y=days_on_seven)
plt.xlabel("Number of day in month")
plt.ylabel("Result of division by 7")
plt.title("day / 7 plot")
The graph below by link
https://i.stack.imgur.com/SIVfT.png
The graph is very crude, but you can see, as noted earlier, that all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
I hope my decision was helpful and interesting to you.
I have this in my DataBase :
Table Days:
IdDay NameDay
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
Table Time:
IdTime Time
1 9am
2 10am
3 11am
Table Work:
IdWork NameWork IdDay IdTime
1 cleaning 6 3
2 Studying 1 2
I am trying to edit a Matrix in My ReportViewer:
Day [Time]
[NameDay]
I want to use code and edit my Matrix like this Algo :
if database day = day in the matrix && database Time= time in matrix
Put NameWork
Is there a way to do that ?
In T-SQL I would just use a group by clause and a count(*) in the select statement to give me the value I need. But with cubes it's different, because the count isn't just over rows, but dimensional combinations. So I've googled for an answer to no avail. Here is a detailed explanation of my problem:
My original MDX is:
SELECT
NON EMPTY
{
[Measures].[Budget]
} ON COLUMNS
,NON EMPTY
{
[Location].[Category - Entity - Facility].[Facility].ALLMEMBERS*
[Location].[Category - Facility - Unit].[Location].ALLMEMBERS*
[Calendar].[Day].[Day].ALLMEMBERS
} ON ROWS
FROM
(
SELECT
{[Location].[Category - Entity - Facility].[Category].&[3]} ON COLUMNS
FROM
(
SELECT
[Calendar].[Year - Quarter - Month - Day].[Day].&[2012-01-01T00:00:00]
: [Calendar].[Year - Quarter - Month - Day].[Day].&[2012-05-31T00:00:00]
ON COLUMNS
FROM [PHI Census]
)
)
Results look like this:
Facility 1 Location 1 Day 1 100
Facility 1 Location 1 Day 2 100
Facility 1 Location 1 Day 3 100
Facility 1 Location 1 Day 4 100
Facility 1 Location 2 Day 1 80
Facility 1 Location 2 Day 2 80
Facility 1 Location 2 Day 3 80
Facility 2 Location 1 Day 1 65
Facility 2 Location 1 Day 2 65
Facility 2 Location 1 Day 3 65
Facility 2 Location 1 Day 4 65
Facility 2 Location 2 Day 1 73
Facility 2 Location 2 Day 2 73
Facility 2 Location 2 Day 3 73
This gives me the [Budget] listed once for each Facility-Location-Day combination. I would like to remove [Calendar].[Day].[Day].ALLMEMBERS from the ON ROWS clause and simply use a calculate member that would return the count of the number of days for each Facility-Location combination along with each row. So basically,
The results would look like this:
Facility Location Budget DayCount
Facility 1 Location 1 100 4
Facility 1 Location 2 80 3
Facility 2 Location 1 65 4
Facility 2 Location 2 73 3
The expression of DayCount could be:
MEMBER [Measures].[DayCount] AS Count(NonEmpty([Calendar].[Day].[Day].ALLMEMBERS, [Measures].[Budget]))