I am declaring variables inside the procedure and setting those values to result from another query.when executed it is giving null.
version:8.0.16
call putrequest('x',"jiraUPM","ASE-12345","inprogress","testcybsjira.com");
requestId and reqid are not null.but it taking null value .
create procedure `putrequest`(in `employeeId` varchar(15),in `reqtype`
varchar(15),in `ticketId` varchar(15),in `status` varchar(15),in `details`
varchar(100))
begin
declare `rid` int;
declare `reqType` int;
select `requestId` into `reqType` from `requesttype` where `request`=`reqtype`;
select `reqId` into `rid` from `employee` where `empId`=`employeeId`;
insert into `requests` values(rid,reqType,`ticketId`,NOW(),NOW(),`status`,`details`);
end
Error executing SQL statement. Column 'reqId' cannot be null - Connection: Connection 1: 93ms
If either of the SELECT queries doens't find a matching row, the corresponding variable will be NULL, and you'll get an error when you try to insert it. You need to check for that before doing the INSERT.
But there's no need for separate SELECT queries and variables, use INSERT INTO ... SELECT ...
INSERT INTO requests
SELECT e.reqId, r.requestId, ticketId, NOW(), NOW(), status, details
FROM requesttype AS r
CROSS JOIN employee AS e
WHERE r.request = reqtype
AND e.empId = employeeId
If reqtype or employeeId can't be found, the join won't return any rows, so nothing will be inserted.
It creates a problem because of the same variable name "reqtype" (declare as a procedure parameter) and "reqType" (Declare as procedure variable)
It considers the null value for reqtype and not return any record.
MySQL is case insensitive . Be careful while given variable name.
DELIMITER $$
create procedure `putrequest`(in `employeeId` varchar(50),in `rtype`
varchar(50),in `ticketId` varchar(15),in `status` varchar(15),in `details`
varchar(100))
begin
declare `rid` int;
declare `reqType` int;
select `reqId` into `rid` from `employee` where `empId`=`employeeId`;
select `requestId` into `reqType` from requesttype where `request`=`rtype`;
insert into `requests` values(rid,reqType,`ticketId`,NOW(),NOW(),`status`,`details`);
end$$
DELIMITER ;
DEMO
Related
I have a before insert trigger in a MySQL 8.0 database that checks for the incoming status and passes it along to the stored procedure, which then returns a value to be SET and passed along to be inserted. The stored procedure is being called correctly; however, when checking the entry in the database, the desired value of 1 is expected. However, NULL is there.
Here is the BEFORE_INSERT trigger on the table
CREATE DEFINER=`user`#`` TRIGGER `data_BEFORE_INSERT` BEFORE INSERT ON `data` FOR EACH ROW BEGIN
DECLARE newSearchAddressId INTEGER;
IF NEW.status = 'fresh' THEN
CALL CALC_FRESH_SEARCH_ADDRESS_ID(NEW.lead, #newSearchAddressId);
SET NEW.searchAddressId = #newSearchAddressId;
END IF;
END
Here is what's in the stored procedure:
CREATE DEFINER=`user`#`` PROCEDURE `CALC_FRESH_SEARCH_ADDRESS_ID`(
IN leadId INT,
OUT newSearchAddressId INT
)
proc_label: BEGIN
DECLARE lastSearchAddressID INT;
DECLARE lastStatus VARCHAR(100);
SELECT
`searchAddressId`,
`status`
INTO
lastSearchAddressID,
lastStatus
FROM `leads`
WHERE `id` = leadId;
-- if there is no status previous it's a first time entry
IF lastStatus IS NULL THEN
SET newSearchAddressId = 1;
LEAVE proc_label;
END IF;
END
In the SELECT statement above from leads table I am assured that only one row will be returned.
The desired outcome is that I select those two columns, status and searchAddressId, and if status is NULL, then we know that there is no previous entry and, therefore, a brand new record. Because it is a brand new record, we assign the #newSearchAddressId to the value of 1 and leave the stored procedure early.
I think I am incorrectly setting the variables or checking in the IF statement. That's at least what I think is going on. I have looked at this post and tried setting just one variable to check with an IF statement but the same undesired result of NULL being in the database.
Here is sample data:
DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`lead` INT NOT NULL,
`status` VARCHAR(100) NOT NULL,
`searchAddressId INT NOT NULL
) ENGINE=INNODB;
INSERT INTO `data` (`lead`, `status`, `searchAddressId`)
VALUES(1, 'fresh', 1),
VALUES(2, 'suspended', 1),
VALUES(3, 'stale', 1),
VALUES(4, 'fresh', 1),
VALUES(5, 'cancelled', 1);
Based on the data above, here is what I am expecting from the procedure above
DECLARE lastSearchAddressID INT;
DECLARE lastStatus VARCHAR(100);
SELECT `searchAddressId`, `status`
INTO lastSearchAddressID, lastStatus
FROM `data`
WHERE `lead` = 6;
based on the SELECT statement above searchAddressID and lastStatus should be NULL
so when reaching the IF statement of
IF lastStatus IS NULL THEN
SET newSearchAddressId = 1;
LEAVE proc_label;
END IF;
Integer 1 should be assigned to the OUT newSearchAddressId INT in the stored procedure's parameters.
A follow-up query of
SELECT * FROM `data` WHERE `lead` = 6;
Would yield:
id
lead
status
searchAddress
6
6
'fresh'
1
What is yielded:
id
lead
status
searchAddress
6
6
'fresh'
NULL
here is the db fiddle the fiddle is not working but while I fiddle with getting it working I think you can see more clearly the expectations
To not publicly disclose our amount of invoices, we want to add random value between 2 ids.
Instead of [1,2,3] we want something like [69,98,179]
UUID is not an option in that project, unfortunately.
Using Mysql 5.7, 8, or MariaDb get the same results.
Here is the approach is taken:
Consider a simple table invoices as follows:
CREATE TABLE `invoices` (
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4;
The function to get random values:
DROP FUNCTION IF EXISTS random_integer;
CREATE FUNCTION random_integer(value_minimum INT, value_maximum INT)
RETURNS INT
LANGUAGE SQL
NOT DETERMINISTIC
RETURN FLOOR(value_minimum + RAND() * (value_maximum - value_minimum + 1));
The function to get the next id:
DROP FUNCTION IF EXISTS next_invoice_id_val;
DELIMITER //
CREATE FUNCTION next_invoice_id_val ()
RETURNS BIGINT(8)
LANGUAGE SQL
NOT DETERMINISTIC
BEGIN
DECLARE lastId BIGINT(8) DEFAULT 1;
DECLARE randId BIGINT(8) DEFAULT 1;
DECLARE newId BIGINT(8) DEFAULT 1;
DECLARE nextId BIGINT(8) DEFAULT 1;
SELECT (SELECT MAX(`id`) FROM `invoices`) INTO lastId;
SELECT (SELECT random_integer(1,10)) INTO randId;
SELECT ( lastId + randId ) INTO nextId;
IF lastId IS NULL
THEN
SET newId = randId;
ELSE
SET newId = nextId;
END IF;
RETURN newId;
END //
DELIMITER ;
SELECT next_invoice_id_val();
and the trigger:
DROP TRIGGER IF EXISTS next_invoice_id_val_trigger;
DELIMITER //
CREATE TRIGGER next_invoice_id_val_trigger
BEFORE INSERT
ON invoices FOR EACH ROW
BEGIN
SET NEW.id = next_invoice_id_val();
END//
DELIMITER ;
That work like a charm, now if we want to generalize the behaviour to all tables.
We need a procedure to execute the query on any specific tables:
DROP PROCEDURE IF EXISTS last_id;
DELIMITER //
CREATE PROCEDURE last_id (IN tableName VARCHAR(50), OUT lastId BIGINT(8))
COMMENT 'Gets the last id value'
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
BEGIN
SET #s := CONCAT('SELECT MAX(`id`) FROM `',tableName,'`');
PREPARE QUERY FROM #s;
EXECUTE QUERY;
DEALLOCATE PREPARE QUERY;
END //
DELIMITER ;
CALL last_id('invoices', #nextInvoiceId);
SELECT #nextInvoiceId;
The procedure for the next id value:
DROP PROCEDURE IF EXISTS next_id_val;
DELIMITER //
CREATE PROCEDURE next_id_val (IN tableName VARCHAR(50), OUT nextId BIGINT(8))
COMMENT 'Give the Next Id value + a random value'
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE randId BIGINT(8) DEFAULT 1;
SELECT (SELECT random_integer(1,10)) INTO randId;
CALL last_id(tableName, #currentId);
IF #currentId IS NULL
THEN
SET nextId = randId;
ELSE
SELECT ( #currentId + randId ) INTO nextId;
END IF;
END //
DELIMITER ;
CALL next_id_val('invoices', #nextInvoiceId);
SELECT #nextInvoiceId;
and the trigger:
# Call the procedure from a trigger
DROP TRIGGER IF EXISTS next_invoice_id_val_trigger;
DELIMITER //
CREATE TRIGGER next_invoice_id_val_trigger
BEFORE INSERT
ON invoices FOR EACH ROW
BEGIN
CALL next_id_val('invoices', #nextInvoiceId);
SET NEW.id = #nextInvoiceId;
END//
DELIMITER ;
and we get => Dynamic SQL is not allowed in stored function or trigger
I've read that storing in a temporary table might be a workaround, but as all posts have between 5 to 10 years old, I think we might have a better solution for such a straightforward case.
What is the workaround for using dynamic SQL in a stored Procedure
#1336 - Dynamic SQL is not allowed in stored function or trigger
Calling stored procedure that contains dynamic SQL from Trigger
Alternatives to dynamic sql in stored function
So, i have this two tables, table_a which have 2 triggers for insert and update the fields at another table ( table_b).
The main problem is, the delete and insert triggers works perfectly, but the update trigger, when used, instead of updating only the field with the same uid on the table_b, it is updating every field ( 20k + fields). I'm not sure what i'm doing wrong, because when i run the sp code alone ( update query) it works as espected. but it just doesn't work when inside the stored procedure. Bellow, i'm setting
trigger code:
CREATE definer=`test_user`#`%` TRIGGER `trg_update` after
UPDATE
ON `table_a` FOR EACH row CALL sp_update(old.uid,new.name, new.number)
Stored procedure code:
CREATE DEFINER=`test_user`#`%` PROCEDURE `sp_update`(IN `uid` CHAR(50), IN `name` CHAR(50), IN `number` CHAR(50))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
SET #UID = uid;
SET #NAME= name;
SET #NUMBER= number;
UPDATE tbl_master
SET
name = #NAME,
number = #NUMBER
where
uid = #UID;
END
Thanks in advance.
EDIT: For those who want to test it out, a test table with every trigger and sp involved in the process:
Tables
CREATE TABLE `table_a` (
`a` INT(11) NULL DEFAULT NULL,
`b` INT(11) NULL DEFAULT NULL,
`c` INT(11) NULL DEFAULT NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `table_b` (
`a` INT(11) NULL DEFAULT NULL,
`b` INT(11) NULL DEFAULT NULL,
`c` INT(11) NULL DEFAULT NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
Triggers
CREATE DEFINER=`root`#`%` TRIGGER `trg_insert` AFTER INSERT ON `table_a` FOR EACH ROW CALL sp_insert(new.a, new.b, new.c);
CREATE DEFINER=`root`#`%` TRIGGER `trg_update` AFTER UPDATE ON `table_a` FOR EACH ROW BEGIN
call sp_update(old.a,new.b,new.c);
END;
Procedures
CREATE DEFINER=`root`#`%` PROCEDURE `sp_insert`(IN `a` INT, IN `b` INT, IN `c` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
begin
SET #A =a;
SET #B = b;
SET #C = c;
insert into table_b (a,b,c) values (#A, #B, #C);
end
CREATE DEFINER=`root`#`%` PROCEDURE `sp_update`(IN `a` INT, IN `b` INT, IN `c` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
set #A = a;
set #B = b;
set #C = c;
update table_b set b=#B, c=#c where a=#A;
END
So, i managed to find out what was causing this issue. I'm not sure if it is relevant or not, but since the MySQL Server is running on a windows server, I'm not sure if this issue is related to the OS or it's a behavior of the database itself.
Basicaly, it seend that i cannot use the same variable name ( inputs are in lower case, and routine body vars are in upper case). So , i had to change it from:
CREATE DEFINER=`root`#`%` PROCEDURE `sp_update`(IN `a` INT, IN `b` INT, IN `c` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
set #A = a;
set #B = b;
set #C = c;
update table_b set b=#B, c=#c where a=#A;
END
To:
CREATE DEFINER=`root`#`%` PROCEDURE `sp_update`(IN `v_a` INT, IN `v_b` INT, IN `v_c` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
set #A = v_a;
set #B = v_b;
set #C = v_c;
update table_b set b=#B, c=#c where a=#A;
END
Not sure if it it's my fault for using the same variable names , or if it's mysql fault for not recognizing the difference between lower/upper case characters.
I'm just needing a little jump start, I'm a little confused how to do this. What I need to do is pull a 'customer_id' from the table and see how many items the customer has rented. If there are no 'customer_id' then it'll return 0. I'm just no quite grasping how to do this so any help is appreciated.
Create Table:
CREATE TABLE Customer
(name VARCHAR(30) NOT NULL,
address VARCHAR(70),
phone CHAR(10) NOT NULL,
customer_id INT(10) PRIMARY KEY NOT NULL);
Create Function: Have this partially started, but unsure if I'm doing it correctly.
DELIMITER $$
CREATE FUNCTION Num_Of_Rented(IN customer_id INT(10))
RETURNS INT(10)
BEGIN
DECLARE num INT(10);
SELECT IFNULL()
FROM
WHERE
RETURN num;
END $$
DELIMITER;
Inside your function, you need to select your value into your variable and then return your variable:
DECLARE num INT(10);
SELECT COUNT(field) INTO num
FROM table
WHERE condition;
RETURN num;
In your case:
DELIMITER $$
CREATE FUNCTION Num_Of_Rented(IN custId INT(10))
RETURNS INT(10)
BEGIN
DECLARE num INT(10);
SELECT COUNT(*) INTO num
FROM Customer C
WHERE C.customer_id = custId ;
RETURN num;
END $$
DELIMITER;
I have a stored procedure that splits a string and ends with a select.
I would like to run an insert on the stored procedure like you would do an insert on a select
Something like this
INSERT INTO ....
CALL sp_split...
My split looks like this:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `split_with_id`(id INT, input varchar(1000), delim VARCHAR(10))
BEGIN
declare foundPos tinyint unsigned;
declare tmpTxt varchar(1000);
declare delimLen tinyint unsigned;
declare element varchar(1000);
drop temporary table if exists tmpValues;
create temporary table tmpValues
(
`id` int not null default 0,
`values` varchar(1000) not null default ''
) engine = memory;
set delimLen = length(delim);
set tmpTxt = input;
set foundPos = instr(tmpTxt,delim);
while foundPos <> 0 do
set element = substring(tmpTxt, 1, foundPos-1);
set tmpTxt = replace(tmpTxt, concat(element,delim), '');
insert into tmpValues (`id`, `values`) values (id, element);
set foundPos = instr(tmpTxt,delim);
end while;
if tmpTxt <> '' then
insert into tmpValues (`id`, `values`) values (id, tmpTxt);
end if;
select * from tmpValues;
END
Create a wrapper function and have it call the procedure. Then SELECT it normally.
DELIMITER $$
CREATE FUNCTION `f_wrapper_split` (strin VARCHAR(255))
RETURNS VARCHAR(255)
BEGIN
DECLARE r VARCHAR(255);
CALL sp_split(strin);
RETURN r;
END
$$
Of course, if sp_split returns multiple results, you'll need to adapt the function to, perhaps, take an INT input as well and return you that particular result. Then just call it multiple times.
It's not very pretty, but that's the best I can think of offhand.