MySQL query with assignment operators and variables works with MySQL 5.7 but not in MySQL 8 - mysql

I have a query, that works with rows having 4 hierachies in one table. Now i query the table with the ID of the lowest level and get as result 4 rows of the parents and the child.
Here is a sample from the DB:
id
title
parent
type
1
Germany
0
1
2
Bavaria
1
2
3
Swabia
2
3
4
Augsburg
3
4
This works all the time in mysql 5.7 like this:
SELECT id, type, title FROM ( SELECT #r AS _id, (SELECT #r := parent FROM category WHERE id = _id) AS parent, #l := #l + 1 AS lvl
FROM (SELECT #r := 4, #l := 0) vars, category h WHERE #r <> 0) T1
JOIN category T2 ON T1._id = T2.id
ORDER BY T1.lvl DESC
Result in MySql 5.7:
id
type
title
1
1
Germany
2
2
Bavaria
3
3
Swabia
4
4
Augsburg
Under Mysql 8 i don't get an error but just no result.
Additional Info
According to the documentation, under both MySQL 5.7 and MySQL8.0, incrementing a variable itself is not guaranteed.
SET #a = #a + 1"; With other statements, such as SELECT, you might get
the expected results, but it's not guaranteed. With the following
statement, you might think that MySQL first evaluates #a and then
makes an assignment ...
I suspect that this is why the query does not work: https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
Additional Info 2 (Explanation)
Beginning with MySQL 8.0.22, a reference to a user variable in a
prepared statement has its type determined when the statement is first
prepared, and retains this type each time the statement is executed
thereafter. Similarly, the type of a user variable employed in a
statement within a stored procedure is determined the first time the
stored procedure is invoked, and retains this type with each
subsequent invocation.
Possibly this would be an alternative in MySql 8 but i need a query that works in both:
with recursive parent_cats (id, parent, title, type) AS (
SELECT id, parent, title, type
FROM category
WHERE id = 4
union all
SELECT t.id, t.parent, t.title, t.type
FROM category t INNER JOIN parent_cats pc
ON t.id = pc.parent
)
SELECT * FROM parent_cats;

Related

MYSQL Update column with Sequential numeric value IF NULL - Continued Sequence

I am attempting to keep a running sequence number for a table.
I will run the query on schedule each night to update a table and populate the sequential number based on the last number +1 and continued.
My Table: mis_order_charges
ID item_id SEQ
1 20 1
5 15 2
6 21 3
9 20 NULL
12 21 NULL
So I would like to run an update query to populate the NULL with 4 and 5, and of course continue this each run.
I have tried a few things:
insert into misc_order_charges (seq)
SELECT #row := #row + 1 as row
FROM misc_order_charges moc, (SELECT #row := 0) r where seq is null;
Didn't work
UPDATE misc_order_charges
JOIN (SELECT #rank := 0) r
SET seq=#rank:=#rank+1 where misc_order_charges.seq is null;
The above worked but of course reset the counted, so the NULLs got 1 & 2
Any help is greatly appreciated!
You can get the max value using MAX() function to use as base for rank variable.
i.e.
UPDATE misc_order_charges
JOIN (SELECT #rank := (SELECT MAX(T.SEQ) FROM misc_order_charges as T)) r
SET seq=#rank:=#rank+1 where misc_order_charges.seq is null;
This will get the first rank as whatever highest SEQ number is in your table.

MYSQL count distinct datas depends on if condition

I have really different problem about database query. There is a little bit different scenarios:
I have a table created with 3 columns. They have ID, ItemId, TypeId columns. I need a count query, it should count ItemId and TypeId together but except duplicate columns. For example;
Id ItemId TypeId
-- ------ ------
1 1 1 -> count +1
2 1 1 -> ignore
3 1 2 -> count -1
4 1 2 -> ignore
5 1 1 -> count +1
result count = 1
In the end, if distinct row repeated, count ignore that row. But TypeId data changed for one specific Item it should increase or decrease count. TypeId equals to 1 count +=1, equals to 2 count -=1.
In MySQL, you would seemingly use count(distinct):
select count(distinct itemId, typeId)
from t;
However, you really have a gaps-and-islands problem. You are looking at the ordering to see where things change.
If I trust that the id has no gaps, you can do:
select count(*)
from t left join
t tprev
on t.id = tprev.id + 1
where not ((t.itemId, t.typeid) <=> (tprev.itemId, t.prev.id))
Try the following query. This employs User-defined session variables. It will work in all the cases (including gaps in Id):
SELECT
SUM(dt.factor) AS total_count
FROM
( SELECT
#factor := IF(#item = ItemId AND
#type = TypeId,
0,
IF(TypeID = 2, -1, 1)
) AS factor,
#item := ItemId,
#type := TypeId
FROM your_table
CROSS JOIN (SELECT #item := 0,
#type := 0,
#factor := 0) AS user_init_vars
ORDER BY Id
) AS dt
DB Fiddle DEMO

MySQL: how to select the Nth value of each group with GROUP BY

I want to select the 2nd response column value of each new_threads group, with a zero as the value if it is a group of 1 row.
new_treads|response
------------------
1 | 0
1 | 1
2 | 0
2 | 0
2 | 1
... | ...
9 | 0
9 | 1
9 | 0
10 | 0
The output being:
new_treads|response
------------------
1 | 1
2 | 0
... | ...
9 | 1
10 | 0
So far, I understand how to get the first with MIN, but I need the 2nd
SELECT
thread,
min(response)
FROM messages
GROUP BY thread;
I would like to use GROUP BY because I'm using GROUP BY for other SELECTs as well
Thanks!
Since the rows are not "numbered", you need to create a number for each group and then select it. I'd do that with user variables:
select thread, response
from (
select #n := (case
when m.thread = #prev_thread then #n
else 0
end) + 1 as n -- If the current thread is the same as the
-- previous row, then increase the counter,
-- else, reset it
, #prev_thread := m.thread as thread -- Update the value of
-- #prev_thread
, m.response
from
(select #n := 0, #prev_thread := 0) as init
-- The 'init' subquery initializes the
-- temp variables:
-- #n is a counter
-- #prev_thread is an identifier for the
-- previous thread id
, messages as m
order by m.thread -- You need to add a second column to order
-- each response (such as "response_id", or
-- something like that), otherwise the returned
-- row may be a random one
) as a
where n = 2; -- Select the rows from the subquery where the counter equals 2
The above works quite fine to find the 2nd row of each group, but only if there's one. So now: how to get a NULL value if there isn't a second row?
The easiest way to do this would be to use a left join:
select t.thread, b.response
from (select distinct thread from messages) as t
left join (
-- Put the above query here
) as b on t.thread = b.thread;
SELECT
thread,
min(response)
FROM messages
GROUP BY thread
HAVING response > min(response)
try this just want to know if it works
I would like to elaborate on the answer above. While it worked well for me, it took some time to piece together the context and generalize it so I could apply it to my code. I hope that this answer will better generalize what is laid out above...
SELECT *
FROM (SELECT distinct keyField --- keyField is the field the query is grouping by
FROM TABLE
-- Add keyField Constraint --
-- Add non-keyField Constraint --
INNER JOIN (SELECT *,
#n:=(CASE -- Iterating through...
WHEN keyField = #prev_keyField -- When keyField value == previous keyField value
THEN #n:=#n+1 -- Define n as the row in the group
ELSE 1 -- When keyField value != previous keyField value, then n is the 1st row in the group
END) as n,
#prev_keyField:= keyField -- Define previous keyField value for the next iteration
FROM (SELECT #n:=0,#prev_keyField:=0) r,TABLE as p
-- Add non-keyField Constraint--
ORDER BY keyField,sortField DESC -- Order by keyField and the field you are sorting by
-- ei. keyField could be `thread`,
-- and sort field could be `timestamp` if you are sorting by time
) s ON s.keyField = p.keyField
WHERE s.n = 2 -- Define which row in the group you want in the query

row number and distinct together

Given table "table1"
name
------
Jhon
Jhon
Robert
Robert
Robert
Needed get unique names and also numeric names as queue,
that is result bust be:
1 Jhon
2 Robert
this query not works
SET #n = 0;
SELECT #n := #n + 1 AS n, DISTINCT name FROM table1
I dont ask how to make this, (this may be make with sub-querys right?) , my question is: why dont working query, which I writed upstairs? why dont like mysql this query?
SELECT #rownum := #rownum + 1 AS row_number,
table1.name
FROM (SELECT DISTINCT name
FROM sparkles) table1
JOIN (SELECT #rownum := 0) r
Will produce:
| ROW_NUMBER | NAME |
-----------------------
| 1 | John |
| 2 | Robert |
See it in action
Why MySQL doesn't like this query:
SELECT #n := #n + 1 AS n, DISTINCT name FROM table1
This query is not correct because SQL's DISTINCT query modifier applies to the whole row, not just to a single column or subset of columns. This is a common misunderstanding made by SQL programmers.
In other words, the result is reduced by DISTINCT only if all columns are the same as another row.
Syntactically, the DISTINCT keyword must follow SELECT. It's not correct to put it after other columns.
You could write the following:
SELECT DISTINCT #n := #n + 1 AS n, name FROM table1
But this query wouldn't get you what you wanted. It would apply the incrementing variable to every row of table1, and then apply DISTINCT to the whole result. Since every row is guaranteed to have a distinct value n, the DISTINCT would have no effect.
Other answers have described doing the DISTINCT inside a derived table (subquery), so that it forces the rows to be reduced based only on the distinct values of name, and then apply the incrementing variable to the resulting rows.
It is not exactly clear what you are asking but, do you mean something like this:
select name, #rn:=#rn+1 n
from
(
select distinct name
from table1
) t1, (SELECT #rn:=0) r
order by name
See SQL Fiddle with Demo
Results:
| NAME | N |
--------------
| Jhon | 1 |
| Robert | 2 |
I also had same kind of problem when i explored that there is no such function like Row Number() as it is in MS SQL then i tried following trick and it worked for me
SELECT #n := #n + 1 AS n, name from (select DISTINCT name FROM table1) tb;

mySQL create a result set of two table alternating rows

I have a query that uses union to join two sub queries e.g.
SELECT * FROM posts WHERE postTypeId=1 (e.g. blog)
UNION
SELECT * FROM posts WHERE postTypeId=2 (e.g. news)
The result set that this approach generates positions the two sub-sets sequentially ("blog" then "news").
I want to create a a result set which interleaves the two, alternating between rows from the "blog" sub-set and the "news" sub-set.
I feel that there must be a simple way to do this, but I have failed to find it.
Any suggestions would be greatly appreciated.
Nick
This is solution that best works for me. It's not identical to any of the current proposals, so I have added it independently. #a, #b and #c are used to create row numbers per sub-set, meaning that in the combined results, 3 rows will share the same row number (a "row set"). This is used as the first order sort, and second order sort then orders the rows within the "row set".
SET #a = 0;
SET #b = 0;
SET #c = 0;
SELECT * FROM(
SELECT #a := #a + 1 AS sortOne, 1 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=3
UNION
SELECT #b := #b + 1 AS sortOne, 2 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=2
UNION
SELECT #c := #c + 1 AS sortOne, 3 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=1
) AS result ORDER BY sortOne, sortTwo
This solutions is derived/inspired by submitted solutions, but I don't think it appropriate to mark any of them as being an accepted solution in itself. So, credit where it's due to Thomas Clayson, Tony Hopkinson and rmunoz whose answers I've voted up. Cheers!
Well, here is a novel way I can think of (not tested, but you'll get the gist):
SELECT * FROM (
SELECT 1 AS query, #n := #n + 1 AS rowNumber, posts.* FROM (select #n:=0), posts WHERE posts.postTypeId=1
UNION
SELECT 2 AS query, #n := #n + 1 AS rowNumber, posts.* FROM (select #n:=0), posts WHERE posts.postTypeId=2
) ORDER BY rowNumber, query;
So this will do the two queries and then order by first rowNumber and then by query. What you'll end up with is something like:
rowNumber | query
1 | 1
1 | 2
2 | 1
2 | 2
etc...
SELECT #n=:0 resets the global variable n to 0 for the query and then the #n := #n + 1 increments the value for each row.
If you need any more explanation let me know. I hope this works! :)
That's easy, you can add a parameter in your subquery, the parameter "rank", this way:
SET #rank=0;
SELECT #rank:=#rank+2 AS rank FROM posts WHERE postTypeId=1 (e.g. blog)
Then, you get:
0, ...
2, ...
If you do the same in the other query but init initializating rank to 1, you will get:
1, ...
3, ...
Finally "ORDER BY rank" and you will get posts and news mixed.
Try
Set #current_row = 0
SELECT * FROM (
SELECT #current_row := #current_row + 1 as Position, posts.*
FROM posts
WHERE postTypeId=1
UNION
SELECT #current_row, posts.*
FROM posts
WHERE postTypeId=2
) dummyTableName
ORDER BY position, postTypeId
maybe