I'm trying to use a while loop in a one-off query on a MySQL (5.1.41-3ubuntu12.10-log) database:
WHILE ((SELECT COUNT(*) FROM
(SELECT id, COUNT(*) AS cnt
FROM foo
GROUP BY id
ORDER BY COUNT(*) DESC) cnts
WHERE cnt > 1) != 0) DO
BEGIN
SET #curr_id = (SELECT id FROM
(SELECT id, COUNT(*) AS cnt
FROM foo
GROUP BY id
ORDER BY COUNT(*) DESC) cnts
WHERE cnt > 1
LIMIT 1);
SET #new_id = (SELECT MAX(id) + 1
FROM foo);
UPDATE foo
SET id = #new_id
WHERE id = #curr_id
LIMIT 1;
END WHILE;
What this does is while there are multiple records with the same id, update one of them with the next id.
The syntax in the body is correct and the predicate used in the while statement also executes without any trouble on it's own. MySQL returns a syntax error on the beginning of the query:
Error Code : 1064
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 'WHILE ((SELECT count(*) FROM
(SELECT id, COUNT(*) AS cnt
FROM stock_produ' at line 1
I realize this might not be The Right Way of doing things, but this is a very badly (or rather not-at-all) thought out database, so it would be awesome if I could get it to work this way.
Thanks,
Robin
It looks as though you are trying to run this procedural code as an anonymous block. While this works in some databases (like Oracle) it can't be done in MySQL.
If you want to run this then put it in a stored procedure and then call the procedure. Hence:
Create procedure
DELIMITER $$
CREATE PROCEDURE `foo_update_routine`()
BEGIN
WHILE ((SELECT COUNT(*) FROM
(SELECT id, COUNT(*) AS cnt
FROM foo
GROUP BY id
ORDER BY COUNT(*) DESC
) cnts
WHERE cnt > 1) != 0)
DO
SET #curr_id = (SELECT id FROM
(SELECT id, COUNT(*) AS cnt
FROM foo
GROUP BY id
ORDER BY COUNT(*) DESC
) cnts
WHERE cnt > 1
LIMIT 1);
SET #new_id = (SELECT MAX(id) + 1 FROM foo);
UPDATE foo SET id = #new_id
WHERE id = #curr_id
LIMIT 1;
END WHILE;
END $$
Call Procedure
CALL `foo_update_routine`;
PS You might want to investigate the HAVING clause for your select statements...
Related
I use this code in MySQL to order by 'anotherColumn' and then get the row number of 'myColumn' and then I perform a calculation and set 'myColumn' to the result:
SET #c = (SELECT COUNT(*) FROM myTable); SET #rownum = 0; UPDATE myTable SET myColumn = #c * (#rownum:= 1 + #rownum) ORDER BY anotherColumn DESC LIMIT 100000;
I'm trying to achieve the same thing in Postgresql but am getting a lot of errors. I have:
SET c = (SELECT COUNT(*) FROM myTable); SET rownum = 0; UPDATE myTable SET myColumn = c * (rownum:= 1 + rownum) ORDER BY anotherColumn DESC LIMIT 100000;
.. but it gives me an error at the first parenthesis. If I remove those parenthesis like this:
SET c = SELECT COUNT(*) FROM myTable; SET rownum = 0; UPDATE myTable SET myColumn = c * (rownum:= 1 + rownum) ORDER BY anotherColumn DESC LIMIT 100000;
.. then it gives me an error at the SELECT. If I just set c to equal 0, I get an error way down at the ORDER. Does anyone know how to convert my code from MySQL to PostgreSQL?
This "pattern" in MySQL is typically used to work around the absence of window function.
You don't need variables in Postgres to achieve something like that:
update my_table
set my_column = t.cnt + t.rn
from (
select pk_column,
(select count(*) from my_table) as cnt,
row_number() over (order by another_column) as rn
from my_table
limit 100000
) t
where t.pk_column = my_table.pk_column;
Where pk_column is the primary key column of your table. If you have more than one PK column, you need to use all of them.
For my homework assignment, the stored procedure should accept an optional integer between 1 and 15, but default to 3 if no value is passed.
DELIMITER //
CREATE OR REPLACE PROCEDURE rankVideos(rank INT)
BEGIN
if rank = null then
SET rank = 3;
END if;
CREATE OR REPLACE TEMPORARY TABLE all_ranks AS (
SELECT * FROM youtube.homework7a
);
create OR REPLACE TEMPORARY TABLE t2 AS ( SELECT
category,
row_number() OVER (ORDER BY cnt DESC) v_cnt,
row_number() OVER (ORDER BY views DESC) v_views,
row_number() OVER (ORDER BY likes DESC) v_likes,
row_number() OVER (ORDER BY dislikes DESC) v_dislikes,
row_number() OVER (ORDER BY comment_count DESC) v_comment_count FROM all_ranks
);
CREATE OR REPLACE TEMPORARY TABLE t3 AS (
SELECT * FROM t2
WHERE v_cnt <= rank OR v_views <= rank OR v_likes <= rank
OR v_dislikes < rank OR v_comment_count <= rank
);
CREATE OR replace TEMPORARY TABLE t4 AS (
SELECT category,
case when v_cnt <= rank then v_cnt ELSE null END cnt,
case when v_views <= rank then v_views ELSE null END views,
case when v_likes <= rank then v_likes ELSE null END likes,
case when v_dislikes <= rank then v_dislikes ELSE null END dislikes,
case when v_comment_count <= rank then v_comment_count ELSE null END comment_count
FROM t3
)
;
SELECT *,
ifnull(cnt,999)
+ ifnull(views,999)
+ ifnull(likes,999)
+ ifnull(dislikes,999)
+ifnull(comment_count,999) num_non_null_cols,
ifnull(cnt,0)
+ ifnull(views,0)
+ ifnull(likes,0)
+ ifnull(dislikes,0)
+ ifnull(comment_count,0) sum_non_null_cols
FROM t4
ORDER BY num_non_null_cols, sum_non_null_cols;
END
//
DELIMITER ;
When I run the procedure and leave the integer blank I get an error that it has an incorrect integer value.
The syntax you show makes me think you are using MySQL or MariaDB.
These implementations don't support a feature for default values for procedure parameters. This has been requested in MySQL: https://bugs.mysql.com/bug.php?id=15975 But so far, it is not supported.
You're using the best workaround I know of, to set the parameter to your default value if it is NULL.
Another way of coding this is to use the COALESCE() function:
SET rank = COALESCE(rank, 3);
It's just another way to achieve the same thing that your IF/THEN code does.
SQL
MSSQL
create proc MyProc
#rank int = 3
as
...
GO
If you pass in a value, it will use that value. If you don't pass in a value, #rank = 3.
i need help for the below code. My problem is that i want only that code executes once per statement (after i search i checked that expression don't exists anymore only once per row).
So i tried to add:
IF NOT EXISTS
(Select count(*) FROM replay_replays_access WHERE id_game = new.id_game GROUP BY id_game HAVING count(*) <5)
THEN
But didn't work either what can i do, its duplicating sometime triplicating the information?
TRIGGER replay
AFTER UPDATE
ON table_replays FOR EACH ROW
begin
IF EXISTS
(SELECT
replay_games.room_name
FROM replay_games
WHERE replay_games.room_name = 'Tournament Room' and replay_games.id = new.id_game)
THEN
IF NOT EXISTS
(Select
count(*)
FROM replay_replays_access
WHERE id_game = new.id_game
GROUP BY id_game
HAVING count(*) <5)
THEN
INSERT INTO replay_replays_access(id_game, id_player, replay_name, do_not_hide)
SELECT
new.id_game,
replay_users.id ,
CONCAT(
(SELECT game_types
FROM replay_games
WHERE id=new.id_game),
': ',
(SELECT
descr
FROM replay_games
WHERE id=new.id_game)) ,
0
FROM replay_users
WHERE
(replay_users.admin > 0 OR
replay_users.privlevel = 'TOURNAMENT MEMBER')
AND NOT replay_users.name = (
SELECT
replay_games.creator_name
FROM replay_games
WHERE replay_games.id = new.id_game);
END IF;
END IF;
END
I have this DB2 query which I want to make MySQL compliant :
UPDATE
(
SELECT x.name, row_number() over () as rown from XYZ x where x.id = '123' and
x.div='abc')A
SET
A.name = 'name_1'
where
A.rown<= ( select count(*) -1 from XYZ where id='123' and div='abc');
Now, I tried writing this I MySQL:
UPDATE
(
select x.name, (#row_number := #row_number +1) as rown
from XYZ x, (Select #row_number := 0)as t
where x.id='123' and x.div='abc'
) A
Set
A.name = 'name_1'
where
A.rown<= ( select count(*) -1 from XYZ where id='123' and div='abc');
However, it gives me the error: The target table A of the UPDATE is not updatable
I have tried multiple ways but all in vain. Where am I going wrong?
Also if the DB2 query can be made into MySql in any other way, since Mysql doesn't support
row_number()
You can't update a derived table. You need to join with the real table so you can update it.
UPDATE XYZ AS x
JOIN (
select x.id, (#row_number := #row_number +1) as rown
from XYZ x, (Select #row_number := 0) as t
where x.id='123' and x.div='abc'
) AS A ON x.id = A.id
Set X.name = 'name_1'
where A.rown <= ( select count(*) from XYZ where id='123' and div='abc');
I'm not sure if this will do the same thing as the DB2 query, though. It seems to assume some inherent ordering in the table, and perhaps DB2 provides such a thing, but MySQL doesn't make any guarantees about ordering when you don't use ORDER BY. If you add ORDER BY x.id in the subquery, maybe that will do what you want.
In DB2 you can do it :
update XYZ f1
set f1.name='name_1'
where f1.id='123' and f1.div='abc'
and rrn(f1) not in
(
select max(rrn(f2)) from XYZ f2 where f2.id='123' and f2.div='abc'
)
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"