SQL: How to select variable value? - mysql

I have a query
INSERT INTO ... SET ....;
SET #last_id = LAST_INSERT_ID();
INSERT INTO ... SET .... ,item_id = #last_id;
INSERT INTO ... SET .... ,item_id = #last_id;
And i want to output to result value of variable. for example:
SELECT #last_id as ID;
query:
INSERT INTO `targets` SET `name` = '123';
SET #target_id = LAST_INSERT_ID();
SELECT #target_id as id;

This is not a query, but rather a set of queries.
Just run them one by one in order.

Related

SQL Stored Procedure IF ELSE logic error

I am trying to implement an insert stored procedure. Basically, how it works is the stored procedure will check whether the record exists or not, then proceed to perform insert. A variable #status will be used as indicator. However, I found out a particular problem with this query is that when I execute the query, it will return the result #status = 1 no matter the data existed or not. However, the INSERT function is fine without any problems just the #status. The following is my implementation:
CREATE PROCEDURE save_proc
(
#userid varchar(10)
#name varchar(30)
)
AS
DECLARE #status int
if exists ( SELECT * FROM table1 where userID = #userid AND userName = #name)
SET #status = 0
else
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
SELECT #status
Try to put it in BEGIN END
CREATE PROCEDURE save_proc
(
#userid varchar(10)
#name varchar(30)
)
AS
DECLARE #status int
if exists ( SELECT * FROM table1 where userID = #userid AND userName = #name)
SET #status = 0
else
begin
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
end
If you are not putting the BEGIN END then the scope of your else statement is just the next line which is getting executed ie, INSERT INTO table1 (userID, userName) VALUES (#userid, #name) is only under the scope of else block. And SET #status = 1 is outside the scope of else block. So once the else block executes the next query will be executed which is SET #status = 1
On a side note:
When you are checking for if exists then don't use the * wildcard. Instead you can use 1 i.e,
if exists ( SELECT 1 FROM table1 where userID = #userid AND userName = #name)
else
begin
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
end
Modify code as above. You haven't defined scope of your else. It was upto insert statement only. SET #status = 1 was executed everytime

Parameterized nested query

I'm trying to parameterize the following insert with a nested select.
INSERT IGNORE INTO table1 (creation_timestamp, str1, str2)
(SELECT now(), "param1", str2 FROM table2 WHERE key = "param2");
I'd like something like
INSERT IGNORE INTO table1 (creation_timestamp, str1, str2)
(SELECT now(), ?, str2 FROM table2 WHERE key = ?)
VALUES ("param1", "param2");
Anyone know how I can accomplish something like this?
Not exactly the same, but very similar.
You can use prepared statements:
http://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
Example:
PREPARE soExample
FROM 'INSERT
INTO usr (id, username, profile_pic)
VALUES (NULL, ?, (SELECT name
FROM customers
WHERE id = ?
LIMIT 1))';
SET #uname = "someUserNameForTheExample";
SET #id = "1";
EXECUTE soExample USING #uname, #id;
Or you can user procedure or/and functions as well
FUNCTION
DROP FUNCTION IF EXISTS insertExample$$
CREATE FUNCTION insertExample(userNameVar VARCHAR(255), uID INT(11)) RETURNS BOOLEAN
BEGIN
INSERT
INTO usr (id, username, profile_pic)
VALUES (NULL, userNameVar, (SELECT name
FROM customers
WHERE id = uID
LIMIT 1));
IF ROW_COUNT() > 0 THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END$$
FUNCTION USE
SELECT insertExample("SomeUsername" 2);
Perhaps you should start by reading https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html. As Mysql Functions enables parameterized input for pre-build queries.

MySQL Trigger - Where am I going wrong

Can someone tell me where I'm going wrong in creating this trigger in MySQL? Every time I try to create it, it keeps telling me there is an error.
USE `cl11-onestock`;
DELIMITER //
CREATE TRIGGER audit_insert
AFTER INSERT
ON stock_usage FOR EACH ROW
BEGIN
SET uUid = (SELECT UUID());
SET stockUsageId = (SELECT NEW.stock_usage_id);
SET siteId = (SELECT NEW.site_id);
SET date = (SELECT NEW.date);
SET patientName = (SELECT NEW.patient_name);
SET dentistDiscountId = (SELECT NEW.dentist_discount_id);
SET itemId = (SELECT NEW.item_id);
SET quantity = (SELECT NEW.quantity);
SET priceIncDiscount = (SELECT NEW.price_inc_discount);
SET vat = (SELECT NEW.vat);
SET priceIncVat = (SELECT NEW.price_inc_vat);
SET createdAt = (SELECT NEW.created_at);
SET updatedAt = (SELECT NEW.updated_at);
INSERT INTO stock_audit VALUES
(uUid, stockUsageId, siteId, date, patientName, dentistDiscountId, itemId, quantity, priceIncDiscount, vat, priceIncVat, createdAt, updatedAt);
END; //
DELIMITER ;
You need to use the keyword new to access the field values after insert and not like you are doing with select and you can use directly inside the insert query
So the trigger should be as
DELIMITER //
CREATE TRIGGER audit_insert
AFTER INSERT
ON stock_usage FOR EACH ROW
BEGIN
INSERT INTO stock_audit VALUES
(new.UUID, NEW.stock_usage_id, NEW.site_id, NEW.date, NEW.patient_name, NEW.dentist_discount_id, NEW.item_id, NEW.quantity, NEW.price_inc_discount, NEW.vat, NEW.price_inc_vat, NEW.created_at, NEW.updated_at);
END; //
DELIMITER ;

MySQL, CONCAT in SELECT statement

(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
I'm using the above statement as part of an INSERT. The problem is the whole line is being translated to the value of #I (this is an index, values are i.e. 0, 1, 2, ...). So, instead of getting the output of the SELECT I'm getting 0, 1, 2, ...
The expected value of the CONCAT is like "0_Delta", then "1_Delta", etc. Replacing the CONCAT by one of this works.
Any comments will be appreciated. Thanks!
[code]
DROP TABLE IF EXISTS xxx_tb;
CREATE TABLE xxx_tb
(
i_Validity INT,
Delta INT
);
DROP TRIGGER IF EXISTS AFTER_INSERT_ON_tb_in;
DELIMITER $$
CREATE TRIGGER AFTER_INSERT_ON_tb_in
AFTER INSERT ON tb_in
FOR EACH ROW
BEGIN
SET #ID = NEW.id_tb;
SET #TYPE = (SELECT Type FROM header WHERE id_tb = #ID);
IF #TYPE = 'abcd' THEN
SET #SAMPLES = (SELECT SampleNumber FROM table WHERE id_tb = #ID);
IF(#SAMPLES > 1) THEN
SET #I = 0;
WHILE(#I < #SAMPLES) DO
INSERT INTO xxx_tb
(
i_Validity,
Delta
)
VALUES
(
(SELECT 0_Validity FROM table WHERE id_tb = #ID),
(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
);
SET #I = #I + 1;
END WHILE;
END IF;
END IF;
END$$
DELIMITER ;
[code]
delta is declared as an integer. You are getting a silent conversion from the string value. Because #i is at the beginning, that is the value you are getting.
You can try declaring it as varchar(255) if you want a string value.
Your insert can be written more easily as an insert . . . select:
INSERT INTO xxx_tb(i_Validity, Delta)
SELECT `0_Validity`, CONCAT(#I, '_Delta')
FROM table WHERE id_tb = #ID);

Is there a way to make SELECT that fails not throw inside a TRY/CATCH?

For example:
CREATE PROCEDURE dbo.MyProc
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
...
DECLARE #Id INT;
-- I don't want the following line to throw and abort processing.
-- I just want #Id to remain NULL. I can add a nested TRY/CATCH, but is there
-- a better way?
SET #Id=(SELECT Id FROM MyTable WHERE ...);
...
END TRY
BEGIN CATCH
...
END CATCH;
END;
Update: Just to clarify, if multiple rows are returned, I want #Id to stay NULL.
I will do something like this to avoid multiple rows errors:
SELECT top 1 #Id = Id FROM MyTable WHERE ...
or this
SELECT #Id = Id FROM MyTable WHERE ...
if ##rowcount > 1
set #id = null
This last piece will set the #Id to null when there were multiple rows.
Try this:
select #id = (select max(id) from MyTable Where <YOUR CONDITION> Having count(1) = 1)
This will return a single row, or no rows if there are multiple matching.
Pretty sure this would not throw:
SELECT #Id = Id FROM MyTable WHERE ...