How could I simplify this mysql statement? - mysql

I use follow mysql statement to get some info from mysql via php.
( SELECT *
FROM mytable
WHERE qid NOT IN ({$used['used']})
AND level = 1
ORDER BY RAND()
LIMIT 5)
UNION
( SELECT *
FROM app_mytable _qt
WHERE qid NOT IN ({$used['used']})
AND level = 2
ORDER BY RAND()
LIMIT 5)
UNION
( SELECT *
FROM app_mytable _qt
WHERE qid NOT IN ({$used['used']})
AND level = 3
ORDER BY RAND()
LIMIT 5)
UNION
( SELECT *
FROM app_mytable _qt
WHERE qid NOT IN ({$used['used']})
AND level = 4
ORDER BY RAND()
LIMIT 5)
$used['used'] is a set of qid that likes 23,31,653,147,146,134,6.....
How could I simplify this mysql statement?

You can use user variables to keep running totals by groups. This is untested but something like the following should work:
select *,
#running:=#previous:=NULL
from (
select *,
#running:=if(#previous=inside.level,#running,0)+1 as TOTAL,
#previous:=inside.level
from (
select *
from mytable
where qid NOT IN ({$used['used']})
order by level, rand()
) as inside
)as outside
where TOTAL < 5;

Some dynamic sql can eliminate the copy pasting for ya... that way you can easily extend this out for as many levels as you like, just mod the max_level.
DECLARE current_level INT; SET current_level = 1;
DECLARE max_level INT; SET max_level = 4;
DECLARE full_sql VARCHAR(1000); SET full_sql = '';
DECLARE base_sql VARCHAR(1000); SET base_sql = '(SELECT * FROM mytable WHERE qid NOT IN ({$used['used']}) AND level = ? ORDER BY RAND() limit 5) ';
WHILE current_level <= max_level
SET full_sql = CONCAT(full_sql, REPLACE(base_sql, '?', current_level));
IF current_level < max_level THEN
SET full_sql = CONCAT(full_sql, ' UNION ');
END IF
SET current_level = current_level + 1;
END WHILE
PREPARE s1 FROM full_sql;
EXECUTE s1;
DEALLOCATE PREPARE s1;

Related

Convert MySQL syntax to PostgreSQL

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.

Stored procedure, using variable no result

The variable #total in DESC LIMIT ? isn't working. If I manually set DESC LIMIT 3 then the sp runs fine and returns rows. I also tried placing the SET total in between PREPARE.
Here is the stored procedure:
DELIMITER //
CREATE PROCEDURE getTotalNET()
BEGIN
DECLARE total INT;
SET total := (SELECT COUNT(*) as item_count FROM items WHERE hostid = '12345' and key_ LIKE '%_net%' AND STATUS = '0' );
PREPARE STMT FROM
" SELECT DISTINCT itemid, clock, VALUE, ns FROM history_uint WHERE itemid IN (SELECT itemid FROM items WHERE hostid = '12345' and key_ LIKE '%_net%' AND STATUS = '0' ) AND clock >= UNIX_TIMESTAMP(NOW() - INTERVAL 120 SECOND) ORDER BY clock DESC LIMIT ?";
EXECUTE STMT USING #total ;
END //
DELIMITER ;

Relation between rows ranking

I have a table that looks like this:
If there is any relationship between the imported and section then they should be grouped together. Depending on the relationship they should be grouped as 1,2,3,4,....
I tried a query that looks like this:
select sec.section,sec.id, sec.imported, sec.id,
case when imp.imported = sec.section or imp.imported is null then 1 ELSE
2 end as rn
from
( select section, id, imported from temp1) sec
left outer join
(
select imported, Section from temp1
) imp on imp.imported = sec.section
But in this scenario my rn is always 1. Can you help me review this query?
I'm not sure how to go around this. Do we need to use a while loop and do it or can this be done using a query?
Example creation script:
create table temp1 (
id int, imported int, section int, rn int, checked int default 0
);
insert into temp1(id, section, rn) values (204, 718, 0);
insert into temp1(id, imported, section, rn) values (997,718,034,0);
insert into temp1(id, imported, section, rn) values (998,034,055,0);
insert into temp1(id, imported, section, rn) values (111,453,234,0);
insert into temp1(id, section, rn) values (908, 453,0);
insert into temp1(id, imported, section, rn) values (231,234,890,0);
insert into temp1(id, section, rn) values (342, 567,0);
My End Result should look like:
I'have tried with while loop too creating a stored procedure:
DROP PROCEDURE IF EXISTS sp_recursiveimport;
Delimiter $$
CREATE PROCEDURE sp_recursiveimport() -- (IN rnX integer)
BEGIN
DECLARE n INT DEFAULT 0; DECLARE i,j,k INT DEFAULT 0; SELECT COUNT(*) FROM temp1 INTO n;
SET i=0; set #rn = 1; -- set #k = 0;
WHILE i<n DO
set j = 0; select i;
set #sec = (select ifnull(section,0) FROM temp1 LIMIT i,1);
set #imp = (select ifnull(imported,0) FROM temp1 LIMIT i,1); select #imp, #sec;
update1: while j<n do select j;
-- if j=0 then
if (select ifnull(imported,0) from temp1 limit j,1) = #sec and (select checked from temp1 limit j,1) = 0 then
set #update = concat('update temp1 set rn = 1, checked = 1 where imported = ',#sec); select #update; PREPARE stmt_name FROM #update; EXECUTE Stmt_name; DEALLOCATE prepare stmt_name;
set #update1 = concat('update temp1 set rn = 1, checked = 1 where section = ',#sec); select #update1; PREPARE stmt_name FROM #update1; EXECUTE Stmt_name; DEALLOCATE prepare stmt_name;
set k = j;
end if;
if (select ifnull(section,0) from temp1 limit j,1) = #imp and (select checked from temp1 limit j,1) = 0 then
set #update3 = concat('update temp1 set rn = 1, checked = 1 where section = ',#imp); select #update3; PREPARE stmt_name FROM #update3; EXECUTE Stmt_name; DEALLOCATE prepare stmt_name;
set #update4 = concat('update temp1 set rn = 1, checked = 1 where imported = ',#imp); select #update4; PREPARE stmt_name FROM #update4; EXECUTE Stmt_name; DEALLOCATE prepare stmt_name;
set k = j;
end if;
-- set #sec = (select ifnull(imported,0) from temp1 limit k,1);
-- set #imp = (select ifnull(section,0) from temp1 limit k,1); select #sec, #imp;
set j= j+1;
end while update1;
set i = i + 1;
END WHILE;
END;
$$
delimiter;
Not sure why its not working.
This is not an answer to the question since, honestly, I don't know how to write this query in MySQL 5.x.
Anyway, at least I wanted to document the answer using recursive CTEs, available on MySQL 8.0 or newer. Here it is:
with recursive sect as (
select id, imported, section, row_number() over() as rn
from temp1 where imported is null
union all
select t.id, t.imported, t.section, s.rn
from temp1 t
join sect s on t.imported = s.section
)
select * from sect order by rn;
Result:
id imported section rn
--- -------- ------- --
998 34 55 1
204 <null> 718 1
997 718 34 1
231 234 890 2
908 <null> 453 2
111 453 234 2
342 <null> 567 3

save result of UNION query in local variable in MYSQL stored procedure

In my MySQL Stoared Procedure I want to store result of bellow query into local variables.
MySQL SP
BEGIN
Declare temp_ID bigint;
Declare temp_teamName Text;
(select ID,team1 from tbl_tournament_matches where leveID = 1 and tournamentID = 91 and matchType = 'L')
UNION
(select ID,team1 from tbl_tournament_matches where leveID = 2 and tournamentID = 91 and looserTeam is not null)
ORDER BY RAND() LIMIT 0,1;
select temp_ID, temp_teamName;
END;
How can I pass result of query into local variable?
note : above SP will return only 1 row.
You can achieve this without having to store the value into a variable.
SELECT * FROM(
select ID,team1 from tbl_tournament_matches where leveID = 1 and tournamentID = 91 and matchType = 'L'
UNION
select ID,team1 from tbl_tournament_matches where leveID = 2 and tournamentID = 91 and looserTeam is not null
) ORDER BY RAND() LIMIT 0,1
But, if you want to store a value for later use, you can use the INTO keyword:
SELECT id, data INTO #x, #y FROM test.t1 LIMIT 1;
http://dev.mysql.com/doc/refman/5.0/en/select-into.html

MySql Query: Select top 3 rows from table for each category

I have a table with records and it has a row called category. I have inserted too many articles and I want to select only two articles from each category.
I tried to do something like this:
I created a view:
CREATE VIEW limitrows AS
SELECT * FROM tbl_artikujt ORDER BY articleid DESC LIMIT 2
Then I created this query:
SELECT *
FROM tbl_artikujt
WHERE
artikullid IN
(
SELECT artikullid
FROM limitrows
ORDER BY category DESC
)
ORDER BY category DESC;
But this is not working and is giving me only two records?
LIMIT only stops the number of results the statement returns. What you're looking for is generally called analytic/windowing/ranking functions - which MySQL doesn't support but you can emulate using variables:
SELECT x.*
FROM (SELECT t.*,
CASE
WHEN #category != t.category THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#category := t.category AS var_category
FROM TBL_ARTIKUJT t
JOIN (SELECT #rownum := NULL, #category := '') r
ORDER BY t.category) x
WHERE x.rank <= 3
If you don't change SELECT x.*, the result set will include the rank and var_category values - you'll have to specify the columns you really want if this isn't the case.
SELECT * FROM (
SELECT VD.`cat_id` ,
#cat_count := IF( (#cat_id = VD.`cat_id`), #cat_count + 1, 1 ) AS 'DUMMY1',
#cat_id := VD.`cat_id` AS 'DUMMY2',
#cat_count AS 'CAT_COUNT'
FROM videos VD
INNER JOIN categories CT ON CT.`cat_id` = VD.`cat_id`
,(SELECT #cat_count :=1, #cat_id :=-1) AS CID
ORDER BY VD.`cat_id` ASC ) AS `CAT_DETAILS`
WHERE `CAT_COUNT` < 4
------- STEP FOLLOW ----------
1 . select * from ( 'FILTER_DATA_HERE' ) WHERE 'COLUMN_COUNT_CONDITION_HERE'
2. 'FILTER_DATA_HERE'
1. pass 2 variable #cat_count=1 and #cat_id = -1
2. If (#cat_id "match" column_cat_id value)
Then #cat_count = #cat_count + 1
ELSE #cat_count = 1
3. SET #cat_id = column_cat_id
3. 'COLUMN_COUNT_CONDITION_HERE'
1. count_column < count_number
4. ' EXTRA THING '
1. If you want to execute more than one statement inside " if stmt "
2. IF(condition, stmt1 , stmt2 )
1. stmt1 :- CONCAT(exp1, exp2, exp3)
2. stmt2 :- CONCAT(exp1, exp2, exp3)
3. Final "If" Stmt LIKE
1. IF ( condition , CONCAT(exp1, exp2, exp3) , CONCAT(exp1, exp2, exp3) )
share
Use group by instead of order by.