How to query making matching pairs of columns in rails? - mysql

I have a movies table in the database with separate columns (integers) for month and year of release date.
I want to get all the movies with the release date from 09/2016 till 02/2017.
This query won't work because of other unwanted results:
Movie.where(month: [09, 10, 11, 12, 1, 2], year: [2016, 2017])
How do I query making pairs of month & year?

Storing dates in that way is a very poor design decision (if you don't also store an actual date value).
So, either do data migration and create a date column which will be trivial to query, or do a big-ass conditional query:
SELECT movies.*
FROM movies
WHERE month = 9 AND year = 2016 OR
month = 10 AND year = 2016 OR
...
month = 2 AND year = 2017
To build this query in activerecord, you can use this answer (rails-5 specific).

Related

What other expressions can I use to try to calculate average daily amount in SSRS Report

I am building a report with 2 matrix tables with different (but very similar) data sets. I need to calculate the average daily transaction amount where the user inputs the month and year they want to see. I was able to use the following expression on the first table with no issue.
=sum(fields!tot_rev.Value)/fields!Day.Value
Where "Day" is the day of the month. I used "Day(trn_dt) as Day" in my query to extract the day from the datetime string.
I tried using the same expression in my second table but it only returns the sum total amount. If I use
=sum(fields!amt.Value)/countdistinct(fields!Day.Value)
it returns a value that seems to be random (I don't know how it came to that value with any calculation)
The only way I have been able to successfully get the average was by inputting the number of days in the expression.
=sum(fields!amt.Value)/22
But I need this to work with the parameters and the number of days will not be constant.
Here is a sample of my data from dataset1:
SELECT MONTH(inv_dt) AS Month, DAY(inv_dt) AS Day, YEAR(inv_dt) AS Year, bill_by, inv_no, tot_rev
FROM inv_info
WHERE (status_cd IN ('FF', 'FP')) AND (MONTH(inv_dt) = #Month) AND (YEAR(inv_dt) = #Year)
Month, Day, Year, bill_by, inv_no, tot_rev
10, 23, 2020, ERSA, 40444, 4881
10, 23, 2020, ERSA, 40443, 2043
10, 22, 2020, DYCO, 40435, 2504
10, 22, 2020, ERSA, 40431, 20524
Here is sample from dataset2 (table with the problem):
SELECT YEAR(trn_info.trn_dt) AS YEAR, MONTH(trn_info.trn_dt) AS MONTH, DAY(trn_info.trn_dt) AS Day, trn_info.reg_no, trn_dtail.amt, trn_info.acc_by
FROM trn_info LEFT OUTER JOIN
trn_dtail ON trn_info.trn_no = trn_dtail.trn_no
WHERE (trn_info.status IN ('FP', 'FF')) AND (trn_info.tpe_cd IN ('AI', 'BI')) AND (trn_dtail.rte_cd = 'TT') AND (MONTH(trn_info.trn_dt) = #Month) AND (YEAR(trn_info.trn_dt) = #Year)
Year, Month, Day, reg_no, amt, acc_by
2020, 10, 1, 40113, 377.96, JLMA
2020, 10, 1, 40115, 6637.07, NASO
2020, 10, 2, 40104, 790, MCGO
2020, 10, 2, 40106, 1406.25, MCGO
2020, 10, 3, 40100, 239.77, JEDX
In the attached picture the upper left corner expression is: =MonthName(Fields!MONTH.Value,false)
The day of month is listed along the left with the total count (count of transactions) and sum for each processor(acc_by/bill_by) in the data field.
I used =iif(isnothing(Sum(Fields!amt.Value)), 0, sum(fields!amt.Value)) as the expression for the amt field.
The column total is the total count and sum for that day from all processors.
The row total at is the average daily count and average daily amt by processor and where the total column and total row intersect it is the average daily count for the month and I would like to show the average daily transaction amt for the month as well but this is what isn't working properly.
See attached image:
image of table 2
Does anyone know what else I can try?
Don't break a date down into its component parts until you are presenting it, and even then use formatting to display the date part without converting the date to ints or strings. So many calculation bugs are introduced this way.
Leave inv_dt and trn_dt as Datetime values in your query and bring them into SSRS that way. Within SSRS, use the date formatting values to show just the year or month.
To calculate your daily average, create a grouping in the SSRS report on the date. Choose an easily understood group name, like InvoiceDate or something. Then use Avg(fields!amt.value,"InvoiceDate"). This will tell SSRS to average the amount within the grouping (each day). If desired, you can than average the average by Avg(Avg(fields!amt.value,"InvoiceDate")).
Please take the time to learn how to manipulate dates in SQL and SSRS. Your query results will be more accurate and your peers will thank you when maintaining your code.
As a sample of what I'm talking about, see if you can spot the logic bug in some DB2 code I'm updating this week (Hint problem happens in January:
AND Month(CUST.acct_svc_strt_d) >= (Month(CURRENT DATE) - 1)
AND Year(CUST.acct_svc_strt_d) = Year(CURRENT DATE)

Convert "YYYYWW" into month (SSRS)

I am creating a report and would like to retrieve data based on a date field (string) in the format YYYYWW. I want to filter the data so I retrieve the current month, previous month, the month before that and older values - using 4 different datasets.
For example:
201547 = November 2015
How do I create a calculated member which converts a string into the month name and year? I would like to use this value to filter out the data from the cube. Is there any other way of doing this?
:]1
Try adding a calculated field and set it to this expression:
=MonthName(Month(DateAdd("ww",CInt(MID("201547",5,LEN("201547")))
,DateSerial(CInt(LEFT("201547",4)),1,1)))) & " " & LEFT("201547",4)
Then use that field to filter your report.
Replace the string for the field returning "201547"
=MonthName(Month(DateAdd("ww",
CInt(MID(Fields!YearWeek.Value,5,LEN(Fields!YearWeek.Value)))
,DateSerial(CInt(LEFT(Fields!YearWeek.Value,4)),1,1))))
& " " & LEFT(Fields!YearWeek.Value,4)
UPDATE: MDX script for querying a calulated member with the required format:
I've tested with the following MDX:
WITH MEMBER [Measures].[month&year] AS
'FORMAT(DateAdd("ww",STRTOVALUE(MID("201547",5,LEN("201547"))) ,CDATE(DateSerial(LEFT("201547",4),1,1))),"MMMM") + " " + LEFT("201547",4)'
SELECT { [month&year] } ON COLUMNS
FROM [Your Cube]
To use it in your query replace "201547" for [Dimension].[Attribute].MemberValue that corresponds to your cube structure:
WITH MEMBER [Measures].[month&year] as
'FORMAT(DateAdd("ww",STRTOVALUE(MID([Dimension].[Attribute].MemberValue,5,LEN([Dimension].[Attribute].MemberValue)))
,CDATE(DateSerial(LEFT([Dimension].[Attribute].MemberValue,4),1,1))),"MMMM") + " " + LEFT([Dimension].[Attribute].MemberValue,4)'
SELECT
{ [Dimension].[Attribute].[Attribute] }
{[month&year]} ON COLUMNS
FROM [Your Cube]
Once you have this field in your dataset you can easily filter the values using a parameter.
Let me know if this can help you.
Using a combination of the resources found out on the internet you can find the information you require.
First you need to find the first day of the first week of the year. This has already been determined by this answer as
DateAdd("d",
(-1) * (CInt(New DateTime(Year(Now), 1, 1).DayOfWeek) +
IIf(CInt(New DateTime(Year(Now), 1, 1).DayOfWeek) < DayOfWeek.Monday,
7,
0)
) + 1,
New DateTime(Year(Now), 1, 1))
This needs to be adapted for any year - not just the current one by finding the year from the string as follows
CInt(Left(Parameters!myDate.Value,4))
Now, as noted in this resource you can use a DateAdd to add the number of weeks of the year you have entered to the first date of the year
=(DateAdd(DateInterval.WeekOfYear,
CInt(Right(Parameters!myDate.Value,2))-1,
<<FIRST_DAY_OF_YEAR>>)
You then want to get the month name of the month of this date, using Month and MonthName. Smashing it all together would result in the following
=MonthName(Month(DateAdd(DateInterval.WeekOfYear,
CInt(Right(Parameters!myDate.Value,2))-1,
DateAdd("d",
(-1) * (CInt(New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1).DayOfWeek) +
IIf(CInt(New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1).DayOfWeek) < DayOfWeek.Monday,
7,
0)
) + 1,
New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1))
)))
This will get a Textual value of the month for the interval
Create a new placeholder in the same textbox and then repeat the above for finding the year
=Year(DateAdd(DateInterval.WeekOfYear, ...
(Remember to remove the last brace otherwise you’ll get a End of Statement expected error.)
This shoud give what you require, so
201501 = December 2014
201547 = November 2015
201553 = December 2015
201622 = May 2016
Let me know if this helps, or if you have further questions
Part 1: Establishing which day falls into which week
ISO-8601 declares the international standards for converting dates into week numbers, and vice-versa.
ISO standard weeks start on a Monday. A year can have either 52 or 53 weeks.
There are 4 ways of establishing when Week 1 starts:
- It is the first week with a majority (4 or more) of its days in January.
- Its first day is the Monday nearest to 1 January.
- It has 4 January in it. Hence the earliest possible dates are 29 December through 4 January, the latest 4 through 10 January.
- It has the year's first working day in it - i.e. excluding Saturdays, Sundays and the 1st of January.
Part 2: Excel calculations
This answer assumes that the dates are being stored as 6-character strings.
First determine the year and what day of the week it starts on.
1) Strip the first four characters from the string.`
=left(<cell containing the year+week string>, 4)
2) Prepend "1/1/" to it.
="1/1/"&left(<cell containing the year+week string>, 4)
3) Convert this string to an Excel date
=datevalue(<the last result>)
4) Determine the Weekday of the 1st day of that year.
=weekday(<the last result>)
5) Calculate the Excel date of the first Monday.
=if((<the last result>-4)<0,<the result of step 3>+8-<the last result>, <the result of step 3>+1-<the last result>)
6) Work out how many weeks on from that we are:
=right(<original 6 char year/week string>,2)
7) Convert 6) to a number
=value(<last result>)
8) Add that to the Monday of Week 1
=<result of step 5>+<result of step 7>*7
9) Convert this to a month number
=month(<last result>)
10) Convert this to a month name
=choose(<last result>,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
11) Add the year back on
=<lastresult>&" "&<result of 1>
This gives the month number which contains the Monday of the current numbered week.
If cell A1 contains '201547
then cell B1 should contain:
=CHOOSE(MONTH(IF((WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4)))-4)<0,DATEVALUE("1/1/"&LEFT(A1,4))+1-WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4))),DATEVALUE("1/1/"&LEFT(A1,4))+8-WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4))))+7*(VALUE(RIGHT(A1,2)))),"January","February","March","April","May","June","July","August","September","October","November","December")&" "&LEFT(A1,4)
If A1 contains 201547 then B1 returns "November 2015".
Further considerations:
You may want to make it more sophisicated, so that a month ending in a Monday does not have the week commencing with its final Monday counted as part of the month. More generally, that week only counts as part of a month is 3 or more of the days are in the month. Or 3 or more of its working days are part of the month.
As it is, I would sort with the calculated month and year as primary key and the YYYYMM code as secondary key.

query criteria to show records for a certain month

I have some records in a query with a column of dates. I want it to only show the records that occur in March. This link
https://support.office.com/en-US/Article/Examples-of-query-criteria-3197228c-8684-4552-ac03-aba746fb29d8#bm1 shows the different types of criterion.
The one below in the table describes how to show only what occurs in a particular month:
"Contain a date that falls in a specific month (irrespective of year), such as December
DatePart("m", [SalesDate]) = 12
Returns records where the transactions took place in December of any year."
I don't know what the SalesDate means in the criteria function, and there isn't any explanation on the page.
[SalesDate] implied a Date/Time field named SalesDate. If your Date/Time field is named something else, such as invoice_date, substitute that name in the DatePart expression:
DatePart("m", [invoice_date]) = 12
For March instead of December invoices, use this:
DatePart("m", [invoice_date]) = 3
You could also use Month() instead of DatePart to get the same result:
Month([invoice_date]) = 3

Grouping months by quarter over multiple years depending on a dynamic start month

Using MySQL and PHP I am building a JSON array to populate a data table.
For the purposed of my question suppose the data table has the following columns:
Year: 2010,2011,2012,2013...<br/>
Month: 1,2,3,4,5...<br/>
Value: 100, 150, 200 etc...<br/>
The table structure cannot be altered and my solution needs come into the MySQL query
The data can be viewed either monthly, quarterly or yearly. Monthly and yearly is achieved easily through grouping by year and month.
Quarterly data can be grouped by calendar quarter (Jan-Mar, Apr-Jun, Jul-Sep, Oct-Dec) by this group statement:
GROUP BY year, round((month/3)+0.3,0)
So where Jan, Feb and March might all have 100 for their value the summed result is 300, same for other months.
Now my problem comes when I want to group values by a financial quarter, for example a quarter that starts in Feb, or any other quarters.
I have a statement that works for the quarter grouping using two variables that can be accessed via the SQL query, start_year (i.e. 2014) and start_month (i.e. 2)
GROUP BY year, (((round(((((month-(start_month-1))+((year-start_year)*12))-((year-start_year)*12))/3)+0.33,0)/4)+4)-floor(((round(((((month-(start_month, '%m')-1))+((year-start_year)*12))-((year-start_year*12))/3)+0.33,0)/4)+4)))*12
which basically will assign a 0,3,6,9 value to each calendar month for the purposes of grouping.
In the financial year starting February this works fine for quarters 1-3, however breaks for the final quarter as it includes Nov and Dec 2014 data and Jan from 2015.
As a result I get 5 rows of data instead of 4.
This is because of the preceding GROUP by year clause, an important addition as we might want to generate a table that views sequential quarters for multiple years.
So what I am looking for is a way of grouping the years together by offsetting the start month.
So when the year starts in Jan it will group Jan-Dec but if we change that to starting Feb it will group Feb-Jan.
Any ideas, suggestions most welcome!
Regards,
Carl
I solved a similar problem just now (a Moodle report aggregating assignment scores by year and quarter) with something like this:
select year(from_unixtime(s.timemarked)) as year, quarter(from_unixtime(s.timemarked)) % 4 + 1 as quarter, count(distinct data1) as "tickets graded" from mdlassignment_submissions s where grade >= 0 group by year, quarter order by year, quarter;
The relevant part for what you're doing is quarter(from_unixtime(s.timemarked)) % 4 + 1 as quarter
As another commenter pointed out, MySQL has a quarter() function, but it doesn't do financial quarters. However, since (as I understand it, at least, based on consulting the relevant wikipedia page) financial quarters are just offset by 1, the % 4 + 1 at the end should convert it.

Get sum of values based on the value of 2 other date related columns

Given the sample data in the screenshot below, would it be possible in mysql to return a sum of values from monthly_amount only where the values are before this month. I used a join to pull this data. The 5 left columns are from one table, and the rest are from another.
The issue I'm running into is, lets say its April of 2015, I can't just do a sum WHERE goal_year <= 2015 AND month_id_FK <= 4, or else I'll get only those 4 months from both years, when in that scenario, I really want all the months from 2014, plus the 4 months from 2015.
I could handle this in PHP, but I wanted to first see if there would be a way to do this in mysql?
try
WHERE Goal_Year*100+month_id_FK <= 201504
alternatively:
WHERE
GOAL_YEAR < 2015 OR
(GOAL_YEAR = 2015 and month_id_FK <= 4)
select sum(monthly_amount) from table where goaldate<(SELECT CURDATE())
this is not the actual query for your table..but if you do like this you will get the answer
you need the sum of monthly amount where the date is before current-date means today.
then you can just compare the currentdate with goal date