MySQL Routine not assigning value to variable? - mysql

I have a table a with the following columns:
id
name
a_id
where a_id is the id of another a row.
Suppose I have 3 rows with the following values:
1, 'name1', null
2, 'name2', 1
3, 'name3', 2
Ok, now I want to create a stored procedure that returns the following string:
"name3|name2|name1"
or, in general, the name value of each row that refers to another row until there is no other reference (separated by the | character).
What is wrong with the following code?
DELIMITER $$
CREATE PROCEDURE chain (IN init_name VARCHAR(100), OUT names VARCHAR(1000))
BEGIN
DECLARE next_id INT default 0;
DECLARE curr_id INT default 0; /* Just in case */
DECLARE n VARCHAR(100) default "";
SET n = init_name;
SELECT a_id
INTO next_id
FROM a
WHERE `name`=n;
SET names = n;
WHILE next_id <> NULL DO
SET curr_id=next_id; /* Again, just in case */
SELECT `name`, a_id
INTO n, next_id
FROM a
WHERE id=curr_id;
SET names = CONCAT(names, "|", n);
END WHILE;
END$$
CALL chain('name3', #names) just outputs "name3"

You need to read #names after calling your procedure :SELECT #names. Or if you want procedure to output it, do SELECT names in the body of SP (right after SET names ....)

Related

how to insert multiple rows in table using store procedure parameter in mysql

i have three table product,sizes and product_size
product
> id
> name
product_size
> product_id FK
> size_id FK
size
> id
> name
Know using store procedure i want to insert single product with muliple sizes
eg.
product.id =1;
product.name = xyz;
{
product_size.product_id = 1
product_size.size_id = 1
product_size.product_id = 1
product_size.size_id = 2
product_size.product_id = 1
product_size.size_id = 3
}
So how to pass sizes parameter to store procedure in mysql
The following is a stored procedure that inserts one record into product_size each time you call the procedure. The procedure takes input parameters for product.id and size.id.
DELIMITER //
CREATE PROCEDURE INSERT_product_size(
IN productID int,
IN sizeID int
)
BEGIN
INSERT INTO
product_size
(product_id, size_id)
VALUES
(productID, sizeID);
END //
DELIMITER ;
This procedure takes a single product id and an "array" of size id's (in the form of a comma-delimited string) and inserts all sizes for the product in one procedure call:
DROP PROCEDURE IF EXISTS INSERT_product_sizes;
DELIMITER //
CREATE PROCEDURE IF NOT EXISTS INSERT_product_sizes(
IN productID int,
IN sizeIDs varchar(100)
)
BEGIN
DECLARE delimiterCount int;
DECLARE sizeID int;
DECLARE loopCount int;
/* Remove spaces, if any, from input string */
SET sizeIDs = REPLACE(sizeIDs, ' ', '');
/* Determine how many commas are in input string */
SET delimiterCount = LENGTH(sizeIDs) - LENGTH(REPLACE(sizeIDs, ',', ''));
SET loopCount = 1;
/* For each id in input string */
WHILE loopCount <= delimiterCount + 1 DO
SET sizeID = SUBSTRING_INDEX(sizeIDs, ',', 1);
INSERT INTO
product_size
(product_id, size_id)
VALUES
(productID, sizeID);
/* Remove last used id from input string */
SET sizeIDs = REPLACE(sizeIDs, CONCAT(sizeID, ','), '');
SET loopCount = loopCount + 1;
END WHILE;
END //
DELIMITER ;
If you want all sizes attached to a product on an insert then a simple
insert into product_size(product_id,size_id)
select inprodid,name
from size
;
will do.
But if you only want some sizes then you could pass the sizes as a delimited string , split them in the procedure in a loop which would include an insert statement. You will find examples of how split strings if you google mysql split string.

Select Next 10 Characters Following Specific String

I'm trying to figure out how to create a single MySQL query that will allow me to display only the next 10 characters following the string "filter" in the Message field. The string "filter" appears at various positions in each record, so I can't use a position filter.
I've been trying to use something like like what I have below, however I've been unable to get the correct query.
SELECT RIGHT(Message,LOCATE('filter',Message) - 10) FROM table
The Message field records within the table looks like:
QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D
6t2fJw.filter0010p3las1-9745-59
filter0025p3las1-13130-59C3D317
And I'm looking for them to look like this after the query:
0019p3las1
0010p3las1
0025p3las1
Any help is greatly appreciated.
Use a combination of LOCATE() within SUBSTRING(). See this SQL Fiddle
CREATE TABLE Table1
(`message` varchar(200))
;
INSERT INTO Table1
(`message`)
VALUES
('QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D'),
('6t2fJw.filter0010p3las1-9745-59'),
('filter0025p3las1-13130-59C3D317')
;
Query 1:
select
SUBSTRING(message,LOCATE('filter',Message)+6,10)
from table1
Note that the +6 is to offset for the length of "filter" because LOCATE finds the position of the "f" and you then need to add 6 for the other characters "ilter". Once that number is determined then just get the next 10 characters.
Results:
| SUBSTRING(message,LOCATE('filter',Message)+6,10) |
|--------------------------------------------------|
| 0019p3las1 |
| 0010p3las1 |
| 0025p3las1 |
See SQLFiddle.
Result table structure
Create table resulttbl (
id int(6) primary key auto_increment ,
resultFIlter varchar(1000)
);
Function to split string
CREATE FUNCTION strSplit(x VARCHAR(65000), delim VARCHAR(12), pos INTEGER)
RETURNS VARCHAR(65000)
BEGIN
DECLARE output VARCHAR(65000);
SET output = REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos)
, LENGTH(SUBSTRING_INDEX(x, delim, pos - 1)) + 1)
, delim
, '');
IF output = '' THEN SET output = null; END IF;
RETURN output;
END;
Stored procedure to split and insert into result table
CREATE PROCEDURE FilterTable()
BEGIN
DECLARE i INTEGER;
DECLARE endpos INTEGER;
DECLARE fullstr VARCHAR(1000);
DECLARE result VARCHAR(1000);
SET fullstr = 'QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D 6t2fJw.filter0010p3las1-9745-59 filter0025p3las1-13130-59C3D317';
SET i = 2;
SET endpos=LENGTH(fullstr) - LENGTH(REPLACE(fullstr, 'filter', '')) ;
delete from resulttbl;
REPEAT
SET result=strSplit(fullstr, 'filter', i);
IF result IS NOT NULL THEN
SET result=LEFT(result,10);
INSERT INTO resulttbl (resultFIlter) values(result);
END IF;
SET i = i + 1;
UNTIL i >= endpos
END REPEAT;
END ;
Call the procedure using the statement CALL FilterTable().
Now the result of your procedure is available on the table resulttbl.
You can get the values from that table using select statement as SELECT * from resulttbl.
Result
id resultFIlter
1 0019p3las1
2 0010p3las1
3 0025p3las1

Stored Procedure updates two rows

DELIMITER //
CREATE OR REPLACE PROCEDURE GET_USER_PNTS(USER_ID INT , PNTS INT, QNT INT)
BEGIN
DECLARE x INT DEFAULT 1;
DECLARE TEMP_GIFT_ID INT;
UPDATE USR_PNT_SUMM SET USD_PNTS = USD_PNTS + PNTS WHERE USER_ID = 1;
COMMIT;
END //
DELIMITER ;
The above stored procedure updates two rows - one for user_id = 1 and the other one for userid 0. I dont understand why!
This is how I call the stored procedure -
CALL GET_USER_PNTS(1, 1, 1)
Please let me know why the user_id 0 is also getting updated.
P.S
1. I am using MariaDB.
2. UserID 0 is what I had manually added in the table. In pratice there won't be any 0 user_id. But even then, the row should not have been updated.
Please rename your parameters:
CREATE OR REPLACE PROCEDURE GET_USER_PNTS(L_USER_ID INT , L_PNTS INT, L_QNT INT)
BEGIN
DECLARE x INT DEFAULT 1;
DECLARE TEMP_GIFT_ID INT;
UPDATE USR_PNT_SUMM SET USD_PNTS = USD_PNTS + L_PNTS WHERE USER_ID = L_USER_ID;
COMMIT;
END //
Probably USER_ID = USER_ID is treated as true.

mysql how to update a column of all rows and for each row a different column value

Here is a sample table:
id name code
----------------
1 n1
2 n2
3 n3
I want to update the code column of every row with different values, so for row of id 1 i want to add this value for code 'zb6DXBfJ', and for row id 2 'NV6Nx4St', and for row id 3 this value for code column 'q23ZMACc'. So my final table should look like this:
id name code
----------------
1 n1 zb6DXBfJ
2 n2 NV6Nx4St
3 n3 q23ZMACc
UPDATE TableName
SET Code = CASE
WHEN id = 1 THEN 'zb6DXBfJ'
WHEN id = 2 THEN 'NV6Nx4St'
WHEN id = 3 THEN 'q23ZMACc'
END;
Try this
UPDATE Table_Name WHERE id = desired_id SET code = desired_code;
Of course, you'll need to substitute Table_Name, desired_id, and desired_code as required.
Depending on where your codes come from, you can try one of the folowing:
If your codes came from another table, you can create a procedure that will "match" each line of the two table in order to update the codes. Here's an example :
First create the tables (the one you already have, and the one with the codes)
CREATE TABLE table1 (
id INT(6) UNSIGNED PRIMARY KEY,
name VARCHAR(300) NOT NULL,
code VARCHAR(300));
CREATE TABLE table2 (
id INT(6) UNSIGNED PRIMARY KEY,
code VARCHAR(300) NOT NULL);
INSERT INTO table1 (id, name) VALUES
(1, 'n1'),
(2, 'n2'),
(3, 'n3');
INSERT INTO table2 (id, code) VALUES
(1, 'zb6DXBfJ'),
(2, 'NV6Nx4St'),
(3, 'q23ZMACc');
Then create the actual procedure
delimiter //
CREATE PROCEDURE assign_strings()
BEGIN
DECLARE _id INT DEFAULT 0;
DECLARE str VARCHAR(300);
DECLARE cur CURSOR FOR SELECT id FROM table1;
open cur;
myloop:LOOP
fetch cur into _id;
SELECT code INTO str FROM table2 WHERE id = _id;
UPDATE table1 SET code = str WHERE id = _id;
end loop myloop;
close cur;
END //
delimiter ;
You can now call the procedure
CALL assign_strings();
Note that I don't know your logic to retrieve these code. Here I just assume table2.id has the code for table1.id. Its a little dumb but your logic may be more complicated.
If your codes are just random strings (non-unique) you can just use a function instead of a procedure like this :
DELIMITER //
CREATE FUNCTION get_random_string()
RETURNS VARCHAR(300)
BEGIN
RETURN 'Your_random_string';
END//
DELIMITER ;
Note that you'll need to implement your own random string strategy. You can use something like a MD5 function with a random number and substrings... whatever you need.
You can now call this function directly in an update statement like so :
UPDATE table1 set code = get_random_string();
Hope it gets you started.

SQL finding ancestors/decendants in a self-referencing table

I have a table which references itself, like this:
CREATE TABLE Foo (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
parent INT NULL,
name VARCHAR (30) NOT NULL,
FOREIGN KEY (parent) REFERENCES Foo(id) ON DELETE CASCADE);
Sample data:
id parent name
1 NULL a
2 NULL b
3 1 a1
4 1 a2
5 3 a1x
6 3 a2x
I want to write queries which will list the ancestors and decenders of a given row, e.g.
CALL find_ancestors('a1x')
Will return
id name
3 a1
1 a
and
CALL find_descendants('a')
Will return
id name
3 a1
5 a1x
How can I write these stored procedures for MySQL 5? Thanks
Bonus question for bounty: also select the distance of the returned row from the source and pass a maximum-distance parameter to the procedure, e.g.
CALL find_ancestors('a1x')
Will return
id name distance
3 a1 1
1 a 2
and
CALL find_ancestors_bounded('a1x',1)
Will return
id name distance
3 a1 1
Lets say we have a table with four elements, id, item, class and parent_id. We want to have the complete Ancestors of any given Item, what we need to do is a custom mysql function that will actually loop through every record looking for a match for our item parent_id, once it founds a match, if the matched item has a parent_id, it will start looping again, and so forth. Every time our function finds a match, it will store it in a comma separated string that will be returned in the end (ex: 1,2,3,4)
Our function would look something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetAncestry` $$
CREATE FUNCTION `junk`.`GetAncestry` (GivenID INT) RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
DECLARE rv VARCHAR(1024);
DECLARE cm CHAR(1);
DECLARE ch INT;
SET rv = '';
SET cm = '';
SET ch = GivenID;
WHILE ch > 0 DO
SELECT IFNULL(parent_id,-1) INTO ch FROM
(SELECT parent_id FROM pctable WHERE id = ch) A;
IF ch > 0 THEN
SET rv = CONCAT(rv,cm,ch);
SET cm = ',';
END IF;
END WHILE;
RETURN rv;
END $$
DELIMITER ;
This code is authored by RolandoMySQLDBA