Increment Row Index And Union With Other Tables On Dynamic Dates - mysql

Question:
I looked at various other examples to increment over rows, but all resulted in the same wrong output. The problem which I encountered was that my code did not successfully increment over rows to build a correct index per new row in the result-set per episode (highlighted in red below).
My first try was:
SET #ep_1 = "Peaky Blinders";
SET #curRow_1 = 0;
SELECT
DATE_FORMAT(created_at, "%Y%m%d") AS year_month_day,
#curRow_1 := #curRow_1 + 1 AS row_number,
#ep_1 AS episode_title,
COUNT(id) AS episode_plays
FROM netflix.episode_plays
WHERE
episode_id = "xyz"
AND created_at >= "2019-07-01" AND created_at <= "2019-07-07"
GROUP BY 1
Other than the rows not incrementing correctly; I also got the following error when I tried setting some variables in the beginning of my code:
Error running query: Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8_general_ci,IMPLICIT) for operation '='
(Note: I have no affiliation with Netflix, I just used Netflix dummy data to answer my question)

I broke down my question in various sections and got to the final answer below.
The most important part was to add the initial result-sets into a subqueries, and thereafter select the data from tables x1,x2, etc.
The second part of the question was, how to combine multiple datasets together (in my case: how do one not only do it for one specific netflix episode, but multiple episodes)? I settled on the UNION ALL - clause.
In the first iteration I tried hard-coding the dates, and thereafter found the INTERVAL-function very helpful.
Finally, the unicode-error I fixed by adding COLLATE utf8_unicode_ci after setting my variables.
If you find mistakes in my code or have any other suggestions, please feel free to suggest them.
-- SET DATA
-- variables for table x1
SET #ep_1 = "Peaky Blinders" COLLATE utf8_unicode_ci;
SET #id_1 = (SELECT id FROM netflix.episodes WHERE episode_title = #ep_1);
SET #date_1 = (SELECT created_at FROM netflix.episodes WHERE episode_title = #ep_1);
SET #curRow_1 = 0;
-- variables for table x2
SET #ep_2 = "Brooklyn Nine-Nine" COLLATE utf8_unicode_ci;
SET #id_2 = (SELECT id FROM netflix.episodes WHERE episode_title = #ep_2);
SET #date_2 = (SELECT created_at FROM netflix.episodes WHERE episode_title = #ep_2);
SET #curRow_2 = 0;
-- QUERY DATA
SELECT
x1.year_month_day,
#curRow_1 := #curRow_1 + 1 AS row_number,
x1.episode_title,
x1.episode_plays
FROM (
SELECT
DATE_FORMAT(created_at, "%Y%m%d") AS year_month_day,
#ep_1 AS episode_title,
COUNT(id) AS episode_plays
FROM netflix.episode_plays
WHERE
episode_id = #id_1
AND created_at >= #date_1 AND created_at <= DATE_ADD(#date_1 , INTERVAL 7 DAY)
GROUP BY 1) x1
UNION ALL
SELECT
x2.year_month_day,
#curRow_2 := #curRow_2 + 1 AS row_number,
x2.episode_title,
x2.episode_plays
FROM (
SELECT
DATE_FORMAT(created_at, "%Y%m%d") AS year_month_day,
#ep_2 AS episode_title,
COUNT(id) AS episode_plays
FROM netflix.episode_plays
WHERE
episode_id = #id_2
AND created_at >= #date_2 AND created_at <= DATE_ADD(#date_2 , INTERVAL 7 DAY)
GROUP BY 1) x2

Related

UPDATE from SELECT but still return selected data in MySQL

I have read a bunch of ways that has gotten me this far. But I can't get to the finish line.
I have a table of coupon codes. I want to use one transaction to select the next available code, mark it as used and input the order number. I can get the update and nested select to work, but I cannot figure out how to actually return the coupon code from the select. It just returns 1 row updated.
Here's what I've got:
UPDATE `prcoupon` pr
SET
`pr`.`status` = '1',
`pr`.`invoicenumber` = '09990002'
WHERE
`pr`.`couponCode` = (SELECT
`prcoupon`.`couponcode`
FROM
`prcoupon`
WHERE
`status` = 0
LIMIT 1)
Sample data
What I need returned is: couponCode: SL2T-03A0-JVCY-W2XMXG
If I understand correctly, you can try to use UPDATE ... JOIN with ROW_Nunber windwon function.
UPDATE prcoupon pr
JOIN (
SELECT *,ROW_NUMBER() OVER(ORDER BY couponCode) rn
FROM prcoupon
WHERE status = 0
) t2 ON pr.couponcode = t2.couponcode
SET pr.status = 1,
pr.invoicenumber = '09990002'
WHERE rn = 1
sqlfiddle

sql rank row results

I"m trying to add a new col that shows the rank (or sequence) of row results by date.
I've written:
SELECT
#row_number:=(CASE
WHEN #member_id = lh.member_id and lc.ladder_advocacy is not null
THEN #row_number + 1
when #member_id = lh.member_id and lc.ladder_advocacy is null then "null"
ELSE 1 /* there is an error here - i need it to return a 1 if not null, then 2 for the 2nd instance, etc */
END) AS rank_advocacy,
#member_id:=lh.member_id AS member_id,
lh.ladder_change,
lc.name,
lc.ladder_advocacy,
lc.ladder_elected,
lc.ladder_policy,
lc.ladder_engagement,
lc.ladder_newventure,
lc.ladder_collective,
lc.is_trigger
FROM
leenk_ladder_history AS lh
LEFT JOIN
leeds_so.leenk_ladder_config AS lc ON lh.ladder_config_id = lc.id
WHERE
ladder_change = 1 AND trigger_active = 1
ORDER BY member_id, trigger_event_date DESC;
There is an error at row 4, and I'm not sure how to fix it. For the first result, I want to return 1. for the second results, I want to return #row_number + 1. Third result, #row_number+2 (etc).
How do I achieve this?
I don't understand how the condition lc.ladder_advocacy is not null is being used. However, the basic structure is:
SELECT (#row_number = IF(#member_id = lh.member_id, #row_number + 1
IF(#member_id := lh.member_id, 1, 1)
)
) as rank_advocacy,
lh.ladder_change,
. . .
Some really important points:
You need to assign #member_id and #row_number in the same expression. MySQL (as with all other databases) does not guarantee the order of evaluation of expressions.
In more recent versions of MySQL, I think the ORDER BY needs to go in a subquery, with the variable expressions in the outer query.

Convert time "28:45" to "4:45" MySQL

I'm looking for a way to order my results based on the actual time. In my table yo can see values like:
1,23:45
2,9:45
3,27:43
When I do a query I would like to know how to order them based on their actual 24 hour time.
Ex:
3,3:43
2,9:45
1,23:45
Notice how it changes 27:43 to 3:43, and creates the order.
Where I am using it, in this query:
SELECT *,COALESCE(ADDTIME(s.`departure_time`,SEC_TO_TIME(rt.delay)),s.`departure_time`) as `rt_time` FROM `stop_times` s INNER JOIN `trips` t ON s.`trip_id` = t.`trip_id` INNER JOIN `stops` st ON st.`stop_id` = s.`stop_id` INNER JOIN `routes` r ON r.`route_id` = t.`route_id` LEFT JOIN `rt_trips` rt ON t.`trip_id` = rt.`trip_id` where (s.`stop_id` = 'CB900') and ( ( s.`departure_time` >= '00:50' and s.`departure_time` <= '05:50') OR ( s.`departure_time` >= '24:50' and s.`departure_time` <= '29:50') ) and (s.`pickup_type` = '0') and (t.`service_id` IN ('removed to make it easier')) HAVING (`rt_time` BETWEEN '01:50' and '05:50' ) ) OR ( `rt_time` BETWEEN '25:50' and '29:50' ) ORDER BY `order` ASC
Explanation:
Information is a transit schedule, that may go forward onto the next day which may be a saturday. So, times may become 25:50, where that means 1:50 the next day.
Thanks
Cyrus
Hmmm, if you just want to get a value between 0 and 24 hours, then I would do:
select concat(mod(substring_index(time_column, ':', 1) + 0, 24), ':',
substring_index(time_column, ':', -1)
)
Try this function on the time_column
concat(mod(substr(time_column,1,INSTR(time_column, ':')-1),24)
,substr(time_column,INSTR(time_column, ':'),3)
)
You might need to cast date to string to integer, do the maths, and again cast it to time. But the fiddle version seems to work properly on varchar to integer conversion. Check this
http://sqlfiddle.com/#!9/ff60f9/1

mysql variable with #variables for calculated values

I have a mysql query where I need to calculate values like ROUND(SUM(temp.total_pq),2) multiple times, so I defined variables to avoid repeating them.
But the line 5 in the query returns wrong value in the results. The value for #diff_client_partner_qtty := ROUND((#partner_qtty_all_runs - #client_qtty_all_runs), 2) AS diff_client_partner_qtty is always NULL the first time I run and thereafter always 84.
I asked the in-house DBA and he says I should not use variables in my query like this because the order in which mysql will set values for the variable is not predictable and hence I may get NULL value.
But why? Also can someone please propose then another way whereby I can avoid rewriting ROUND(SUM(temp.total_pq),2) multiple times other than a subquery. I would prefer to avoid a subquery because I think even in its current form query is not that readable.
SELECT temp.dtaccounted AS accounting_period,
#partner_qtty_all_runs := ROUND(SUM(temp.total_pq),2) AS partner_qtty_all_runs,
ROUND(temp.mmq,2) AS mopay_qtty,
#client_qtty_all_runs := ROUND(SUM(temp.total_cq),2) AS client_qtty_all_runs,
#diff_client_partner_qtty := ROUND((#partner_qtty_all_runs - #client_qtty_all_runs), 2) AS diff_client_partner_qtty,
#partner_gtv := ROUND(temp.total_pq_gtv, 2) AS partner_gtv,
#client_gtv := ROUND(temp.total_cq_gtv,2) AS client_gtv,
#diff_client_partner_gtv := ROUND((#partner_gtv - #client_gtv), 2) AS diff_client_partner_gtv,
temp.stariffcurrency AS tariffcurrency,
ROUND(#diff_client_partner_gtv * ffactor, 2) AS diff_client_partner_gtv_eur,
temp.scountry AS country,
temp.spartnererpid AS partner_erp_id,
c.name AS partner_name,
temp.nproducttype AS product,
temp.capping
FROM
(SELECT SUM(npartnerquantity) AS total_pq,
SUM(nmindmaticsquantity) AS mmq,
SUM(nclientquantity) AS total_cq,
SUM(dgrosstariff * npartnerquantity) AS total_pq_gtv,
SUM(dgrosstariff * nclientquantity) AS total_cq_gtv,
nrun,
vb.scountry,
vb.spartnererpid,
dtaccounted,
stariffcurrency,
vb.nproducttype,
cq.bisenabled AS capping
FROM report_table vb,
client_table cq
WHERE vb.accperiod > '2013-12-01'
AND vb.partnerid = cq.partnerid
AND vb.scountry = cq.scountry
AND vb.nproducttype = cq.nproducttype
AND (cq.dtvalidto IS NULL
OR cq.dtvalidto > vb.accperiod)
GROUP BY scountry,
nproducttype,
partnerid,
nrun,
accperiod
) temp,
customer c,
currency_conversion cc
WHERE temp.partnerid = c.erp_id
AND temp.total_pq <> temp.total_cq
AND cc.scurrencyfrom = temp.stariffcurrency
AND cc.scurrencyto = 'EUR'
AND cc.dtrefdate = temp.accperiod
GROUP BY temp.scountry,
temp.partnerid,
c.name,
temp.nproducttype,
temp.accperiod
ORDER BY temp.accperiod,
temp.scountry,
temp.partnerid,
temp.nproducttype,
temp.capping \G;

Assigning a value from another database and function - MySQL variable scope

I am using MySQL to make a report showing the number of hours billed for a particular date range and project. The complexity is that the date range is variable for each project (different start month and start day). This information is coming from a value in another database/table.
I have the following UDF in MySQL:
DELIMITER //
CREATE FUNCTION TimeLeft(startday INT, today INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s INT;
IF startday < today THEN SET s = 0;
ELSE SET s = 1;
END IF;
RETURN s;
END //
DELIMITER;
I use that function in the following query, which is supposed to take the value returned in the TimeLeft function to determine the values for the start month (month(curdate())-#xx) and start day (#yy) for each project to calculate the hours:
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
Here's how I am setting the values for #xx and #yy:
SET #xx = 0; #this is the value that we will use to manipulate the month for the date range
SET #yy = 0;
#yy:= SELECT start_day_of_month FROM dashboard.client; #this doesn't seem to work
SELECT #xx:= TimeLeft(#yy,dayofmonth(curdate()));
I am getting some issues:
#yy is not getting the value - possibly my syntax is wrong?
The variables are set at the top of the code, so they are not getting changed for each project as they should be (there should be a different #xx and #yy for each project since each one has a different start and end date).
Here's the full query:
#below is where I assign the variables
SET #xx = 0; #this is the value that we will use to manipulate the month for the date range
SET #yy = 0;
#yy:= SELECT start_day_of_month FROM dashboard.client; #this doesn't seem to work
SELECT #xx:= TimeLeft(#yy,dayofmonth(curdate()));
# below is the MySQL query that is meant to use the variables assigned above
SELECT X.expr1 AS 'Project Name', #monthly_hours - SUM(X.expr2) AS 'Hours Billed
FROM
(SELECT
projects.name AS expr1
, sum(time_records.value) AS expr2
FROM project_objects
INNER JOIN projects
ON projects.id = project_objects.project_id
INNER JOIN time_records
ON time_records.parent_id = project_objects.id
WHERE time_records.parent_type = 'Task'
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
AND time_records.record_date <= curdate()
GROUP BY projects.name
UNION
SELECT
projects.name AS expr1
, sum(time_records.value) as expr2
FROM projects
INNER JOIN time_records
ON projects.id = time_records.parent_id
WHERE time_records.parent_type = 'Project'
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
AND time_records.record_date <= curdate()
GROUP BY projects.name) X
GROUP BY X.expr1
I think there is some issue of where I am assigning the variables #xx and #yy. These should be done for each individual Project, so putting them up on the top is probably not the best idea. I'm also not sure if I am assigning the #yy value correctly. It's supposed to query the value of the field of a table that is in another database but it keeps throwing a syntax error on the #yy assignment to that field.
Assign value to #yy inside select:
SELECT #yy:= start_day_of_month FROM dashboard.client;