MySQL stored procedure looping control - mysql

I am looping over a cursor and have the standard exist loop control set however in the loop I also do select into from another table, if this search returns no results then that triggers the exit handler and the loop terminates.
My question is: How do I attached the continue handler for the loop to a cursor and ignore the select into or how do I get round this problem?
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_results=1;
.
.
.
search_loop:WHILE (no_more_results=0) DO
.
FETCH my_csr INTO something;
.
SELECT thing INTO my_thing FROM `dups` where `dups`.thing = thing_id;
The last select triggers the exit conditions...
Thanks
Paul

Since your code is expecting zero or one rows, use the SET syntax:
SET my_thing = (SELECT thing FROM dups where dups.thing = thing_id);
This will set my_thing to null if there are no rows.
I haven't tested this, so if it is still setting the handler, change to this, which always returns a row:
SET my_thing = SELECT IFNULL((SELECT thing FROM dups where dups.thing = thing_id), null);
Alternatively, you can convert your statement similarly:
SELECT IFNULL((SELECT thing FROM dups where dups.thing = thing_id), null) INTO my_thing;
I believe these alternatives are presented in order of readability.

Related

Is there a way to set multiple variables in one SELECT query in MySQL?

Hello, everybody.
In my SP, there's a SELECT query like this.
SELECT val_01, val_02 INTO var_01, var_02 FROM table_name WHERE col_name = condition_01;
This query works properly.
But I've added exit handler for SQLWARNING and when above SELECT query has no result, it makes SQL WARNING like this and stopped.
No data - zero rows fetched, selected, or processed
I've googled and found a way to solve this problem. It is changing the query like this...
SELECT var_01 = val_01, var_02 = val_02 FROM table_name WHERE col_name = condition_01;
This query works. But there's a difference between original query and modified query.
In SP, original one doesn't throw SELECT query's result. It just put SELECT query's value into variables.
But the latter one throws its SELECT result with variable's value.
So my cpp code which call SP get wrong value.
I can change query to..
SET var_01 = SELECT val_01 FROM table_name WHERE col_name = condition_01;
SET var_02 = SELECT val_02 FROM table_name WHERE col_name = condition_01;
But as you know, this is very inefficient and below query doesn't work.
SET var_01, var_02 = (SELECT val_01, val_02 FROM table_name WHERE col_name = condition_01);
Is there an efficient way to make same result of original query?
Please help me. Thank you.
Without more context, it's unclear precisely what you are asking, but if you need to temporarily override the exit handler, nest your query in a new scoping block, with a handler to clear the variables. (They have to be cleared if inside a loop, or they will still contain their prior values if assigned).
-- inside existing procedure
BEGIN -- add this and the handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET var_01 = NULL, var_02 = NULL;
SELECT val_01, val_02 INTO var_01, var_02 FROM table_name WHERE col_name = condition_01;
END; -- add this
-- procedure continues here
SELECT a, b INTO aa, bb ...
Reference: See INTO in https://dev.mysql.com/doc/refman/5.7/en/select.html
SET #var_01 = #var_02 = '';
SELECT _val_01, _val_02 INTO #var_01, #var_02 FROM table_name WHERE col_name = condition_01;
// check whether var_01=='' or length==0 then do what you want
SELECT #var_01, #var_02
// or
SELECT LENGTH(#var_01)

cursor loop in PostgreSQL

This below code is a cursor in PostgreSQL 9.0. I want to fetch my records by joining more than one table and i am getting JSON data from that join.
So I want to loop those records and parse that json using query something like
SELECT "Dump"->'activities-steps'->0->'value' as "steps"
FROM "ActivitySessionDump" where "Id"=42594321345021288
then i have to get data from this query and insert to some other table like
insert to table name (key,value);
So i prepared one readonly cursor to achieve this task
begin work;
DECLARE
sessionids INSENSITIVE no scroll CURSOR FOR
SELECT asn."Id",asn."UserId",asn."ActivityId",ad."Dump"
FROM "ActivitySession" as asn inner join "ActivitySessionDump" as ad
on asn."Id"=ad."ActivitySessionId"
where asn."CreatedAt" between now() - interval '5 hours' and now() and asn."ActivityId"=1
for read only;
---- i want her loop should start and i will parse a json Dump by executing query--------
--------insert record to another table---------------
---end loop-----------
FETCH next FROM sessionids;
CLOSE sessionids;
COMMIT WORK;
Any help really appreciated.Thanks
Since you cannot loop in SQL, you'll have to use PL/pgSQL, for example with a DO statement.
In your case, that could look like this:
DO
$$DECLARE
asn_id ...;
asn_userid ...;
...
c refcursor;
BEGIN
/* assign the SQL cursor to the refcursor variable */
c := 'sessionids';
LOOP
FETCH c INTO asn_id, asn_userid, ...;
IF NOT FOUND THEN EXIT; END IF;
/* process the result row */
END LOOP;
END;$$;
Of course it is a bit awkward to declare a cursor in SQL and use it in PL/pgSQL.
It might be better to put the statement in a FOR loop like this:
FOR asn_id, asn_userid, ... IN
SELECT ...
LOOP
/* process the result row */
END LOOP;
Maybe you could even squeeze the whole thing into a single INSERT statement, that would be most efficient:
INSERT INTO ...
(SELECT ...);
As far as I can tell, the loop or function is unnecessary. It can be replaced with a simple query using string aggregation:
SELECT string_agg("Dump"->'activities-steps'->0->'value', ',') as steps
FROM "ActivitySessionDump" d
WHERE d."ActivitySessionId" IN (SELECT asn."Id"
FROM "ActivitySession" as asn
join "PersonDataSource" as pd on pd."UserId" = asn."UserId"
where asn."CreatedAt" between now() - interval '5 days' and now()
and asn."ActivityId" = 1
and pd."DataSourceId" = 1);
Unrelated, but: you should really avoid those dreaded quoted identifiers
here is the code for my question and i am unable to
EXECUTE 'SELECT rec."Dump"::json#>''{activities-steps,0}''->>''value'' as steps ' INTO jsonrec; line;
SELECT '{"activities-steps":[{"dateTime":"2016-10-17","value":"4023"}]}'::json#>'{activities-steps,0}'->>'value' as steps;
where as i can execute this code in console.
but inside function i cant.
CREATE OR REPLACE FUNCTION ThirdPartyDataParse()
RETURNS text AS $$
DECLARE
sessionid NO SCROLL CURSOR FOR SELECT asn."Id",asn."UserId",asn."ActivityId",pd."DataSourceId",ad."Dump"::TEXT
FROM "Development"."ActivitySession" as asn inner join "Development"."PersonDataSource" as pd on pd."UserId" = asn."UserId" inner join "Development"."ActivitySessionDump" as ad
on asn."Id"=ad."ActivitySessionId" where asn."CreatedAt" between now() - interval '5 days' and now() and asn."ActivityId"=1 and pd."DataSourceId"=1 for read only;
titles TEXT DEFAULT '';
rec record;
jsonrec record;
BEGIN
OPEN sessionid;
loop
FETCH sessionid INTO rec;
--raise notice '%d',rec."UserId";
if not found then
exit ;
end if;
EXECUTE 'SELECT rec."Dump"::json#>''{activities-steps,0}''->>''value'' as steps ' INTO jsonrec;
titles := titles || ',' || jsonrec."steps";
end loop;
return titles;
END;
$$ LANGUAGE plpgsql;

Why does MySQL find an error in the CONCAT statement?

I'm trying to write a stored procedure in MySQL, where I need to loop on a cursor, and execute an SQL statement that uses a piece of data from the cursor that gets fetched into a variable, then executed as SQL. The cursor orders_cur is sorted on this_addr; in a given block of this_addr records, the first record is skipped, and the rest need to be flagged by setting the duplicateorder="1".
For some reason, I cannot get the line with the CONCAT function to work without giving me an error:
OPEN orders_cur;
order_loop: LOOP
-- Now loop on orders_cur until this_addr = match_addr
find_addr_loop: REPEAT
FETCH orders_cur INTO this_addr,this_orderid;
UNTIL this_addr = match_addr
END REPEAT;
-- Skip the first order that matched by performing another fetch
FETCH orders_cur INTO this_addr,this_orderid;
-- Now start next loop which does the real work; set duplicateorder on the remaining records in cursor,
-- using the orders_cur.order_id to locate the actual record in the Reservations table.
set_dupe_loop: WHILE this_addr = match_addr
SET #sql = CONCAT('UPDATE Reservations SET duplicateorder = \'1\' WHERE order_id=',this_orderid);
PREPARE runme FROM #sql;
EXECUTE runme;
FETCH orders_cur INTO this_addr,this_orderid;
END WHILE set_dupe_loop:;
DEALLOCATE PREPARE runme;
END LOOP order_loop;
I have tried every variation possible on escaping the literals that I need around the '1' to no avail, and am going cross-eyed...if anyone sees where my error lies, I would very much appreciate it...
--rixter
You don't need a cursor for this operation. You can do:
UPDATE Reservations r JOIN
(SELECT this_addr, MIN(order_id) as minoi
FROM Reservations r2
WHERE this_addr = match_addr
GROUP BY this_addr
) dups
ON r.this_addr = dups.this_addr and r.order_id > dups.minoi
SET r.duplicateorder = 1;
In general, you should avoid cursors, especially those that require dynamic SQL. When you can express the logic as a set operation, it is usually best to do so.
Nothing wrong with the CONCAT, the loop is not initiated/enclosed properly.
This set_dupe_loop: WHILE this_addr = match_addr
should be this set_dupe_loop: WHILE this_addr = match_addr DO

mysql trigger with select from database and update a column

I have this trigger. If the incoming log agrees with input filter, than is not saved into database. But, I want to keep number of "hits" of each Primitive_filter. I have a column named hit_rate, which is int(30). Is there some way how to do that? Maybe specific error? Or sth else? Thx for help.
UPDATE Primitive_filters SET hit_rate = hit_rate + 1 where Primitive_filters.id = ???;
trigger
delimiter //
CREATE TRIGGER inputFilter
before insert
on Logs
for each row
begin
declare msg varchar(255);
IF (SELECT COUNT(*) FROM Primitive_filters, Primitive_in_filter, Filters WHERE
Filters.name = "input" AND Filters.id = Primitive_in_filter.id_filter AND Primitive_in_filter.id_primitive = Primitive_filters.id AND
(Primitive_filters.id_host LIKE CONCAT('%',(SELECT host FROM Hosts WHERE id = new.id_host),'%') OR Primitive_filters.id_host IS NULL) AND
(Primitive_filters.facility LIKE CONCAT('%',new.facility,'%') OR Primitive_filters.facility IS NULL) AND
(Primitive_filters.priority LIKE CONCAT('%',new.priority,'%') OR Primitive_filters.priority IS NULL) AND
(Primitive_filters.program LIKE CONCAT('%',new.program,'%') OR Primitive_filters.program IS NULL) AND
(new.msg REGEXP Primitive_filters.msg OR Primitive_filters.msg IS NULL)) > 0 THEN CALL raise_error; END IF;
END //
delimiter ;
This is NOT the answer to your question.
It's only a hint how to fix a potentially serious performance problem in your code.
Don't use this:
IF (SELECT COUNT(*) FROM ... giant query ...) > 0
THEN CALL raise_error;
END IF;
Use this instead:
IF EXISTS (SELECT 1 FROM ... giant query ...)
THEN CALL raise_error;
END IF;
The former condition calculates a count ... it must read all rows returned by the query
If the query returns billion rows, it must reads them all --> because you asked give me a count of rows.
Then, after the query return the count, there is a check: if the query returns at least one row, then do something.
The latter condition stops executing the query when the query returns first row, saving time and resources.

Stored Procedure stops if "SELECT fieldname INTO var" returns 0 rows

I'm running a stored procedure and have ran into a problem with the following line:
SELECT photo_data_id
INTO gallery_rel_id_check
FROM photo_data
WHERE object_type = 4
AND data_id = 0
AND photo_id = row_photo_id LIMIT 1;
In some cases, this query will return 0 rows and thus gallery_rel_id_check doesn't get a value assigned to it.
For some odd reason, this seems to stop the stored procedure. Should I be assigning gallery_rel_id_check in a different way as I need to use it later in the procudure to check to see if its > 0
Thanks
You can declare a continue handler to explicitly set gallery_rel_id_check to the appropriate value if the query does not return any rows.
For example:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET gallery_rel_id_check = -1;
credit goes to #Paolo Bergantino
Just use:
SET gallery_rel_id_check = (SELECT...);