I have a query like
Select id,name,... FROM table WHERE id IN (?)
The query is actually more complicated with joins and another (?) with the same ids. I've been told to put it on a stored procedure to make it easier to manage and safer, but mysql doesn't support arrays, so I'm not sure what is the best way to do it, considering that I don't know how many id I will have to pass. I have seen people passing a string. Is it the only way?
You can use temporary tables. Though this answer is not highly voted so I suppose that using a string parameter is the prefered way (17 votes vs 2 votes) (see the link). The answer with temporary table is taken from another question: Pass array to MySQL stored routine
Use a join with a temporary table. You don't need to pass temporary
tables to functions, they are global.
create temporary table ids( id int ) ;
insert into ids values (1),(2),(3) ;
delimiter //
drop procedure if exists tsel //
create procedure tsel() -- uses temporary table named ids. no params
READS SQL DATA
BEGIN
-- use the temporary table `ids` in the SELECT statement or
-- whatever query you have
select * from Users INNER JOIN ids on userId=ids.id ;
END //
DELIMITER ;
CALL tsel() ; -- call the procedure
Related
I want to create an trigger inside an procedure. but after some research I got to know that it is not possible. can u suggest me another way I can achieve the below actions. (I cant share exact data and queries due to some reason. please refer similar queries.)
What I want
I have created an temporary table containing data i need.
eg. CREATE TEMPORARY TABLE temp1 SELECT id, col_1 FROM table1 WHERE col_1=2;
I want to inset data in table table2 when data is inserted in temp1, which i can achieve by creating a TRIGGER. but the problem is I want to give a value in table2 which will be dynamic and will be taken from nodejs backend. so i created a PROCEDURE which takes parameter neededId. but i cant created trigger inside a procedure. is their any other way i can achieve this?
Procedure I Created
here neededId is the foreign key I get from backend to insert
DELIMITER $$
USE `DB`$$
CREATE PROCEDURE `MyProcedure` (IN neededID int)
BEGIN
DROP TABLE IF EXISTS temp1;
CREATE TEMPORARY TABLE temp1 SELECT id, col_1 FROM table1 WHERE col_1=2;
DROP TRIGGER IF EXISTS myTrigger;
CREATE TRIGGER myTrigger AFTER INSERT ON temp1 FOR EACH ROW
BEGIN
INSERT into table2("value1", "value2", neededId);
END;
END$$
DELIMITER ;
SQL Statements Not Permitted in Stored Routines
Generally, statements not permitted in SQL prepared statements are also not permitted in stored programs. ... Exceptions are SIGNAL, RESIGNAL, and GET DIAGNOSTICS, which are not permissible as prepared statements but are permitted in stored programs.
SQL Syntax Permitted in Prepared Statements
CREATE TRIGGER is not listed.
Finally: the trigger cannot be created in stored procedure, function, prepared statement, trigger or event procedure.
In MySQL, you can't create a trigger for a temporary table, regardless of whether you do it in a stored procedure or not.
https://dev.mysql.com/doc/refman/8.0/en/create-trigger.html says:
The trigger becomes associated with the table named tbl_name, which must refer to a permanent table. You cannot associate a trigger with a TEMPORARY table or a view.
I assume the same is true of MariaDB. It's not clear from your question which one you use. You say MySQL at first, but you tagged the question mariadb. Be aware that these are not the same database product, and they are not necessarily compatible.
Here's a demo of the error, tested on MySQL 8.0.28:
mysql> create temporary table t ( i int );
mysql> create trigger t before insert on t for each row set NEW.i = 42;
ERROR 1361 (HY000): Trigger's 't' is view or temporary table
So in your case, you cannot use a trigger for the temporary table. You'll have to think of a different way to implement inserts to your second table.
I have a stored procedure, internally I want to call another procedure that returns a record set, how do I get an navigate the record set returned by the stored procedure via the 'CALL' ?
[edit] I've been trying to use a TEMPORARY TABLE as suggested, but having problems:
DROP TEMPORARY TABLE IF EXISTS tbl_HeadOfDepts;
CREATE TEMPORARY TABLE tbl_HeadOfDepts (biDept_id tinyint(4))
INSERT INTO tbl_HeadOfDepts CALL rsHeadOfAnyDepartments(vcCompKey, biWho_id);
I need to use CALL because 'rsHeadOfAnyDepartments' is not a function, but this will not be accepted.
Work in progress, but what I have so far that is not accepted by editor:
BEGIN
#--
# Procedure:
# rsWhoCanIaccess
#
# Parameters:
# vcCompKey, the key corresponding to the company
# biWho_id, the id of the person to check access for
#
# Returns:
# recordset containing all the people this person can access
#--
DECLARE tiSuperUser tinyint(4);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 #sqlstate = RETURNED_SQLSTATE,
#errno = MYSQL_ERRNO, #text = MESSAGE_TEXT;
CALL procLogError(vcCompKey, CONCAT("rsWhoCanIaccess: "
,#errno, " (", #sqlstate, "): ", #text));
END;
#Is this user a super user?
SELECT tiIsSuperUser(vcCompKey, biWho_id) INTO tiSuperUser;
SET tiSuperUser = 0;#Hack for testing
IF (tiSuperUser = 1) THEN
#The user is a superuser, return everyone in the company
SELECT
t1.biPerson_id
FROM
tbl_people t1
INNER JOIN
tbl_companies t2
ON
t1.biCompany_id=t2.biCompany_id
AND
t2.vcKey=vcCompKey;
ELSE
#User is not a superuser, is the user head of any departments?
DROP TEMPORARY TABLE IF EXISTS tbl_HeadOfDepts;
CREATE TEMPORARY TABLE tbl_HeadOfDepts (biDept_id tinyint(4))
INSERT INTO tbl_HeadOfDepts CALL rsHeadOfAnyDepartments(vcCompKey, biWho_id);
SELECT * FROM tbl_HeadOfDepts;
END IF;
END
No, stored procedures can produce result sets, but not consume them directly as output from inner calls to other stored procedures. The best that you can do performance-wise is to populate a non-temporary work table and use the results.
Depending on your software and the reality of multiple callers concurrently, you might need to include a session id concept with an auto_increment (AI) column in some control table. This would ensure that with concurrency, multiple callers are not stomping on each other's rows, thus making it non-viable.
How that session would work, at a high level, is the following. The inner stored proc would be handed an AI value (theSession) from the control table, use it to populate a safely segmented session in the work table, and return as an out parameter to the outer (calling) stored proc. That outer one could then safely use those rows, and clean up at the end (delete from workTable where sessionId=theSession).
Why do I suggest a non-temporary work table? To be clear, the work table would be non-temporary. First of all there is the hassle of getting the if exists drop to work. Most importantly, though, it is about performance. DDL calls for temporary table creation are not inexpensive. You will only believe this when you do performance testing to see what I mean. It may seem trivial, but in trivial operations, those DDL calls for creation could very well account for the lion share of the time necessary for the inner stored proc to complete.
internally I want to call another procedure that returns a record set,
In your inner procedure create a TEMPORARY TABLE and populate that temp table saying insert into your_temp_table select query. then you can use that same temp table in your outer query anywhere.
It can even be a normal table as well and need not be temporary table. Also make sure to DROP the table once your procedure computation done as clean-up.
That's wrong per your comment. You should do it like below (a sample code)
create procedure rsHeadOfAnyDepartments(vcCompKey varchar(10), biWho_id int)
as
begin
DROP TEMPORARY TABLE IF EXISTS tbl_HeadOfDepts;
CREATE TEMPORARY TABLE tbl_HeadOfDepts(col1 int, col2 varchar(10), col3 varchar(30));
INSERT INTO tbl_HeadOfDepts
SELECT col1, col2, col3
FROM tblTest;
end
I'm using MySQL and trying to create a temp table. I will be doing a 2 while loop statements in PHP to populate the temp table. Firstly though I can't seem to get the Insert into temp table to work. I've tried many different versions of this, some using '#' for the table and various things (are there differences in SQL server and MySQL commands?). Here's my last attempt (P.S the Select statement works fine on its own).
CREATE TEMPORARY TABLE temp
(
aID varchar(15) NOT NULL,
bID varchar(15) NOT NULL
)
INSERT INTO temp
SELECT aID, bID
FROM tags
WHERE placeID = "abc" AND tagID = "def";
Help appreciated!
Also, just a general Q...this query will have to be run many times. Will using temp tables be OK or cause the server issues?
working on what Code-Monk wrote, consider the following:
drop procedure if exists uspK;
DELIMITER $$
create procedure uspK ()
BEGIN
drop temporary table if exists temp; -- could be some other random structure residue
create temporary table temp
SELECT aID, bID
FROM tags
WHERE placeID = "abc" AND tagID = "def";
-- use the temp table somehow
-- ...
-- ...
-- ...
drop temporary table temp; -- otherwise it survives the stored proc call
END
$$ -- signify end of block
DELIMITER ; -- reset to default delimiter
Test Stored Procedure
call uspK(); -- test it, no warnings on edge conditions
What not to do
One would not find much luck with the following. If you think so, run it a few times;
drop procedure if exists uspK;
DELIMITER $$
create procedure uspK ()
BEGIN
-- drop temporary table if exists temp;
create temporary table if not exists temp
SELECT aID, bID
FROM tags
WHERE placeID = "abc" AND tagID = "def";
-- use the temp table somehow
-- ...
-- ...
-- ...
-- drop temporary table temp; -- otherwise it survives the stored proc call
END
$$ -- signify end of block
DELIMITER ; -- reset to default delimiter
because create temporary table if not exists temp is flakey
General Comments
One should not embark into writing stored procs until somewhat fluent on the simple topic of DELIMITERS. Wrote about them in a section here called Delimiters. Just hoping to head you off from unnecessary wasted time on such a simple thing, than can waste a lot of debugging time.
Also, here in your question, as well as in that reference, keep in mind that the creation of tables is DDL that can have a large percentage of the overall profiling (performance). It slows down a proc versus using a pre-existing table. One might think the call is instantaneous, but it is not. As such, for performance, using a pre-existing table with ones results put into their own segmented rowId is much faster than enduring DDL overhead.
You can create temporary table and insert select statemet in following way:
create temporary table temp
SELECT aID, bID
FROM tags
WHERE placeID = "abc" AND tagID = "def";
To drop the temporary table before creating it again. put following statement before creating temporary table:
drop temporary table if exists temp;
Note: It will be good if you can put all this code in stored procedure. and call it to create temporary table.
I am looking for Sql-Server relevant feature in MySql
Table variable inside procedure. here
Temporary Table inside procedure. here
I hope the table variables are in-memory and temporary tables are stored in temp database.
If both declared inside procedure, both will be cleaned after the procedure finished its execution.
MySql provides only one option called Temporary Table. How to achieve these two? and do we need to clear those tables or MySql will take care of it after the procedure completed?
From MySQL documentation:
You can use the TEMPORARY keyword when creating a table. A TEMPORARY table is visible only to the current session, and is dropped automatically when the session is closed. This means that two different sessions can use the same temporary table name without conflicting with each other or with an existing non-TEMPORARY table of the same name.
More info: http://dev.mysql.com/doc/refman/5.1/en/create-table.html#create-temporary-table
But what happens if I'm using a single connection? May be you will have some data conflicts. In the past I use temporary tables with a unique id to avoid conflicts if I call the procedure with the same connection more than one time:
DELIMITER $$
DROP PROCEDURE some_proc $$
CREATE PROCEDURE some_proc ()
BEGIN
-- creating a unique value
DECLARE VARIABLE tmp_uuid varchar(50);
SET tmp_uuid = uuid();
CREATE TEMPORARY TABLE IF NOT EXISTS tbl_temporary
(
uuid_id varchar(50),
col1 varchar(10),
col2 varchar(10),
INDEX(uuid_id)
);
-- do some operations on that
INSERT INTO tbl_temporary(uuid_id, ...) VALUES (tmp_uuid,...);
-- now, cleaning the data
DELETE FROM tbl_temporary WHERE uuid_id = tmp_uuid;
-- if the current session is closed the table will be automatically deleted
END$$
DELIMITER ;
Background - I have a DB created from a single large flat file. Instead of creating a single large table with 106 columns. I created a "columns" table which stores the column names and the id of the table that holds that data, plus 106 other tables to store the data for each column. Since not all the records have data in all columns, I thought this might be a more efficient way to load the data (maybe a bad idea).
The difficulty with this was rebuilding a single record from this structure. To facilitate this I created the following procedure:
DROP PROCEDURE IF EXISTS `col_val`;
delimiter $$
CREATE PROCEDURE `col_val`(IN id INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp_record;
CREATE TEMPORARY TABLE tmp_record (id INT(11), val varchar(100)) ENGINE=MEMORY;
SET #ctr = 1;
SET #valsql = '';
WHILE (#ctr < 107) DO
SET #valsql = CONCAT('INSERT INTO tmp_record SELECT ',#ctr,', value FROM col',#ctr,' WHERE recordID = ',#id,';');
PREPARE s1 FROM #valsql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SET #ctr = #ctr+1;
END WHILE;
END$$
DELIMITER ;
Then I use the following SQL where the stored procedure parameter is the id of the record I want.
CALL col_val(10);
SELECT c.`name`, t.`val`
FROM `columns` c INNER JOIN tmp_record t ON c.ID = t.id
Problem - The first time I run this it works great. However, each subsequent run returns the exact same record even though the parameter is changed. How does this persist even when the stored procedure should be dropping and re-creating the temp table?
I might be re-thinking the whole design and going back to a single table, but the problem illustrates something I would like to understand.
Unsure if it matters but I'm running MySQL 5.6 (64 bit) on Windows 7 and executing the SQL via MySQL Workbench v5.2.47 CE.
Thanks,
In MySQL stored procedures, don't put an # symbol in front of local variables (input parameters or locally declared variables). The #id you used refers to a user variable, which is kind of like a global variable for the session you're invoking the procedure from.
In other words, #id is a different variable from id.
That's the explanation of the immediate problem you're having. However, I would not design the tables as you have done.
Since not all the records have data in all columns, I thought this might be a more efficient way to load the data
I recommend using a conventional single table, and use NULL to signify missing data.