SQL | insert into + select + variables with condition? - mysql

My task is inserting 3 random datas per ID from another table
and I got a mistake with syntax
set #num := 0, #type := '' ,#stat :='';
INSERT INTO random
as
(
SELECT
*
FROM (
select userID,userNAME, chaID, chaNAME,goal,gender,
#num := if(#type = userID, #num +1,1) as row_number,
#type := userID as dummy,
#stat as status
from userchar
order by userID
) as x where x.row_number <= 3)
I'm going to put this code in event scheduler to insert the new datas in daily
1064 - You have an error in your SQL syntax; check the manual that
corresponds to your MariaDB server version for the right syntax to use
near 'INSERT INTO random as ( SELECT * FROM ( select userID,userNAME,
chaID, c' at line 2
thank you so much for every suggestions.

I suspect the problem is trying to run multiple statements at the same time. You can fix this by initializing the variables in the query itself:
INSERT INTO random( . . . )
select u.*
from (select userID, userNAME, chaID, chaNAME, goal, gender,
(#num := if(#u = userID, #num +1,
if(#u := userId, 1, 1)
)
) as row_number,
userID as dummy,
#stat as status
from userchar u cross join
(select #u = '', #num := 0, #stat := '') params
order by userID, rand()
) u
where u.row_number <= 3;
There are several other issues:
When using insert, always list the columns. This is particularly important if you are learning SQL, so you learn good habits.
You should not assign a variable value in one expression and use it in another. MySQL (and MariaDB) do not guarantee the order of evaluation of expressions in a select, so the expressions can be evaluated in either order.
If you want random rows, then use rand(). There is a difference between "indeterminate" and "random".

Related

A view with internal variables

I have a query where I want to fetch the top x for each value of another column. For doing this I have the following query
SELECT *,
(#num := IF(#job = job_id, #num + 1, IF(#job := job_id, 1, 1))) row_number
FROM job_user_rank t
CROSS JOIN (SELECT #num := 0, #job := null) c
where is_match = 1
ORDER BY job_id, is_match DESC, rank DESC
I then wrap this query and add a where row_number <= ?, however I want to make that inner query into a view instead, but I get the following error [HY000][1351] View's SELECT contains a variable or parameter, how can I get around that?

MariaDB/MySQL RANK() implementation

I am working on migration from MS SQL Server to MariaDB 10.0. I have query which use RANK() PARTITION BY. I already created some kind of RANK() implementation on my table but it's not working properly.
The original query was:
RANK() OVER (PARTITION BY visits.id_partner ORDER BY visits.updated_at DESC) AS rank
My implementation for MariaDB/MySQL:
SELECT
...,
(
CASE visits.id_partner
WHEN #currId THEN
#curRow := #curRow + 1
ELSE
#curRow := 1 AND #currId := visits.id_partner
END
) AS rank
FROM
records rec
JOIN visits ON visits.id = rec.id_visit,
(
SELECT
#curRank := 0,
#currId := NULL
) r
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
I want to select row ranked by id_partner order by updated_at field. When id_partner is same as on row before RANK should increase by 1. When is different than before, it should reset to 1.
But my query is not working at all. I have still rank 1 on all rows. Can you help me find mistake?
Thank you for help!
It is tricky to use variables in MySQL/MariaDB. A variable should only be used and assigned in one statement (as you do correctly). However, AND can short-circuit variable assignment.
I use a construct like this for ROW_NUMBER(). RANK() is actually a bit of a pain . . . DENSE_RANK() and ROW_NUMBER() are simpler. However, this seems to be the code that you are aiming for:
SELECT ...,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM records rec JOIN
visits
ON visits.id = rec.id_visit CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC;
EDIT:
In MySQL (and presumably in MariaDB), sometimes variables don't work quite right unless you use a subquery. So, try this:
SELECT . . .,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM (SELECT ...
FROM records rec JOIN
visits
ON visits.id = rec.id_visit
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
) t CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params;

How to get row number in mysql?

I'm making an tracking app, and i want to show the user on which place he/she is. For this I thaught of ordering by 'totalkm' desc and then get the row number. The problem is i don`t know how to do this, as i'm fairly new to the Database world.
I tried something like this:
WITH mytable AS {
SET #row_number = 0;
SELECT (#row_number := #row_number +1) AS num, user,totalkm
FROM profile ORDER BY totalkm DESC ; }
SELECT num
FROM mytable WHERE user = "bogdan9832";
But i get the error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'mytable AS {
SET #row_number = 0' at line 1
From what i understood, there is no support for WITH in mysql. Can someone show me an alternative?
You have to use following query for the same:-
First declare the variable as:-
SET #row_number = 0;
Then use this query:-
SELECT (#row_number:= #row_number + 1) AS ROW_NUMBER, OTHER_COLS
FROM YOUR_TABLE;
SELECT num, user, totalkm
FROM (
SELECT (
#row_number := #row_number +1
) AS num, user, totalkm
FROM profile
ORDER BY totalkm DESC
) AS a
WHERE user = "bogdan9832"
Using sub queries but only a single SQL statement:-
SELECT num
FROM
(
SELECT (#row_number := #row_number +1) AS num, user, totalkm
FROM
(
SELECT banner.*
FROM profile
ORDER BY totalkm DESC
) profile
CROSS JOIN (SELECT #row_number := 0) sub0
) sub0
WHERE user = "bogdan9832"

what does `(SELECT #rn :=0) var_init` do?

I have seen some MySql queries using
(SELECT #rn :=0, #ct := NULL ) var_init
i don't know what it does .. i have searched for it for a long time and still i don't have an answer. any help is much appreciated .
it will be much appriaced if some one could explain this query to me ...
SELECT * FROM (
SELECT c. * , #rn := IF( `type` != #ct , 1, #rn +1 ) AS rownumber, #ct := `type` FROM jb_company c ,
(SELECT #rn :=0, #ct := NULL ) var_init ORDER BY `type`
) c
WHERE rownumber <=20
I am using the above query to fetch limited number of rows( i.e 20) of each type in from the table ( see the link below..for the question where i needed this )
Mysql query to fetch limited rows of each type
but i am still not getting the query.. some please help
Thanks in advance.
It plainly initializes the values (#rn :=0, #ct := NULL), which results in an alias var_init containing one row, and joins the rest on it (so, having no effect on the rows themselves other then setting up the variables in the beginning).
This is often used to avoid needing multiple statements to set up the variables. That single query is equal to:
SET #rn :=0;
SET #ct := NULL;
SELECT * FROM (
SELECT c. * ,
#rn := IF( `type` != #ct , 1, #rn +1 ) AS rownumber,
#ct := `type`
FROM jb_company c
ORDER BY `type`
) c
WHERE rownumber <=20
.. which is multiple statements, so usually used due to API limitations of the using code, or to make sure the variables start out as they should be on shared connections.
This is an expression in MySQL used to define variables that might be used in the expression.
By default, MySQL defaults variables to strings. So, to get a numeric variable, you want to assign the variable to a number the first time it is seen. This can be done in a set statement. However, a "single" query would then consist of multiple statements.
Often, variables in MySQL are used to approximate the window/analytic functions available in most other databases. Other databases do not encourage the use of such variables in queries (although they are typically allowed and can be useful under some -- more limited -- circumstances).
The query that you mention would be expressed as the following in most databases:
SELECT *
FROM (SELECT c.* ,
row_number() over (partition by `type` order by `type`) as rownumber
FROM jb_company c
) c
WHERE rownumber <= 20;
The way the MySQL version works is by creating a derived table and using the variables to add rows with the appropriate values.

rails using sql variables in find_by_sql

I have this query:
SET #current_group = NULL;
SET #current_count = 0;
SELECT user_id, MIN( created_at ) as created_at, CASE WHEN #current_group = user_id THEN #current_count WHEN #current_group := user_id THEN #current_count := #current_count + 1 END AS c
FROM notifies
G ROUP BY user_id, c
ORDER BY id desc LIMIT 0 , 10
If i launch it it works
but if i put it in a find_by_sql method like:
Notify.find_by_sql("SET #current_group = NULL; SET #current_count = 0; SELECT user_id, MIN( created_at ) as created_at, CASE WHEN #current_group = user_id THEN #current_count WHEN #current_group := user_id THEN #current_count := #current_count + 1 END AS c FROM notifies GROUP BY user_id, c ORDER BY id desc LIMIT 0 , 10")
It returns this error:
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET #current_count = 0; SELECT user_id, MIN( created_at ) as created_at, CASE WH' at line 1:
How can i do?
thanks
It's because find_by_sql only works with a single statement.
Setting variables happen in separate statements. set specifically is in the Database Administration section, number 12.4.4, and select is in Data Manipulation section, number 12.2.7. Consoles (usually) allow multiple statements, and keep the variables around, but ActiveRecord queries do not.
To allow multiple statements, I think you have to maintain a persistent connection with the database, which Rails doesn't do (edit: normally). But I'm not certain about that - if anyone else knows, I'd love a more definite reason.
Edit: actually, I have a solution for you. Try this:
items = YourModel.transaction do
YourModel.connection.execute("SET #current_group = NULL;")
YourModel.connection.execute("SET #current_count = 0;")
# this is returned, because it's the last line in the block
YourModel.find_by_sql(%Q|
SELECT user_id, MIN( created_at ) as created_at, CASE WHEN #current_group = user_id THEN #current_count WHEN #current_group := user_id THEN #current_count := #current_count + 1 END AS c
FROM notifies
GROUP BY user_id, c
ORDER BY id desc LIMIT 0 , 10
|)
end
All those run in a single transaction, and any variables / settings inside the transaction block will persist between queries. You're still bound to a single statement per query though. There might be a way to do it without an actual transaction wrapping the whole set, but I haven't looked for it - most likely you want one, or you now have a very specific thing to look for if you know you don't.
Enjoy!
Accordingly to the API it is this syntax:
Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
So you might want to try putting it into an array instead of using parenthesis.
Notify.find_by_sql ["SET #current_group = NULL; SET #current_count = 0; SELECT user_id, MIN( created_at ) as created_at, CASE WHEN #current_group = user_id THEN #current_count WHEN #current_group := user_id THEN #current_count := #current_count + 1 END AS c FROM notifies GROUP BY user_id, c ORDER BY id desc LIMIT 0 , 10"]