Stored procedure, using variable no result - mysql

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 ;

Related

Stored procedure is too slow in mysql

I have a routine. But it' s too slow. How can I improve the query?
My records: http://www.sqlfiddle.com/#!9/14cceb/1/0
My query:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE example.ssa()
BEGIN
drop table if exists gps_table;
drop table if exists exam_datas;
CREATE TEMPORARY TABLE gps_table(ID int PRIMARY KEY AUTO_INCREMENT,timei
int,
trun_date_time datetime, tadd_meter int, tin_here int null);
insert into gps_table(timei,trun_date_time,tadd_meter,tin_here) select
imei, run_date_time, add_meter, in_here from example_table;
CREATE TEMPORARY TABLE exam_datas(ID int PRIMARY KEY AUTO_INCREMENT,vimei
int, vbas_run_date_time datetime, vbit_run_date_time datetime, vdifff int);
select tin_here from gps_table limit 1 into #onceki_durum;
select count(id) from gps_table into #kayit_sayisi;
set #i = 1;
set #min_mes = 0;
set #max_mes = 0;
set #frst_id = 0;
set #imei = 0;
set #run_date_time = '0000-00-00 00:00:00';
set #run_date_time2 = '0000-00-00 00:00:00';
myloop: WHILE (#i <= #kayit_sayisi) DO
select tin_here from gps_table where id = #i into #in_here_true;
if (#in_here_true = 1) then
select id,trun_date_time, tadd_meter from gps_table where id = #i into #frst_id,#run_date_time2, #min_mes;
select id from gps_table where id > #frst_id and tin_here =0 order by id asc limit 1 INTO #id;
SET #id = #id-1;
select id, timei, trun_date_time, tadd_meter from gps_table
where id = #id and tin_here =1 limit 1 into #i, #imei, #run_date_time, #max_mes;
if(#i-#frst_id>3) then
set #i:=#i+1;
insert into exam_datas(vimei,vbas_run_date_time,vbit_run_date_time,vdifff) Values (#imei, #run_date_time2, #run_date_time, #max_mes-#min_mes);
SELECT * FROM exam_datas;
SET #asd =1;
elseif 1=1 then
set #i:=#i+1;
End if;
ELSEIF 1=1
THEN SET #i:=#i+1;
End if;
IF (#i = #kayit_sayisi)
THEN set #tamam =1; LEAVE myloop;
END IF;
END WHILE myloop;
select DISTINCT * from exam_datas;
drop table if exists exam_datas;
drop table if exists gps_table;
END
I need: id= 6 first true and id= 11 last_true
firs_trure - last_true = 304-290= 14
id=14 first true and id=18 last_true
firs_true - last_true = 332-324= 8
This routine is too slow.
MySql version is 5.7 and There are 2 milions record in the table.
UPDATE:
Query is here. HERE
Thank you #LukStorms
It's possible to get such results in 1 query.
Thus avoiding a WHILE loop over records.
This example works without using window functions. Just using variables inside the query to calculate a rank. Which is then used to get the minimums and maximums of the groups.
select
imei,
min(run_date_time) as start_dt,
max(run_date_time) as stop_dt,
max(add_meter) - min(add_meter) as diff
from
(
select imei, id, run_date_time, add_meter, in_here,
case
when #prev_imei = imei and #prev_ih = in_here then #rnk
when #rnk := #rnk + 1 then #rnk
end as rnk,
#prev_imei := imei as prev_imei,
#prev_ih := in_here as prev_ih
from example_table t
cross join (select #rnk := 0, #prev_ih := null, #prev_imei := null) vars
order by imei, id, run_date_time
) q
where in_here = 1
group by imei, rnk
having count(*) > 4
order by imei, min(id);
In the procedure such query can be used to fill that final temporary table.
A test on db<>fiddle here

Problem with increment variable and stored procedure to check duplicates

I have problem with my procedure. I have table oferty_in which contain fields (id, status, ..., id_om). I want procedure which check if exist rows with the same id_om.
If exist, delete rows where status = 'N' (N - new).
My procedure almost works, but i have problem with iterate in loop. Every time I run my procedure ,procedure delete a half of rows. I don't know where is problem...
DELIMITER //
CREATE PROCEDURE check_duplicates_oferty_in()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE v_id_del BIGINT;
SELECT count(*) INTO n FROM oferty_in where status_oferty = 'N';
SET i=0;
WHILE i<n DO
IF EXISTS (SELECT id_om FROM oferty_in group by id_om having count(*) >= 2 LIMIT i,1) THEN
SELECT id_om INTO v_id_del FROM oferty_in group by id_om having count(*) >= 2 LIMIT i,1;
DELETE from oferty_in where id_om = v_id_del and status_oferty = 'N';
END IF;
SET i=i+1;
END WHILE;
END
//
I try also:
IF EXISTS (SELECT id_om FROM oferty_in group by id_om having count(*) >= 2 LIMIT i,1) THEN
SELECT id_om INTO v_id_del FROM oferty_in group by id_om having count(*) >= 2 LIMIT i,1;
DELETE from oferty_in where id_om = v_id_del and status_oferty = 'N';
SET i=i+1;
ELSE
SET i=i+1;
END IF;
But it's the same.
Every time half of rows. I use counter 'i' and while loop to iterate row by row on rows in oferty_in when status = 'N'. Anyone have a idea what I did wrong? Thanks for help and time.
You seem to want to delete rows with status = 'N' when id_om is duplicated.
I want procedure which check if exist rows with the same id_om. If exist, delete rows where status = 'N' (N - new).
Non-working code doesn't generally help explain logic, so this is what I am going by.
You definitely do not need a looping construct for this, nor a cursor:
delete o
from oferty_in o join
(select o2.id_om
from oferty_in o2
group by o2.id_om
having count(*) > 1 and sum(status = 'N') > 0
) o2
on o.id_om = o2.id_om
where o.status = 'N';

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

Mysql Set Variable ve Stored Procedure

I have a question about mysql stored procedure and variables;
Table Name : es_adwords
Fields : id # image_source # image_link # computer_id # is_all_pc # image_state # banner_size
I want to create stored procedure that should search computer_id = {pc_number} and image_state = 0.
If not, stored procedure should return to be equal to is_all_pc = 1.
DELIMITER $$
DROP PROCEDURE IF EXISTS `GetAdwordsBanner` $$
DELIMITER $$
CREATE PROCEDURE `GetAdwordsBanner`(IN compid INT)
BEGIN
SET #result_id := 0;
SELECT #result_id := id FROM es_adwords WHERE image_state = 1 AND computer_id = compid ORDER BY id DESC LIMIT 0,1;
IF (#result_id != 0) THEN
SELECT image_source,image_link,banner_size FROM es_adwords WHERE image_state = 1 AND computer_id = compid ORDER BY id DESC LIMIT 0,1;
ELSE
SELECT image_source,image_link,banner_size FROM es_adwords WHERE image_state = 1 AND is_all_pc = 1 ORDER BY id DESC LIMIT 0,1;
END IF;
END $$
DELIMITER ;
Sincerely Yours...
Your logic has a problem. I would rewrite this to:
DELIMITER $$
CREATE PROCEDURE `GetAdwordsBanner`(IN compid INT)
BEGIN
-- use a local variable instead of a user variable
DECLARE result_count INT;
-- count how many rows are matching
SELECT COUNT(*) INTO result_id FROM es_adwords WHERE image_state = 1 AND computer_id = compid;
-- if there are matches
IF result_count > 0 THEN
-- return the first matching row
SELECT image_source,image_link,banner_size FROM es_adwords WHERE image_state = 1 AND computer_id = compid ORDER BY id DESC LIMIT 0,1;
ELSE
-- return the default one
SELECT image_source,image_link,banner_size FROM es_adwords WHERE image_state = 1 AND is_all_pc = 1 ORDER BY id DESC LIMIT 0,1;
END IF;
END $$
DELIMITER ;
This should work.

SQL IF SELECT query is null then do another query

I have a query that regularly returns "nothing", and I would like to run a different query if this is the case, but I know not of the way of doing this. If anyone could be of help please.
Here is the current code I am using...
SELECT * FROM cfg_users JOIN cfg_ash ON cfg_users.iUserId = cfg_ash.iUserid WHERE iTeamId='0' AND sDisabled IS NULL AND iStatusId > 0 AND sDate = '2014-08-01' GROUP BY cfg_users.iUserId ORDER BY iStatusId, sName
I basically want to say
IF <my code> IS NULL THEN <do other code>, IF <my code> IS NOT NULL THEN return the result.
Thanks
There are some simple way only use sql.
Define your first query as a temp table, with union all, filter the second query with temp table's count.
with temp as (select * from t1 where 1=0)
select * from temp
union all
select * from t2 where (select count(*) from temp) =0
This query will return the second table's records.
with temp as (select * from t1 )
select * from temp
union all
select * from t2 where (select count(*) from temp) =0
And if temp query have result, only return temp query.
You can test with sql fiddle here.
A way you can do it is like this
set two variables equal to the queries you want to execute.
set another variable equal to the correct query when the first is not null.
execute that query with a stored procedure.
STORED PROCEDURE:
DELIMITER $$
CREATE PROCEDURE `dynamic_query`(in input varchar(255))
BEGIN
SET #a := input;
PREPARE stmt FROM #a;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
DELIMITER ;
THE TWO SELECTS YOU WANT TO EXECUTE:
SET #A := "SELECT * FROM cfg_users JOIN cfg_ash ON cfg_users.iUserId = cfg_ash.iUserid WHERE iTeamId='0' AND sDisabled IS NULL AND iStatusId > 0 AND sDate = '2014-08-01' GROUP BY cfg_users.iUserId ORDER BY iStatusId, sName";
SET #B := "your other select here";
THE DEFINER TO GET THE CORRECT QUERY:
SET #C := (
SELECT
CASE
WHEN EXISTS
( SELECT *
FROM cfg_users
JOIN cfg_ash ON cfg_users.iUserId = cfg_ash.iUserid
WHERE iTeamId='0'
AND sDisabled IS NULL
AND iStatusId > 0
AND sDate = '2014-08-01'
GROUP BY cfg_users.iUserId
ORDER BY iStatusId, sName
)
THEN #A
ELSE #B
END
);
EXECUTE THE STATEMENT:
CALL dynamic_query(#C);
DEMO WHEN THE QUERY EXISTS
DEMO WHEN THE QUERY DOESN'T EXIST
You can store the results in a temporary table / table variable, and then check the count
e.g.
CREATE TABLE #Results ( --columns you need here )
INSERT INTO #Results SELECT *
FROM cfg_users
JOIN cfg_ash ON cfg_users.iUserId = cfg_ash.iUserid WHERE iTeamId='0' AND sDisabled IS NULL AND iStatusId > 0 AND sDate = '2014-08-01'
GROUP BY cfg_users.iUserId
ORDER BY iStatusId, sName
SET #Count = SELECT COUNT(*) FROM #Results
IF 0 = #Count THEN
INSERT INTO #Results -- Other Query Here
SELECT * FROM #Results
n.b. you should really specify what columns you want in both queries rather than using *