I need to write a mysql procedure to delete s_conf which is parent of other s_conf through s_conf_cpsc and each have child rows on s_conf_at. Each s_conf has an id_a which is a string.
The procedure should check if s_conf is only child, then delete, else do nothing to it and proceed with the other.
It should delete all related rows on s_conf, s_conf_cpsc and s_conf_at (unless condition previously stated is met).
So far I have this
CREATE PROCEDURE delete_s_conf_recursive(IN ida VARCHAR(255))
BEGIN
DECLARE _id INT;
DECLARE _id_a VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT ss.id_a, ss.id FROM s_conf AS s
LEFT JOIN s_conf_cpsc AS scc ON scc.id_conf = s.id AND s.id_a = ida
LEFT JOIN s_conf AS ss ON scc.id_conf_h = ss.id
WHERE ss.id_a IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO _id_a, _id;
IF done THEN
LEAVE read_loop;
END IF;
CALL delete_s_conf_recursive(_id_a);
END LOOP;
CLOSE cur;
SET #q = (SELECT COUNT(*) FROM s_conf_cpsc LEFT JOIN s_conf ON s_conf_cpsc.id_conf_h = s_conf.id AND s_conf.id_a = ida WHERE s_conf.id_a = ida);
IF (#q < 2) THEN
DELETE s_conf_tipo_at FROM s_conf_tipo_at
LEFT JOIN s_conf ON s_conf_tipo_at.id_conf = s_conf.id AND s_conf.id_a = ida
WHERE s_conf.id_a = ida;
DELETE s_conf_cpsc FROM s_conf_cpsc
LEFT JOIN s_conf ON s_conf_cpsc.id_conf = s_conf.id AND s_conf.id_a = ida
WHERE s_conf.id_a = ida;
DELETE FROM s_conf
WHERE s_conf.id_a = ida;
END IF;
END $$
DELIMITER ;
But it fails to check that the s_conf is only child before atempting to delete.
The tables are ON DELETE RESTRICT and should not be changed.
Example: I want to delete s_conf 1 which has childs 2 and 3. But 3 is also child of 4. So the procedure should delete s_conf_cpsc 1-2 and 1-3 but only delete s_conf_at of 2 because 3 is still a child of 4.
Mysql version is 5.6
Could you try the below?
CREATE PROCEDURE delete_s_conf_recursive(IN ida VARCHAR(255))
BEGIN
DECLARE _id INT;
DECLARE _id_a VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT ss.id_a, ss.id FROM s_conf AS s
LEFT JOIN s_conf_cpsc AS scc ON scc.id_conf = s.id AND s.id_a = ida
LEFT JOIN s_conf AS ss ON scc.id_conf_h = ss.id
WHERE ss.id_a IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO _id_a, _id;
IF done THEN
LEAVE read_loop;
END IF;
CALL delete_s_conf_recursive(_id_a);
END LOOP;
CLOSE cur;
SET #q = (SELECT COUNT(*) FROM s_conf_cpsc WHERE id_conf_h = (SELECT id FROM s_conf WHERE id_a = ida));
IF (#q = 0) THEN
DELETE FROM s_conf_at WHERE id_a = ida;
DELETE FROM s_conf_cpsc WHERE id_conf = (SELECT id FROM s_conf WHERE id_a = ida);
DELETE FROM s_conf WHERE id_a = ida;
END IF;
END $$
DELIMITER ;
The main difference is that it checks the number of child rows in the s_conf_cpsc table directly using a subquery. It does this by counting the number of rows in s_conf_cpsc where the id_conf_h column matches the id of the row in the s_conf table with the input ida. If the count is 0, indicating that the s_conf row has no children, it proceeds to delete the related rows in the s_conf_at, s_conf_cpsc, and s_conf tables.
I found that a simpler approach was to separate the function into two parts, one that will take care recursevily of the children recieving id from parent and child to be deleted in order to check if the id pased as parent is the only one and if not to delete that relation without touching the s_conf_at, and other function that will called the first but will only take care of the first id that is passed.
Here is the result
DROP PROCEDURE IF EXISTS delete_s_conf_recursive_ch;
DELIMITER $$
CREATE PROCEDURE delete_s_conf_recursive(IN ida VARCHAR(255))
BEGIN
DECLARE _id INT;
DECLARE _id_a VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT ss.id_a, ss.id FROM s_conf AS s
LEFT JOIN s_conf_cpsc AS scc ON scc.id_conf = s.id AND s.id_a = ida
LEFT JOIN s_conf AS ss ON scc.id_conf_h = ss.id
WHERE ss.id_a IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO _id_a, _id;
IF done THEN
LEAVE read_loop;
END IF;
CALL delete_s_conf_recursive_ch(_id_a, ida);
END LOOP;
CLOSE cur;
DELETE s_conf_at FROM s_conf_at
LEFT JOIN s_conf ON s_conf_at.id_conf = s_conf.id AND s_conf.id_a = ida
WHERE s_conf.id_a = ida;
DELETE FROM s_conf
WHERE s_conf.id_a = ida;
END $$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE eliminar_s_conf_hijo(IN ida VARCHAR(255), ida_p VARCHAR(255))
BEGIN
DECLARE _id INT;
DECLARE _id_a VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE q INT;
DECLARE cur CURSOR FOR
SELECT ss.id_a, ss.id FROM s_conf AS s
LEFT JOIN s_conf_cpsc AS scc ON scc.id_conf = s.id AND s.id_a = ida
LEFT JOIN s_conf AS ss ON scc.id_conf_h = ss.id
WHERE ss.id_a IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO _id_a, _id;
IF done THEN
LEAVE read_loop;
END IF;
CALL eliminar_s_conf_hijo(_id_a, ida);
END LOOP;
CLOSE cur;
SELECT COUNT(*) INTO q FROM s_conf_cpsc WHERE s_conf_cpsc.id_conf_h IN (SELECT id FROM s_conf WHERE s_conf.id_a = ida);
DELETE FROM s_conf_cpsc
WHERE s_conf_cpsc.id_conf IN (SELECT id FROM s_conf WHERE s_conf.id_a = ida_p)
AND s_conf_cpsc.id_conf_h IN (SELECT id FROM s_conf WHERE s_conf.id_a = ida);
IF q < 2 THEN
DELETE s_conf_at FROM s_conf_at
LEFT JOIN s_conf ON s_conf_at.id_conf = s_conf.id AND s_conf.id_a = ida
WHERE s_conf.id_a = ida;
DELETE FROM s_conf
WHERE s_conf.id_a = ida;
END IF;
END $$
DELIMITER ;
Related
I have a stored procedure with 1 parameter but linked to 3 tables, I want to display all of the table with one parameter with this query
CREATE DEFINER=`brambang`#`%` PROCEDURE `Read_discount_campaign`(IN Insert_ID INT(10)
)
proc:
BEGIN
DECLARE is_insert_id INT(10);
DECLARE param_1_message TEXT;
SET #is_insert_id = Insert_ID;
SELECT CONVERT( CONCAT('Maaf, ID discount_campaign tidak bisa \'0\'') USING UTF8) INTO param_1_message;
IF (Insert_ID = 0) THEN
SELECT param_1_message AS message;
LEAVE proc;
END IF;
IF (Insert_ID != 0) THEN
SELECT * FROM discount_campaigns where
id = #is_insert_id;
END IF;
IF (Insert_ID != 0) THEN
SELECT * FROM discount_campaign_advanced where
discount_campaign_id = #is_insert_id;
END IF;
IF (Insert_ID != 0) THEN
SELECT dcp.discount_campaign_id, CASE WHEN dcp.active = 0
THEN dcp.product_id ELSE pc.category_id END as discount_campaign_product
FROM discount_campaign_product dcp JOIN product_categories pc
ON dcp.product_id = pc.product_id
WHERE dcp.discount_campaign_id = Insert_ID;
END IF;
END
After I run this, the output was only from this query (last IF logic)
IF (Insert_ID != 0) THEN
SELECT dcp.discount_campaign_id, CASE WHEN dcp.active = 0
THEN dcp.product_id ELSE pc.category_id END as discount_campaign_product
FROM discount_campaign_product dcp JOIN product_categories pc
ON dcp.product_id = pc.product_id
WHERE dcp.discount_campaign_id = Insert_ID;
END IF;
Instead what I want is it displays all of the logic not only the last one, how to combine all of this logic so the output was from all of the logic, not only from the last logic, is it use cross join?
CREATE TRIGGER input_verification AFTER INSERT ON InputHistory
FOR EACH ROW
WHEN NO EXISTS (SELECT * FROM InputHistory JOIN Intput on InputHistory.inputId = Input.inputId WHERE InputHistory.inputId = NEW.inputId AND InputHistory.inputHistoryOrderNumber=Input.inputOrderNumber AND InputHistory.inputHistoryStatus >= 300)
BEGIN
DECLARE configId INTEGER;
SELECT Input.inputConfigurationId INTO configId FROM Input JOIN InputHistory ON Input.inputId = InputHistory.inputId WHERE NEW.inputId=Input.inputId;
UPDATE Configuration SET configurationReady = true WHERE configurationId= configId;
END
I have this trigger
DELIMITER ;
DELIMITER $$
CREATE TRIGGER when_auction_ends
AFTER UPDATE ON Auctions
FOR EACH ROW
BEGIN
SELECT A.vin, A.status, A.winner, A.minprice, B.amount, B.buyer INTO #vin, #stat, #winner, #minprice, #amount, #buyer
FROM Auctions A, Bids B WHERE A.status = 'Inactive' AND A.winner = null AND A.vin = B.vin AND B.amount = (SELECT MAX(amount) FROM Bids B WHERE B.vin = A.vin AS
IF #amount < #minprice THEN
UPDATE Auctions
SET A.winner = 'NONE'
WHERE A.vin = B.vin;
ELSEIF #amount >= #minprice THEN
UPDATE Auctions
SET A.winner = B.buyer
WHERE A.vin = B.vin;
END IF ;
END$$
DELIMITER ;
But when it activates, the values are not updated. I am thinking that its because mysql does not know what A.winner is in "SET A.winner". I tried looking online on how to use OLD and NEW but did not have any luck.
Running into an error when trying to crate a stored procedure in MySQL (5.1).
Whenever I try and run this SQL script, I am presented with:
Error 1064 (42000) At line 3: You have an error in your SQL syntax.
Near 'DECLARE checkExists INT; SET checkExists = 0; SELECT count(*) INTO c' at line 29
From what I understand, declaring a variable this way should be acceptable.
I have included the SQL script below.
DELIMITER //
CREATE PROCEDURE add_movie(
IN movieTitle VARCHAR(100),
IN movieYear INT,
IN movieDirector VARCHAR(100),
IN movieBannerURL VARCHAR(100),
IN movieTrailerURL VARCHAR(100),
IN starFirstName VARCHAR(50),
IN starLastName VARCHAR(50),
IN starDateOfBirth DATE,
IN starPhotoURL VARCHAR(200),
IN genreName VARCHAR(32),
OUT movieAdded INT,
OUT starAdded INT,
OUT genreAdded INT,
OUT movieStarLinkAdded INT,
OUT movieGenreLinkAdded INT)
LANGUAGE SQL
NOT DETERMINISTIC
SQL SECURITY INVOKER
COMMENT 'Adds Movie, Star, Genre and respective links to DB if they do not exist'
BEGIN
SET movieAdded = 0;
SET starAdded = 0;
SET genreAdded = 0;
SET movieStarLinkAdded = 0;
SET movieGenreLinkAdded = 0;
DECLARE checkExists INT;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM movies m WHERE m.title = movieTitle;
IF(checkExists > 0) THEN
INSERT INTO movies(title, year, director, banner_url, trailer_url)
VALUES (movieTitle, movieYear, movieDirector, movieBannerURL, movieTrailerURL);
SET movieAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM stars s WHERE s.first_name = starFirstName AND s.last_name = starLastName;
IF(checkExists > 0) THEN
INSERT INTO stars(first_name, last_name, dob, photo_url)
VALUES (starFirstName, starLastName, starDateOfBirth, starPhotoURL);
SET starAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM genres g WHERE g.name = genreName;
IF(checkExists > 0) THEN
INSERT INTO genres(name)
VALUES (genreName);
SET genreAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM stars_in_movies sm, stars s, movies m
WHERE m.title = movieTitle AND s.first_name = starFirstName
AND s.last_name = starLastName AND sm.star_id = s.id AND sm.movie_id = m.id;
IF(checkExists > 0) THEN
INSERT INTO stars_in_movies(star_id, movie_id)
VALUES(
SELECT s.id, m.id FROM stars s, movies m WHERE s.first_name = starFirstName
AND s.last_name = starLastName AND m.title = movieTitle);
SET movieStarLinkAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM genres_in_movies gm, genres g, movies m
WHERE m.title = movieTitle AND genre.name = genreName
AND gm.movie_id = m.id AND gm.genre_id = g.id;
IF(checkExists > 0) THEN
INSERT INTO genres_in_movies(genre_id, movie_id)
VALUES(
SELECT g.id, m.id FROM genres g, movies m
WHERE g.name = genreName AND m.title = movieTitle);
SET movieGenreLinkAdded = 1;
END IF;
END //
DELIMITER ;
If someone could help me understand what I am doing incorrectly here, I would greatly appreciate it. Thank you.
Move all DECLARE statements to the start of a BEGIN .. END block, next to BEGIN.
See documentation: https://dev.mysql.com/doc/refman/5.6/en/declare.html
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.
I have created a following stored procedure to insert rows in the table panel_user:
CREATE PROCEDURE panel_users()
BEGIN
DECLARE i INTEGER;
DECLARE user_id BIGINT(20);
DECLARE panel_id BIGINT(20);
DECLARE wt DECIMAL(10,6);
DECLARE level DECIMAL(6,2);
DECLARE total INTEGER;
SET i=1;
SELECT COUNT(*) INTO total FROM smartmeter_usage.users_reporting;
WHILE i<=total DO
SELECT u.userId INTO user_id, p.panelId INTO panel_id, ur.usage_percent INTO level, ipr.wt_base INTO wt
FROM mondrainusage.user_d u
INNER JOIN smartmeter_usage.imei_phone_reporting ipr ON u.imeiNumber = ipr.IMEI
INNER JOIN smartmeter_usage.users_reporting ur ON ur.mobile_number = ipr.mobile_number
INNER JOIN smartmeter_usage.panel pn ON pn.panel_id = ur.panel_id
INNER JOIN mondrainusage.panel p ON p.panelName = pn.panel_name
WHERE u.userId = i;
INSERT INTO mondrainusage.panel_user (panelId, userId, userWeight, usageLevel) VALUES(panel_id, user_id, wt, level);
SET i = i+1;
END WHILE
END
I am getting following error:
Undeclared variable: p
I am not getting what is wrong here in my query. Is there a way out of it?
EDIT:
k I got the solution for the above problem. here is the solution:
CREATE PROCEDURE panel_users()
BEGIN
DECLARE i INTEGER;
DECLARE user_id BIGINT(20);
DECLARE panel_id BIGINT(20);
DECLARE wt DECIMAL(10,6);
DECLARE level DECIMAL(6,2);
DECLARE total INTEGER;
SET i=1;
SELECT COUNT(*) INTO total FROM smartmeter_usage.users_reporting;
WHILE i<=total DO
SELECT u.userId, p.panelId, ur.usage_percent, ipr.wt_base INTO user_id, panel_id, level, wt
FROM mondrainusage.user_d u
INNER JOIN smartmeter_usage.imei_phone_reporting ipr ON u.imeiNumber = ipr.IMEI
INNER JOIN smartmeter_usage.users_reporting ur ON ur.mobile_number = ipr.mobile_number
INNER JOIN smartmeter_usage.panel pn ON pn.panel_id = ur.panel_id
INNER JOIN mondrainusage.panel p ON p.panelName = pn.panel_name
WHERE u.userId = i;
INSERT INTO mondrainusage.panel_user (panelId, userId, userWeight, usageLevel) VALUES(panel_id, user_id, wt, level);
SET i = i+1;
END WHILE
END
Bit now it is giving me syntax error near 'END' at line 21.
Why is this so?
EDIT:
Got the solution:
END WHILE;
Here is a solution that worked.
CREATE PROCEDURE panel_users()
BEGIN
DECLARE i INTEGER;
DECLARE user_id BIGINT(20);
DECLARE panel_id BIGINT(20);
DECLARE wt DECIMAL(10,6);
DECLARE level DECIMAL(6,2);
DECLARE total INTEGER;
SET i=1;
SELECT COUNT(*) INTO total FROM smartmeter_usage.users_reporting;
WHILE i<=total DO
SELECT u.userId, p.panelId, ur.usage_percent, ipr.wt_base INTO user_id, panel_id, level, wt
FROM mondrainusage.user_d u
INNER JOIN smartmeter_usage.imei_phone_reporting ipr ON u.imeiNumber = ipr.IMEI
INNER JOIN smartmeter_usage.users_reporting ur ON ur.mobile_number = ipr.mobile_number
INNER JOIN smartmeter_usage.panel pn ON pn.panel_id = ur.panel_id
INNER JOIN mondrainusage.panel p ON p.panelName = pn.panel_name
WHERE u.userId = i;
INSERT INTO mondrainusage.panel_user (panelId, userId, userWeight, usageLevel) VALUES(panel_id, user_id, wt, level);
SET i = i+1;
END WHILE;
END