Sample Header Ad - 728x90

User variable in nested subquery not getting picked up - in complex sort of 2 tables with gaps

3 votes
1 answer
5038 views
I have just recently learned about user variables (ie, @myvar) and am trying to create a complex sorting query that takes 2 tables where the second is missing a column which then gets filled in by a subquery. In this subquery I need to generate a number by doing a lookup on the first table and modifying the result. More specifically, I have a table A (id, date), and table B (id, weekday) and I need to return a date for each row of B where it is the *next date occurring on the same weekday where such a date does not occur in A*, all starting from a specific minimum date, where entries in table A are all higher (ie, later) than that date. Table B is sorted by staggered weekday (First Monday, First Tuesday... Second Monday, Second Thursday..) and then these get "slotted in" (by assigning "date") where that date in A does not occur. So if the two tables look something like: A id | date a-a 2014-11-11 a-b 2014-11-13 a-c 2014-11-18 B id | weekday b-a Tuesday b-b Tuesday b-c Wednesday Then the resulting sort (if start date is 2014-11-10, a Monday) would be: sorted id | date a-a 2014-11-11 b-c 2014-11-12 (Wednesday) a-b 2014-11-13 a-c 2014-11-18 b-a 2014-11-25 (Tuesday) b-b 2014-12-02 (Tuesday) I have been working on this query for a while now, and almost had it done, but then I couldn't find a way to pass the "start date" to the subquery that allocates dates to table B. The docs say that > Subqueries in the FROM clause cannot be correlated subqueries. They are materialized in whole (evaluated to produce a result set) before evaluating the outer query, so they cannot be evaluated per row of the outer query. So I reimplemented the subquery to move the variable to a WHERE clause, using JOINs, but this still didn't seem to help, since I couldn't figure out how to return just the single row needed without an outer query to do that. So here is what I have, in the hopes someone can help me figure this out: SELECT @next_date as next_date, @tmp_i := CASE w.weekday + 0 WHEN 0 THEN @idxw_U:=@idxw_U+1 WHEN 1 THEN @idxw_M:=@idxw_M+1 WHEN 2 THEN @idxw_T:=@idxw_T+1 WHEN 3 THEN @idxw_W:=@idxw_W+1 WHEN 4 THEN @idxw_R:=@idxw_R+1 WHEN 5 THEN @idxw_F:=@idxw_F+1 WHEN 6 THEN @idxw_S:=@idxw_S+1 END as idxW, @idxw_offset := (7 + w.weekday - dayofweek(@next_date)) mod 7 as idxw_offset, @idxw_corr := (@tmp_i *7)-7 + (7 + w.weekday - dayofweek(@next_date)) mod 7 as idxW_corr, w.weekday as weekday, @dayofweek:= w.weekday+0 as dweekday, @TMP_date:= DATE_ADD(@next_date, INTERVAL (@idxw_corr) DAY) as tmp_date, @clash_offset:= IF((SELECT 1 FROM t_date WHERE date = @TMP_date), IFNULL( ( SELECT next_slot FROM ( SELECT -- DAYOFWEEK(d.date) as weekdaynum, -- dayname(d.date) as weekday, -- d.date, -- MIN(d2.date) as min_d2, DATE_ADD(MIN(d2.date), INTERVAL 1 WEEK) as next_slot, -- DATEDIFF(d2.date, d.date) as datediff, -- DATEDIFF(d2.date, d.date) DIV 7 as weeksdiff, -- DATEDIFF(MIN(d3.date), d2.date) DIV 7 as nextdiff FROM t_date as d JOIN t_date as d2 JOIN t_date as d3 WHERE d.date >= @TMP_date and DAYOFWEEK(@TMP_date) = DAYOFWEEK(d.date) = DAYOFWEEK(d2.date) = DAYOFWEEK(d3.date) and d2.date > d.date and d3.date > d2.date and DATEDIFF(d2.date, d.date) = 7 GROUP BY d.date ORDER BY d.date ASC ) as t -- these 3 lines below are the ones I need to figure out how to accomplish without an outer query -- ie, find the first (lowest date) result that has a nextdiff greater than 1 (a gap of more than 1 week between results) WHERE nextdiff >1 ORDER BY date ASC LIMIT 1 ) , CONCAT('dow: ',@dayofweek,' ',@TMP_date) ) -- , 1) -- commented this out while debugging, debug output is line above; in final, if return is null, that means offset is one week , 0 ) as clash_offset, @NEW_date:= (SELECT DATE_ADD((SELECT @TMP_date), INTERVAL @clash_offset WEEK)) as date, w.extra FROM t_weekday as w JOIN (SELECT @idxw := 0, @idxw_U := 0, @idxw_M := 0, @idxw_T := 0, @idxw_W := 0, @idxw_R := 0, @idxw_F := 0, @idxw_S := 0, @next_date := DATE_ADD((SELECT t.date FROM t as t ORDER BY t.date DESC LIMIT 1), INTERVAL 1 DAY) ) as varrs ORDER BY date DESC, weekday, id ASC @TMP_date is the starting (minimum) date. The subquery is the one inside the IFNULL, and tested outside of this, as its own query, it works perfectly, when specifying a literal value (or setting the variable in a JOIN()). As a test, I did: SELECT @next_date as next_date, @tmp_i := CASE w.weekday + 0 WHEN 0 THEN @idxw_U:=@idxw_U+1 WHEN 1 THEN @idxw_M:=@idxw_M+1 WHEN 2 THEN @idxw_T:=@idxw_T+1 WHEN 3 THEN @idxw_W:=@idxw_W+1 WHEN 4 THEN @idxw_R:=@idxw_R+1 WHEN 5 THEN @idxw_F:=@idxw_F+1 WHEN 6 THEN @idxw_S:=@idxw_S+1 END as idxW, @idxw_offset := (7 + w.weekday - dayofweek(@next_date)) mod 7 as idxw_offset, @idxw_corr := (@tmp_i *7)-7 + (7 + w.weekday - dayofweek(@next_date)) mod 7 as idxW_corr, w.weekday as weekday, @dayofweek:= w.weekday+0 as dweekday, @TMP_date:= DATE_ADD(@next_date, INTERVAL (@idxw_corr) DAY) as tmp_date, @clash_offset:= IF((SELECT 1 FROM t_date WHERE date = @TMP_date), IFNULL(:= IF((SELECT 1 FROM t_date WHERE show_after = @TMP_show_after), IFNULL( ( -- SELECT f FROM ( -- uncommenting this line and the ftmp one results in NULL, with them commented out, @TMP_date is picked up no problem SELECT @TMP_date as f LIMIT 1 -- ) as ftmp ) , CONCAT('dow: ',@dayofweek,' ',@TMP_show_after) ) -- , 1) , 0 ) as clash_offset, @NEW_date:= (SELECT DATE_ADD((SELECT @TMP_date), INTERVAL @clash_offset WEEK)) as date, w.extra FROM t_weekday as w JOIN (SELECT @idxw := 0, @idxw_U := 0, @idxw_M := 0, @idxw_T := 0, @idxw_W := 0, @idxw_R := 0, @idxw_F := 0, @idxw_S := 0, @next_date := DATE_ADD((SELECT t.date FROM t as t ORDER BY t.date DESC LIMIT 1), INTERVAL 1 DAY) ) as varrs ORDER BY date DESC, weekday, id ASC Which confirms what the docs say. However, I don't know how to modify my query (the first one) to get the result I want. ---------- Edit: I found an error in the logic, I need to return the highest result (date) in a series of *nextdiff*s if all return 1, or the first higher-than-1 result. It currently only would return the latter. So that needs fixing too.
Asked by insaner (189 rep)
Jul 9, 2016, 09:16 AM
Last activity: Jun 16, 2025, 12:04 PM