I try to create stored procedure in MySQL:
create PROCEDURE sp_attachAuthorToBook(
IN _bookId INT,
IN _authorName VARCHAR(20)
)
BEGIN
declare _authorId int
select _authorId = id from Authors where name = _authorName
IF (_authorId is null) THEN
INSERT INTO Authors (name)
VALUES (_authorName)
SELECT _authorId = SCOPE_IDENTITY()
END IF
insert into Books_Authors (bookID, authorID)
values (_bookId, _authorId)
END;
But I get the error:
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 'select _authorId = id from Authors where name = _authorName IF
(_authorId is nul' at line 1
Seems like script is too large to be executed. Line just broken near: IF (_authorId is null) THEN -- IF (_authorId is nul
You have a problem with your delimiters. By default it's ";". But you need internal delimiter, so :
DELIMITER |
create PROCEDURE sp_attachAuthorToBook(
IN _bookId INT,
IN _authorName VARCHAR(20)
)
BEGIN
declare _authorId int;
select _authorId = id from Authors where name = _authorName;
IF (_authorId is null) THEN
INSERT INTO Authors (name)
VALUES (_authorName);
SELECT _authorId = LAST_INSERT_ID();
END IF;
insert into Books_Authors (bookID, authorID)
values (_bookId, _authorId);
END|
Its working code on SQLYOG: Please try in this way
DELIMITER $$
CREATE /*[DEFINER = { user | CURRENT_USER }]*/ PROCEDURE `test`.`sp_attachAuthorToBook`(
IN _bookId INT,
IN _authorName VARCHAR(20)
)
BEGIN
DECLARE _authorId INT;
SELECT id INTO _authorId FROM Authorss WHERE `name` = _authorName;
IF _authorId IS NULL THEN
INSERT INTO Authorss(`name`) SELECT _authorName;
SET _authorId = LAST_INSERT_ID();
END IF;
INSERT INTO Book_Authors(bookID, authorID) SELECT _bookId, _authorId;
END$$
DELIMITER ;
Related
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
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
I created a stored procedure which should perform the following:
Insert into Agencies Table, creating an id in the PK index row
Save the id in a variable
Insert the id and other data into Users table
The syntax below does not trigger any errors:
DELIMITER $$
CREATE PROCEDURE insertNewAgencyAndAdmin (IN aName varchar (100), IN numTrav int, IN polType int, IN uEmail varchar(255), IN uFName varchar(40), IN uLName varchar(40), IN uTitle varchar(100))
BEGIN
INSERT INTO btsAgency.Agencies (agencyName, numTrav, polType) VALUES (#aName, #numTrav, #polType);
SET #agencyID = (SELECT agencyID from btsAgency.Agencies where agencyID = LAST_INSERT_ID());
INSERT INTO btsUsers.Users (userAgencyID, userEmail, userFirstName, userLastName, userTitle ) VALUES
(#agencyID, #uEmail, #uFName, #uLName, #uTitle);
END
However, the Stored Proc doesn't insert my parameters into the tables when executed. So, after searching I try to create the SP like this (including $$ after "END" and resetting the delimiter to a semi-colon ):
DELIMITER $$
CREATE PROCEDURE insertNewAgencyAndAdmin (IN aName varchar (100), IN numTrav int, IN polType int, IN uEmail varchar(255), IN uFName varchar(40), IN uLName varchar(40), IN uTitle varchar(100))
BEGIN
INSERT INTO btsAgency.Agencies (agencyName, numTrav, polType) VALUES (#aName, #numTrav, #polType);
SET #agencyID = (SELECT agencyID from btsAgency.Agencies where agencyID = LAST_INSERT_ID());
INSERT INTO btsUsers.Users (userAgencyID, userEmail, userFirstName, userLastName, userTitle ) VALUES
(#agencyID, #uEmail, #uFName, #uLName, #uTitle);
END $$
DELIMITER ;
MySql creates the Stored Proc but gives me the following error:
1064 - 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 'DELIMITER' at line 1
When I execute the created Stored Proc it still doesn't insert the data in my parameters.
Any help would be appreciated.
Some problems in your stored procedure:
It is important to indicate the difference between 9.4. User-Defined Variables and routine parameters 13.1.15. CREATE PROCEDURE and CREATE FUNCTION Syntax, are different variables (eg.: aName != #aName).
Avoid naming parameters or variables as columns of your tables.
DELIMITER $$
CREATE PROCEDURE `insertNewAgencyAndAdmin` (
/*
IN `aName` varchar (100),
IN `numTrav` int,
IN `polType` int,
IN `uEmail` varchar(255),
IN `uFName` varchar(40),
IN `uLName` varchar(40),
IN `uTitle` varchar(100)
*/
IN `_aName` varchar (100),
IN `_numTrav` int,
IN `_polType` int,
IN `_uEmail` varchar(255),
IN `_uFName` varchar(40),
IN `_uLName` varchar(40),
IN `_uTitle` varchar(100)
)
BEGIN
/*
INSERT INTO `btsAgency`.`Agencies` (
`agencyName`,
`numTrav`,
`polType`
) VALUES (
#`aName`,
#`numTrav`,
#`polType`
);
*/
INSERT INTO `btsAgency`.`Agencies` (
`agencyName`,
`numTrav`,
`polType`
) VALUES (
`_aName`,
`_numTrav`,
`_polType`
);
/*
SET #`agencyID` = (SELECT `agencyID` from `btsAgency`.`Agencies` where `agencyID` = LAST_INSERT_ID());
INSERT INTO `btsUsers`.`Users` (
`userAgencyID`,
`userEmail`,
`userFirstName`,
`userLastName`,
`userTitle`
) VALUES (
#`agencyID`,
#`uEmail`,
#`uFName`,
#`uLName`,
#`uTitle`
);
*/
INSERT INTO `btsUsers`.`Users` (
`userAgencyID`,
`userEmail`,
`userFirstName`,
`userLastName`,
`userTitle`
) VALUES (
LAST_INSERT_ID(),
`_uEmail`,
`_uFName`,
`_uLName`,
`_uTitle`);
END$$
DELIMITER ;
SELECT * FROM discounts $$
delimiter $$;
CREATE PROCEDURE insertData(IN id int,IN title varchar(255),IN amount decimal)
BEGIN
insert into discounts values (id,title,amount);
END; $$
call insertData(3,"sadasd",56);
/*DROP PROCEDURE insertData */
I have this MySQL query to create a stored procedure:
Delimiter //;
Create Procedure addUser(
IN facebookId varchar(20),
IN name varchar(50),
In accessToken varchar(100),
in expires float)
Begin
Declare invitingUsers Table(Id varchar(20));
Insert Into Users
(`Facebook_Id`,`Name`,`Access_Token`,`Expired`) Values (facebookId,name,accessToken,expires);
Select Inviting_Id
From Invited_Users
Where Invited_Id = facebookId
Into invitingUsers;
Update Table Users
Set Credit = Credit + 1
Where Facebook_Id In (Select Id From invitingUsers);
End//
but I'm keep getting this error - can't understand why:
#1064 - 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 'Table(Id varchar(20)); Insert Into Users (`Facebook' at line 7
Change #1
Delimiter //; to Delimiter //
Change #2
Create a Temp Table in Memory
Change #3
Changed
Select Inviting_Id
From Invited_Users
Where Invited_Id = facebookId
Into invitingUsers;
into
INSERT INTO invitingUsers
Select Inviting_Id From Invited_Users
Where Invited_Id = facebookId;
With these changes I give you this:
Delimiter //
Create Procedure addUser(
IN facebookId varchar(20),
IN name varchar(50),
In accessToken varchar(100),
in expires float)
Begin
Declare invitingUsers Table(Id varchar(20));
Create temporary table if not exists invitingUsers
(Id varchar(20), PRIMARY KEY (id)) ENGINE=MEMORY;
Insert Into Users
(`Facebook_Id`,`Name`,`Access_Token`,`Expired`)
Values (facebookId,name,accessToken,expires);
INSERT INTO invitingUsers
Select Inviting_Id From Invited_Users
Where Invited_Id = facebookId;
Update Table Users
Set Credit = Credit + 1
Where Facebook_Id In (Select Id From invitingUsers);
End//
Delimiter ;
Give it a Try !!!
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.