I have a table that stores some comma separated strings. Is there a way to write a sql to
return me a separate rows for each token string obtained by splitting across commas.
You can simply write and call a stored procedure
DELIMITER $$
DROP PROCEDURE IF EXISTS explode_table $$
CREATE PROCEDURE explode_table(bound VARCHAR(255))
BEGIN
DECLARE id INT DEFAULT 0;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value INT;
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR SELECT table1.id, table1.value
FROM table1
WHERE table1.value != '';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` INT NOT NULL,
`value` VARCHAR(255) NOT NULL
) ENGINE=Memory;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id, value;
IF done THEN
LEAVE read_loop;
END IF;
SET occurance = (SELECT LENGTH(value)
- LENGTH(REPLACE(value, bound, ''))
+1);
SET i=1;
WHILE i <= occurance DO
SET splitted_value =
(SELECT REPLACE(SUBSTRING(SUBSTRING_INDEX(value, bound, i),
LENGTH(SUBSTRING_INDEX(value, bound, i - 1)) + 1), ',', ''));
INSERT INTO table2 VALUES (id, splitted_value);
SET i = i + 1;
END WHILE;
END LOOP;
SELECT * FROM table2;
CLOSE cur1;
END; $$
-------------------
CALL explode_table(',');
Related
I need to write a stored procedure using mysql.
In the repeat loop showing below, if I use something as:
SET VDATEI=(SELECT A.DATEI FROM STAFF_CONVENIOS A WHERE
A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1);
the stored procedure works well and I get a result set.
But, I need select into multiple variables. Something as:
SELECT
A.DATEI, A.DIAI, A.HORAI
INTO
VDATEI, VDIAI, VHORAI
FROM
STAFF_CONVENIOS A
WHERE
A.CONV_ID = CONVE AND A.STAFF_ID = MED
LIMIT 1;
In this case my stored procedure is broken I get no result set.
How to use select into to multiple variables into stored procedure with temporary tables?
My stored procedure is similar to:
CREATE PROCEDURE P_GETHORARIOS(
IN `MED` BIGINT,
IN `DATAREF` DATE,
IN `ESPEC` BIGINT,
IN `CONVE` BIGINT
)
BEGIN
DECLARE BDONE,BTEMP, BOCUP INT;
DECLARE DIA;
DECLARE VDIA,OK TINYINT;
DECLARE VDURACAO TINYINT;
DECLARE VDATEI DATE;
DECLARE VDIAI TINYINT;
DECLARE VHORAI TIME;
DECLARE VHORA, VHORAI,VHORAF TIME;
DECLARE CURS CURSOR FOR SELECT DIA,COALESCE(A.DURACAO,30) AS DURACAO, A.HINI FROM STAFF_ESCALA A
WHERE A.DIA=DIA;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET BDONE = 1;
DROP TEMPORARY TABLE IF EXISTS TBLRESULTS;
CREATE TEMPORARY TABLE IF NOT EXISTS TBLRESULTS (
DATA_AG DATE,
DIA TINYINT,
HORA TIME );
SET DIA=DAYOFWEEK(DATAREF);
OPEN CURS;
SET BDONE = 0;
REPEAT
SET OK=TRUE;
FETCH CURS INTO VDIA,VDURACAO,VHORAI;
-- SET VDATEI=(SELECT A.DATEI FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1);
SELECT A.DATEI, A.DIAI,A.HORAI INTO VDATEI, VDIAI,VHORAI FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1;
SET BTEMP=IF(VDATEI IS NULL,TRUE,FALSE);
SET OK=OK AND BTEMP ;
IF (OK) THEN
INSERT INTO TBLRESULTS VALUES (DATAREF,VDIA,VHORA);
ELSE INSERT INTO TBLRESULTS VALUES (VDATEI,VDIAI,VHORAI);
END IF;
UNTIL BDONE END REPEAT;
CLOSE CURS;
SELECT A.* FROM TBLRESULTS;
END
I have posted a sample of my problem. Here is the real stored procedure:
CREATE DEFINER=`SYSDBA`#`%` PROCEDURE `P_GETHORARIOS`(
IN `MED` BIGINT,
IN `DATAREF` DATE,
IN `ESPEC` BIGINT,
IN `CONVE` BIGINT
)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE BDONE INT;
DECLARE BTEMP TINYINT;
DECLARE BOCUP TINYINT;
DECLARE DIA BIGINT;
DECLARE VDIA TINYINT;
DECLARE VDURACAO TINYINT;
DECLARE VHORA, VHORAI,VHORAF TIME;
DECLARE VMED_ID BIGINT;
DECLARE VESPEC_ID BIGINT;
DECLARE VCONV_ID BIGINT;
DECLARE OK TINYINT;
DECLARE VNOMEESPEC,VNOMEMED VARCHAR(100);
DECLARE CURS CURSOR FOR SELECT DIA,COALESCE(A.DURACAO,30) AS DURACAO, A.HINI, A.HFIM, A.STAFF_ID,B.NOME AS NOMESTAFF, A.ESPEC_ID,C.NOME AS NOMEESPEC
FROM STAFF_ESCALA A
LEFT JOIN STAFF B ON B.ID=A.STAFF_ID
LEFT JOIN ESPECIALIDADES C ON C.ID=A.ESPEC_ID
WHERE A.DIA=DIA;
-- DECLARE CURS CURSOR FOR SELECT HINI, HFIM FROM STAFF_ESCALA;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET BDONE = 1;
DROP TEMPORARY TABLE IF EXISTS TBLRESULTS;
CREATE TEMPORARY TABLE IF NOT EXISTS TBLRESULTS (
DATA_AG DATE,
DIA TINYINT,
HORA TIME,
MED_ID BIGINT,
NOME_MED VARCHAR(100),
ESPEC_ID BIGINT,
NOME_ESPEC VARCHAR(100)
);
SET DIA=DAYOFWEEK(DATAREF);
OPEN CURS;
SET BDONE = 0;
REPEAT
SET OK=TRUE;
FETCH CURS INTO VDIA,VDURACAO,VHORAI,VHORAF,VMED_ID,VNOMEMED,VESPEC_ID,VNOMEESPEC;
IF (MED IS NOT NULL) THEN
SET OK=IF(MED=VMED_ID,TRUE,FALSE) ;
END IF;
IF (ESPEC IS NOT NULL) THEN
SET OK=OK AND IF(ESPEC=VESPEC_ID,TRUE,FALSE) ;
END IF;
IF (CONVE IS NOT NULL) THEN
-- SET #EOK=(SELECT 1 FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1);
SELECT 1 INTO #EOK FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1;
SET BTEMP=IF(#EOK IS NULL,TRUE,FALSE);
SET OK= OK AND BTEMP ;
END IF;
IF (OK) THEN
SET VHORA=VHORAI;
IF (VDURACAO IS NULL) THEN
SET VDURACAO=30;
END IF;
WHILE (VHORA <= VHORAF) DO
SET BOCUP=(SELECT 1 FROM AGENDA_STAFF A WHERE A.DATA_MARCADA=DATAREF AND A.HORA_MARCADA=VHORA);
IF (BOCUP IS NULL) THEN
INSERT INTO TBLRESULTS VALUES (DATAREF,VDIA,VHORA,VMED_ID,VNOMEMED,VESPEC_ID,VNOMEESPEC);
END IF;
SET VHORA=ADDTIME(VHORA, VDURACAO * 100);
END WHILE;
SET VHORA=ADDTIME(VHORA, VDURACAO * (-100));
IF (VHORA < VHORAF) THEN
SET VHORA=VHORAF;
SET BOCUP=(SELECT 1 FROM AGENDA_STAFF A WHERE A.DATA_MARCADA=DATAREF AND A.HORA_MARCADA=VHORA);
IF (BOCUP IS NULL) THEN
INSERT INTO TBLRESULTS VALUES (DATAREF,VDIA,VHORA,VMED_ID,VNOMEMED,VESPEC_ID,VNOMEESPEC);
END IF;
END IF;
END IF;
UNTIL BDONE END REPEAT;
CLOSE CURS;
SELECT A.* FROM TBLRESULTS A LEFT JOIN AGENDA_STAFF B ON B.STAFF_ID=A.MED_ID AND B.HORA_MARCADA=A.HORA AND B.DATA_MARCADA=A.DATA_AG;
END
//
The problem is with the lines:
-- SET #EOK=(SELECT 1 FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1);
SELECT 1 INTO #EOK FROM STAFF_CONVENIOS A WHERE A.CONV_ID=CONVE AND A.STAFF_ID=MED LIMIT 1;
If I use "SET #EOK..." all works well.
If I use SELECT 1 INTO #EOK.. the problem appears.
More info with data
Expected result from call p_gethorarios(7,'2017-11-20',47,21)
"data_ag";"dia";"hora";"med_id";"nome_med";"espec_id";"nome_espec"
"2017-11-20";"2";"08:30:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"09:00:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"09:30:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"10:00:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"08:30:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"09:00:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"09:30:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
"2017-11-20";"2";"10:00:00";"7";"Paulo Renato Scofano";"47";"Pediatria/Neonatologia"
STAFF-ESCALA TABLE
ID;STAFF_ID;DIA;HINI;HFIM;ESPEC_ID;DURACAO;CREATION_TIME;MODIFICATION_TIME;LAST_USER_MODIF
25;7;2;08:00:00;10:00:00;50;;18/11/2017 21:44:02;;1
26;7;6;08:00:00;10:00:00;50;;18/11/2017 21:55:27;;1
27;7;4;08:00:00;10:00:00;50;;18/11/2017 21:55:27;;1
28;7;5;08:00:00;10:00:00;50;;18/11/2017 21:56:36;;1
35;7;3;08:00:00;10:00:00;47;;19/11/2017 19:10:29;;1
36;7;5;08:00:00;10:00:00;47;;19/11/2017 19:10:29;;1
38;7;2;08:00:00;10:00:00;47;;19/11/2017 19:10:29;;1
You may have to use user/session variables (the #xyz kind). The documentation does not explicitly state such, but it only shows examples with them.
https://dev.mysql.com/doc/refman/5.7/en/select-into.html
If it is an issue with the NOT FOUND handler getting triggered from something other than the cursor, you could try structuring your code something like this (very pseudocode):
FETCH
WHILE( NOT DONE )
BEGIN
DO STUFF
SET DONE = FALSE
FETCH
END
sorry for my english :)
I need (only with SQL) select all column values form table A, split its by separator and insert separated values to other table?
For example, i have table like this:
ID, NAME, MAGIC
1, Marty, ACD ACFX U128BH
and i need export "MAGIC" value to separate table like this:
ID, USERID, MAGIC
1, 1, ACD
2, 1, ACFX
3, 1, U128BH
How to do it? I found eg. SQL insert data to other table after split string, but this is MS SQL syntax (?) and im using MySQL.
Thanx for reply
EDIT THIS ANSWER TO MUCH YOUR WORK
#drop table magic_t;
create table magic_t(
id int,
name varchar(100),
magic varchar(100)
);
#drop table split_insert;
create table split_insert(
split_value varchar(100)
);
insert into magic_t values(1,'nam1','VAL1 VAL2 VAL3');
insert into magic_t values(1,'nam1','VAL4 VAL5 VAL3');
#drop function strSplit;
DELIMITER ;;
CREATE FUNCTION strSplit(x varchar(255), delim varchar(12), pos int) returns varchar(255)
return replace(substring(substring_index(x, delim, pos), length(substring_index(x, delim, pos - 1)) + 1), delim, '');;
DELIMITER ;
#drop procedure split_table;
DELIMITER ;;
CREATE procedure split_table ()
begin
DECLARE done INT DEFAULT 0;
DECLARE while_condition boolean default true;
DECLARE counter INT DEFAULT 0;
DECLARE magic_value varchar(100);
declare splited_value varchar(100);
DECLARE colum_list CURSOR FOR SELECT magic FROM magic_t;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; -- to stop loop when fetch is over
OPEN colum_list;
REPEAT FETCH colum_list INTO magic_value;
if done =0 then
set counter =1;
set while_condition = true;
while(while_condition) do
set splited_value = strSplit(magic_value,' ',counter) ;
if splited_value <>'' then
insert into split_insert values(splited_value);
end if;
if splited_value='' then set while_condition=false; end if;
set counter =counter+1;
end while;
end if;
UNTIL done -- here stop repeat when done =1
END REPEAT;
end ;;
DELIMITER ;
call split_table ();
select * from split_insert;
Can anyone shed light on why this will not loop through the function and populate the temp table? Ive tried a number of things and at best can only get the first value to populate. I'm trying to take a value and a ":" separated string (queried in this procedure) to populate a table so I can reference it in a larger query. The function SPLIT_STR works great on its own but I cant seem to increment value "a" so that it separates ALL values per field per value.
BEGIN
DECLARE platform_val VARCHAR(255);
DECLARE productName_val VARCHAR(255);
DECLARE no_more_rows BOOLEAN;
DECLARE num_rows INT DEFAULT 0;
DECLARE loop_cntr INT DEFAULT 0;
DECLARE str VARCHAR(255);
DECLARE a INT DEFAULT 1;
DECLARE cur1 CURSOR FOR SELECT
ProductName,
ProductPlatforms
FROM Feed
limit 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
OPEN cur1;
select FOUND_ROWS() into num_rows;
the_loop: LOOP
FETCH cur1
INTO productName_val,
platform_val;
SET str = platform_val;
WHILE a< num_rows DO
SET str=SPLIT_STR(str,":",a);
insert into temp values (productName_val, str);
SET a=a+1;
END WHILE;
END LOOP the_loop;
END
This is the idea I came up with
CREATE PROCEDURE `col2lines`()
BEGIN
declare v_platform,v_productName,v_platformAll varchar(255) default null;
declare v_finished int default 0;
declare curs cursor for select ProductName,ProductPlatforms from Feed limit 10;
declare continue handler for not found set v_finished = 1;
drop temporary table if exists temp_table;
create temporary table temp_table (
productName varchar(255),
platform varchar(255)
);
open curs;
parent: LOOP
fetch curs into v_productName, v_platformAll;
if (v_finished = 1) then LEAVE parent; end if;
child: LOOP
set v_platform = substring_index(v_platformAll, '::', 1);
set #strlen = char_length(v_platform);
if (#strlen > 0) then
insert into temp_table values (v_productName, v_platform);
set v_platformAll = substr(v_platformAll, #strlen + 3);
iterate child;
end if;
LEAVE child;
END LOOP child;
iterate parent;
END LOOP parent;
close curs;
select * from temp_table;
END
i am copying data for a Many to Many Relationship table making a Third Table.
The stored procedure currently looks like this but it has some error
DELIMITER $$
CREATE PROCEDURE `test`.UpdateRelatedAccounts()
BEGIN
DECLARE ssn_sel_id VARCHAR(255) DEFAULT 0;
DECLARE id_sel_id CHAR(36) DEFAULT 0;
DECLARE id_sel_rel CHAR(36) DEFAULT 0;
DECLARE no_more_rows BOOLEAN;
DECLARE num_rows INT DEFAULT 0;
DECLARE no_more_rel_rows BOOLEAN;
DECLARE rel_num_rows INT DEFAULT 0;
DECLARE ssn_all_cur CURSOR FOR
SELECT ssn, id FROM ssn WHERE ssn NOT IN ('','000-00-0000');
DECLARE ssn_cur CURSOR FOR
SELECT id FROM ssn WHERE id != id_sel_id AND ssn = ssn_sel_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
-- DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rel_rows = TRUE;
OPEN ssn_all_cur;
SELECT FOUND_ROWS() INTO num_rows;
the_loop: LOOP
FETCH ssn_all_cur
INTO ssn_sel_id, id_sel_id;
IF no_more_rows THEN
CLOSE ssn_all_cur;
LEAVE the_loop;
END IF;
OPEN ssn_cur;
SELECT FOUND_ROWS() INTO rel_num_rows;
the_rel_loop: LOOP
FETCH ssn_cur
INTO id_sel_rel;
IF no_more_rel_rows THEN
CLOSE ssn_cur;
LEAVE the_rel_loop;
END IF;
INSERT INTO `ssn_related` ( `ssn_primary`, `ssn_related` ) VALUES ( id_sel_id, id_sel_rel ), ( id_sel_rel, id_sel_id );
END LOOP the_rel_loop;
END LOOP the_loop;
END$$
DELIMITER ;
How do i nest to use the values and insert into third table.
Cursors are slow and most of the time unnecessary. Nested cursors are slow² and unnecessary². Cursors shall only be used as the last resort, when there's really no other way.
What you want to do can be broken down to this:
INSERT INTO `ssn_related` ( `ssn_primary`, `ssn_related` )
SELECT
ssn_1.id,
ssn_2.id
FROM
ssn ssn_1
INNER JOIN ssn ssn_2 ON ssn_2.ssn = ssn_1.id
WHERE ssn_1.ssn NOT IN ('', '000-00-0000')
AND ssn_2.id != ssn_1.id;
Then you do the same again with swapped columns in the SELECT.
INSERT INTO `ssn_related` ( `ssn_primary`, `ssn_related` )
SELECT
ssn_2.id,
ssn_1.id
FROM
ssn ssn_1
INNER JOIN ssn ssn_2 ON ssn_2.ssn = ssn_1.id
WHERE ssn_1.ssn NOT IN ('', '000-00-0000')
AND ssn_2.id != ssn_1.id;
I have a table containing 100,000+ records from which i have some calculations to do based on a value which is stored in a specific column.
For this i have written a stored procedure which takes 100 rows at a time from the base table. Since cursors don't support dynamic sql. What i did was i create a dynamic view and the cursor always selects data from that view.
The code :
BEGIN
-- VARIABLES TO RETURN
DECLARE ORIG_COUNT INT ;
DECLARE SERVED_COUNT INT;
-- VARIABLED FOR LOOPING
DECLARE no_more_rows BOOLEAN;
DECLARE no_more_rows_sub BOOLEAN;
DECLARE num_rows INT DEFAULT 0;
SET #ORIG_COUNT =0;
SET #SERVED_COUNT=0;
SELECT COUNT(*) FROM table_one AS A WHERE A.year='2011' INTO #ORIG_COUNT ;
DELETE FROM table_test;
insert into table_test values ('Total Rows',#ORIG_COUNT,NULL);
DROP TABLE IF EXISTS table_request;
CREATE TABLE table_request (
BN_NUM VARCHAR(25) NOT NULL,
DN_NUM varchar(10) NOT NULL,
A_TOTAL FLOAT DEFAULT 0,
B_TOTAL FLOAT DEFAULT 0,
C_TOTAL FLOAT DEFAULT 0,
PRIMARY KEY (BN)
);
WHILE (#SERVED_COUNT<#ORIG_COUNT) DO
DROP VIEW IF EXISTS pbs_history.temp_view;
SET #query = CONCAT('CREATE VIEW temp_view as SELECT A.BN,A.DN
FROM table_one AS A
WHERE A.year='2011'
ORDER BY A.ID
LIMIT ', #SERVED_COUNT,',100');
PREPARE stmt from #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
/* ================= HERE ==================== */
SELECT * FROM temp_view;
BLOKA:BEGIN
DECLARE BN VARCHAR(25);
DECLARE DN VARCHAR(10);
DECLARE num_rows INT(5) DEFAULT 0;
DECLARE bn_count INT(5) DEFAULT 0;
DECLARE sel_recs CURSOR FOR SELECT * FROM temp_view;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
insert into table_test values('Start Served',#SERVED_COUNT,#query);
OPEN sel_recs;
SELECT FOUND_ROWS() INTO #num_rows;
insert into table_test values('Found Rows in View',#num_rows,NULL);
the_loop: LOOP
FETCH sel_recs INTO BN,DN;
IF no_more_rows THEN
CLOSE sel_recs;
LEAVE the_loop;
END IF;
SET #bn_count=0;
SELECT COUNT(*) FROM table_request WHERE BILL_NUMBER=BN INTO #bn_count;
insert into table_test VALUES(BN,DN,#bn_count);
IF(#bn_count=0) THEN
INSERT INTO table_request VALUES (BN,DN,0,0,0);
END IF;
BLOKB:BEGIN
DECLARE CAT VARCHAR(5);
DECLARE T1 FLOAT;
DECLARE data_fetch CURSOR FOR SELECT CAT_ID, TYPE_1 FROM transactions WHERE ID=BN;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows_sub = TRUE;
OPEN data_fetch;
sub_loop: LOOP
FETCH data_fetch INTO CAT,T1;
IF no_more_rows_sub THEN
CLOSE data_fetch;
LEAVE sub_loop;
END IF;
IF (CAT='P1') THEN
UPDATE table_request SET A_TOTAL = A_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P2') THEN
UPDATE table_request SET B_TOTAL = B_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P3') THEN
UPDATE table_request SET N_TOTAL = N_TOTAL+T1 WHERE BN_NUM=BN;
END IF;
END LOOP sub_loop;
END BLOKB;
SET #SERVED_COUNT = #SERVED_COUNT+1;
END LOOP the_loop;
insert into table_test values ('End the_loop',0,NULL);
END BLOKA;
END WHILE;
END
I'm testing the code with a subset from the original table. And the subset has 461 records.
And with this code there should be 5 iterations. And in the final iteration the view should contain only 61 records. But with this code each iteration has 100 records, and during each iteration it takes into consideration only the first 100 records.
But if i run SELECT * FROM temp_view in mysql console after the procedure has run, it shows the last 61 records.
Anybody can point out what i'm doing wrong here?