Debuggin mySQL syntax error in stored procedure - mysql

I am redirecting this file into mysql promt to add a stored procedure:
DROP PROCEDURE IF EXISTS make_transaction;
DELIMITER //
CREATE PROCEDURE make_transaction(IN v_quote_id INT, IN v_your_id INT)
BEGIN
DECLARE v_is_seller BOOLEAN;
DECLARE v_option_type BOOLEAN;
DECLARE v_trader_id INT;
DECLARE v_premium DOUBLE(18, 4);
DECLARE v_offer_expires DATETIME;
DECLARE v_instrument_id INT;
DECLARE v_pretend_now DATETIME;
DECLARE v_buyer_id INT;
DECLARE v_seller_id INT;
DECLARE v_buyer_total_balance DOUBLE(18, 4);
SELECT
instrument_id,
trader_type,
option_type,
trader_id,
premium,
offer_expires
INTO
v_instrument_id,
v_is_seller,
v_option_type,
v_trader_id,
v_premium,
v_offer_expires
FROM
option_quotes
WHERE
quote_id = v_quote_id;
IF v_is_seller THEN
SET v_seller_id = v_trader_id;
SET v_buyer_id = v_your_id;
ELSE
SET v_buyer_id = v_trader_id;
SET v_seller_id = v_your_id;
END IF;
-- Last STOCK_TRADE time is assumed to be the current time
SELECT DATE_TIME
INTO v_pretend_now
FROM STOCK_TRADE
WHERE INSTRUMENT_ID=v_instrument_id
ORDER BY DATE_TIME DESC
LIMIT 1;
SELECT total_balance
INTO v_buyer_total_balance
FROM traders
WHERE trader_id=v_buyer_id;
IF offer_expires <= v_pretend_now THEN
SELECT 'That offer has expired';
ELSE IF v_buyer_total_balance < v_premium THEN
SELECT 'You do not have enough money to transact on this offering';
ELSE
INSERT INTO option_transactions
(
transaction_time,
quote_id,
buyer_id,
seller_id,
buyer_gain,
seller_gain
)
VALUES
(
v_pretend_now,
v_quote_id,
v_buyer_id,
v_seller_id,
NULL, -- line 85
NULL
);
END IF;
END //
DELIMITER ;
The error I am receiving trying to enter the stored procedure into db:
ERROR 1064 (42000) at line 5: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 85
The option_transactions table looks like this:
+------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+-------+
| quote_id | int(11) | NO | MUL | NULL | |
| buyer_id | int(11) | NO | MUL | NULL | |
| seller_id | int(11) | NO | MUL | NULL | |
| transaction_time | datetime | NO | | NULL | |
| buyer_gain | double(18,4) | YES | | NULL | |
| seller_gain | double(18,4) | YES | | NULL | |
+------------------+--------------+------+-----+---------+-------+
I am new at mySQL, but the insert into command near line 85 appears correct in syntax to me, I am not sure where the problem is. How may I fix this?
(mysql server version 5.5.42)

Line 85 is not in the middle of your INSERT statement, it's at the end of the CREATE PROCEDURE statement. If I paste your code into vim and :se nu to show line numbers, and then I delete the initial lines before CREATE PROCEDURE, the last line is line 85:
84 END IF;
85 END //
86
87 DELIMITER ;
So the line numbers in the error start counting from the first line of the statement, not the first line of the SQL script file.
Then I went looking and found an imbalanced blocks:
IF offer_expires <= v_pretend_now THEN
ELSE IF v_buyer_total_balance < v_premium THEN
ELSE
END IF;
You used a nested IF/THEN/ELSE/END IF inside an ELSE. So you actually have two IF statements, but only one END IF.
To fix this, you have two options:
Make it one IF statement with an ELSIF:
IF offer_expires <= v_pretend_now THEN
ELSEIF v_buyer_total_balance < v_premium THEN
ELSE
END IF;
See https://dev.mysql.com/doc/refman/5.7/en/if.html for docs on the syntax of IF/THEN/ELSIF/ELSE/END IF.
Finish both IF statements:
IF offer_expires <= v_pretend_now THEN
ELSE
IF v_buyer_total_balance < v_premium THEN
ELSE
END IF;
END IF;

Related

premature loop exits in store procedure function works correctly but not runs for all variable

i have a single table with duplicate id.i created a table with distinct id and count of that id i want to compare and update based on that id
+----+-------+
| id | value |
+----+-------+
| 1 | 1:a |
| 1 | 1 |
| 1 | 2:b |
+----+-------+
expected output
+----+-------+
| id | value |
+----+-------+
| 1 | 1:a 1 |
| 1 | 2:b |
+----+-------+
what i have tried is i have written a procedure which executes perfectly but it prematurely exist the loop. i don't know why it exits can anyone guide me
DELIMITER //
CREATE PROCEDURE value_merge()
BEGIN
DECLARE v_val INTEGER DEFAULT 0;
DECLARE i INTEGER DEFAULT 1;
DECLARE row_cou INTEGER DEFAULT 0;
DECLARE colan INTEGER DEFAULT 0;
DECLARE mm_num varchar(20) DEFAULT "";
DECLARE merge_value varchar(132000) DEFAULT "";
DEClARE merge_cursor CURSOR FOR
SELECT Material_Number,cou FROM zz_plant_data_given_table_form_work_bhai where flag='y';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_val = 1;
OPEN merge_cursor;
get_loop: LOOP
FETCH merge_cursor INTO mm_num,row_cou;
IF v_val = 1 THEN
LEAVE get_loop;
END IF;
while i<=row_cou do
SELECT Basic_Data_Text INTO merge_value FROM `zz_plant_data_given_table_form_work` where Material_Number=mm_num and seq=row_cou;
set colan=ROUND ((LENGTH(merge_value)- LENGTH( REPLACE (merge_value, ":", "") )) / LENGTH(":"));
IF colan>0 THEN
set row_cou=row_cou-1;
ELSE
update `zz_plant_data_given_table_form_work` set sts='y' where Material_Number=mm_num and seq=row_cou;
set row_cou=row_cou-1;
update zz_plant_data_given_table_form_work set Basic_Data_Text=concat(Basic_Data_Text,' ',merge_value) where Material_Number=mm_num and seq=row_cou;
END IF;
END while;
update zz_plant_data_given_table_form_work_bhai set flag='' where Material_Number=mm_num;
END LOOP get_loop;
CLOSE merge_cursor;
END//
DELIMITER
;
This two sentences fires the NOT FOUND handler, so I suggest use another type of query to get merge_value
Change it for:
set merge_value = ( SELECT Basic_Data_Text
FROM `zz_plant_data_given_table_form_work`
where Material_Number=mm_num
and seq=row_cou
limit 1 ); /* to prevent errors*/

How do I store each date between 2 sets of dates in a separate column

I have a table A like so:
id | valid_from_date | valid_to_date |
1 | 2015-05-01 23:15:01 | 2015-05-02 10:20:23 |
2 | 2015-07-13 10:25:02 | 2015-07-14 18:20:45 |
I want to be able to get all the dates between valid_from_date & valid_to_date per id, in the below format, which I then intend to put in a new table:
id | date |
1 | 2015-05-01 |
1 | 2015-05-02 |
2 | 2015-07-13 |
2 | 2015-07-14 |
What would be the best way to achieve this?
Thank you.
I don't know it's best way or not....but it gives Required output....
create tableb as:
CREATE TABLE `tableb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dates` date DEFAULT NULL,
`aid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
Now,
insert into tableb(aid,dates) select id,date(valid_from_date) as dates
from tablea union select
id,date(valid_to_date) as dates from tablea order by id;
May be Stored procedure is right option :
CREATE PROCEDURE `test_procedure`()
BEGIN
DECLARE aid INT;
DECLARE avalid_from_date date;
DECLARE avalid_to_date date;
DECLARE tempdt date;
DECLARE done INT DEFAULT FALSE;
DECLARE getdates CURSOR FOR SELECT * from tablea;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN getdates;
read_loop: LOOP
FETCH getdates INTO aid,avalid_from_date,avalid_to_date;
IF done THEN
LEAVE read_loop;
END IF;
set tempdt=date(avalid_from_date);
WHILE (tempdt <= date(avalid_to_date)) do
insert into tableb(dates,aid) values( tempdt,aid);
set tempdt=tempdt+INTERVAL 1 DAY;
end while;
END LOOP;
CLOSE getdates;
END
Now,Just call test_procedure;
All the output rows will be inserted in tableb.

Duplicate a record with some changes in stored proc mysql

my table is:
+------+---------+----------+--------+---------+------------+----------+----------------+------------------+-----------+--------+--------+---------+-------+
| r_id | r_width | r_height | r_code | r_store | r_provider | r_parent | r_regiter_time | r_last_used_time | r_comment | r_type | r_open | r_usage | r_log |
+------+---------+----------+--------+---------+------------+----------+----------------+------------------+-----------+--------+--------+---------+-------+
| 1 | 2000 | 3500 | test 1 | 0 | 0 | 0 | 0 | 0 | nop | NULL | NULL | 99.3618 | NULL |
| 2 | 2000 | 3000 | usd22 | 2 | 1 | 0 | 1440915529 | 1440915529 | comment | 2 | 0 | 13.0833 | NULL |
| 3 | 3000 | 2500 | qb88 | 1 | 2 | 0 | 1440921466 | 1440921466 | commex | 3 | 0 | 0 | NULL |
+------+---------+----------+--------+---------+------------+----------+----------------+------------------+-----------+--------+--------+---------+-------+
3 rows in set (0.00 sec)
AND this my stored procedure :
CREATE DEFINER=`root`#`localhost` PROCEDURE `ROLE_SPLIT_V`(IN `id` INT, IN `top` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE exit handler for sqlexception
BEGIN
SHOW ERRORS LIMIT 1 ;
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
SHOW WARNINGS LIMIT 1;
ROLLBACK;
END;
START TRANSACTION;
SET #MyRec = (SELECT `r_width`,(`r_height` - top ) as 'r_height' ,CONCAT(`r_code`,'_',ROUND(RAND() * 1000)) as 'r_code' ,`r_store`,`r_provider`,
`r_parent`,`r_regiter_time`,`r_last_used_time`,`r_comment`,`r_type`,`r_open`,`r_usage`,`r_log` FROM prfix_role WHERE r_id = id) ;
-- UPDATE prfix_role SET prfix_role.r_height = top WHERE prfix_role.r_id = id ;
INSERT INTO prfix_role (`r_width`,`r_height`,`r_code`,`r_store`,`r_provider`,`r_parent`,`r_regiter_time`,`r_last_used_time`,`r_comment`,`r_type`,`r_open`,`r_usage`,`r_log`)
VALUES (MyRec);
SET #lst_id = LAST_INSERT_ID();
-- UPDATE prfix_slice SET s_top = s_top - top,s_role_id = lst_id WHERE s_role_id = id AND s_top > top ;
COMMIT;
END
But not see any change after run in my db I have this error:
Operand should contain 1 column(s)
You are not using session variable properly.
Instead, in your case of requirement, I suggest you to go with local variables to read-into values from table. And use them with insert statement.
Example:
START TRANSACTION;
BEGIN
DECLARE _r_width INT; -- or whatever it be
DECLARE _r_height INT; -- or whatever it be
DECLARE _r_code VARCHAR(255);
DECLARE _r_store INT; -- or whatever it be
DECLARE _r_provider INT; -- or whatever it be
DECLARE _r_width INT; -- or whatever it be
--- all other required fields here
-- now select values into above variables
SELECT `r_width`, (`r_height` - top ) as 'r_height'
, CONCAT(`r_code`,'_',ROUND(RAND() * 1000)) as 'r_code'
, `r_store`,`r_provider`
, `r_parent`, `r_regiter_time`, `r_last_used_time`
, `r_comment`, `r_type`, `r_open`
, `r_usage`, `r_log`
INTO _r_width, _r_height, _r_code, _r_store
-- rest of other field variables should go here
FROM prfix_role WHERE r_id = id ;
-- now use the variable values with insert statement
INSERT INTO prfix_role ( `r_width`, `r_height`, `r_code`, `r_store`
, `r_provider`, `r_parent`, `r_regiter_time`, `r_last_used_time`
, `r_comment`, `r_type`, `r_open`, `r_usage`, `r_log` )
VALUES ( _r_width, r_height, _r_code, _r_store, .... );
-- if you want to use last inserted value,
-- you can directly call it in the statement
UPDATE prfix_slice
SET s_top = s_top - top
, s_role_id = LAST_INSERT_ID()
WHERE s_role_id = id
AND s_top > top ;
END; -- end transaction
I changing my code to this and solve problem:
CREATE DEFINER=`root`#`localhost` PROCEDURE `ROLE_SPLIT_V`(IN `id` INT, IN `top` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE exit handler for sqlexception
BEGIN
SHOW ERRORS LIMIT 1 ;
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
SHOW WARNINGS LIMIT 1;
ROLLBACK;
END;
START TRANSACTION;
-- SET #MyRec = ;
--
INSERT INTO prfix_role SELECT NULL,`r_width`,(`r_height` - top ) as 'r_height' ,CONCAT(`r_code`,'_',ROUND(RAND() * 1000)) as 'r_code' ,`r_store`,`r_provider`,
`r_parent`,`r_regiter_time`,`r_last_used_time`,`r_comment`,`r_type`,`r_open`,`r_usage`,`r_log` FROM prfix_role WHERE r_id = id ;
UPDATE prfix_role SET prfix_role.r_height = top WHERE prfix_role.r_id = id ;
SET #lst_id = LAST_INSERT_ID();
UPDATE prfix_role SET prfix_role.r_height = top WHERE prfix_role.r_id = id ;
UPDATE prfix_slice SET s_top = s_top - top,s_role_id = #lst_id WHERE s_role_id = id AND s_top > top ;
COMMIT;
END

Srtange result from mysql function

I create a function that returns a value, selected in a periods table like this:
CREATE TABLE `periods` (
`period` DATE NOT NULL,
`threshold` DOUBLE NOT NULL,
PRIMARY KEY (`period`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
+------------------------+
| periods |
+------------+-----------+
| period | threshold |
+------------+-----------+
| 2013-11-01 | 5 |
+------------+-----------+
| 2013-12-01 | 1 |
+------------+-----------+
| 2014-01-01 | 5 |
+------------+-----------+
| 2014-02-01 | 5 |
+------------+-----------+
And function create:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `GET_THRESHOLD`(`PERIOD` VARCHAR(10))
RETURNS double
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
COMMENT 'RETURN THRESHOLD'
BEGIN
DECLARE RESULT DOUBLE;
SELECT threshold INTO RESULT FROM periods WHERE period = PERIOD LIMIT 1;
RETURN RESULT;
END
$$
DELIMITER ;
but the function returns a value the Wrong
mysql>SELECT GET_THRESHOLD('2013-12-01')
-> 5
someone can help me?
You parameter has the same name as a column. That's a no-no. Try this:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `GET_THRESHOLD`(`V_PERIOD` VARCHAR(10))
RETURNS double
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
COMMENT 'RETURN THRESHOLD'
BEGIN
DECLARE RESULT DOUBLE;
SELECT threshold INTO RESULT FROM periods WHERE period = V_PERIOD LIMIT 1;
RETURN RESULT;
END
$$
DELIMITER
The statement:
WHERE period = PERIOD
is comparing the column value to itself. So, it chooses all rows that have a non-NULL column value. Not very interesting.
It is good practice to always prefix variables and arguments with something to distinguish them from columns in tables.

SQL cursor & stored procedure order arrangement

I was working on a stored procedure to update the order field in a product table.
It works only the problem now is the the last item in the loop(cur), is increased twice instead of once (so dubbeled). Like so:
+-----------------+
|product + order |
|_id | |
| | |
| 1 | 0 |
| 2 | 1 |
| etc.. | etc..|
| 36 | 35 |
| 37 | 36 |
| 38 | 38 |
| |
+-----------------+
I cant figure out why. The link table(CategoryProduct) in this case goes to 38 with a category_id of 2 CALL curorder(2);
Stored procedure:
DELIMITER //
CREATE PROCEDURE curorder(
IN catid INT
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE i INT DEFAULT 0;
DECLARE p INT;
DECLARE cur CURSOR FOR SELECT product_id FROM test.CategoryProduct WHERE category_id = catid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO p;
UPDATE `test`.`Product` SET `order` = i WHERE `Product`.`product_id` =p;
SET i = i + 1;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
The Database is a Mysql Database. Any suggestions for improving the procedure are always welcome.
Thanks in advance.
EDIT:
I already tried to place the SET i STATEMENT beneath the IF STATEMENT but with no result.
You should put:
IF done THEN
LEAVE read_loop;
END IF;
Above your update statement, the last time mysql walks trough the loop is uses the old variables because there is no new 'p'. but i is incremented.
I good way to debug stored procedures is with a log table:
CREATE TABLE procedureLog
(
id INTEGER AUTO_INCREMENT,
description TEXT,
PRIMARY KEY (id)
);
For this case you can log the update parameters with the follow query:
INSERT INTO  `test`.`procedureLog` (`id` ,`description`) VALUES (null,  CONCAT('id: ', CAST(p as CHAR), ' order: ', CAST(i as CHAR)));
Good luck!