How create a Stored procedure that will accept a parameter - mysql

I have a "set" of SQL statements
DROP TABLE IF EXISTS data.s;
CREATE TABLE data.s LIKE data._style;
INSERT INTO data.s Values (?,?,?,?,?,?,?,?,?,?,?,?,?);
UPDATE data.s n JOIN data.s_ o ON n.ID = o.ID SET n.TC = o.TC;
UPDATE data.s n JOIN data.s_ o ON n.ID = o.ID SET n.VR = o.VR;
UPDATE data.s n JOIN data.o_ o ON n.ID = o.ID SET n.OC = o.OC;
DELETE FROM data.s WHERE TC <= 0;
DELETE FROM data.s WHERE TC < 100;
DELETE FROM data.s WHERE OC < 100 ;
Using "s" table as example, How would I create a SP where "s" is a variable, which could be replace with t, u v, z...... whatever? I would like to change this variable with a SQL call statement.

MySQL does not handle real dynamic SQL, so you have to use prepared statement.
Look at the accepted answer : How To have Dynamic SQL in MySQL Stored Procedure and especially the link he gives (Dynamic SQL part).
Something like :
CREATE PROCEDURE `execute`(IN sqlQuery varchar(255))
BEGIN
set #sqlQuery := sqlQuery;
prepare stmp from #sqlQuery;
execute stmp;
deallocate prepare stmp;
END
CREATE PROCEDURE `yourProcName`(IN tableName varchar(50))
BEGIN
call execute(concat('DROP TABLE IF EXISTS ', tableName));
call execute(concat('CREATE TABLE ', tableName, ' LIKE data._style'));
...
END

Related

how can I limit my query with the number that my variable _Limite passes?

I am trying to create this procedure stored in my MySql version 5.0 database but it does not allow me to create it.
the error is in "LIMIT _Limite" apparently this version does not accept this sentence.
how can I limit my query with the number that my variable _Limite passes?
BEGIN
SELECT p.Id,
Peso
FROM pacas p
INNER JOIN entradas e ON EntradaFK = e.Id
WHERE FibraFK = _FibraFK
AND PresentacionFK = _PresentacionFK
AND PatioFK = _Patio
AND e.Fecha <= _Fecha
AND e.Estado = 'A'
AND p.Estado = 'A'
ORDER BY Id
LIMIT _Limite;
END
You can achieve this by using prepared statements.
SET _Limite := 1;
CALL statement(CONCAT('
SELECT p.Id,
Peso
FROM pacas p
INNER JOIN entradas e ON EntradaFK = e.Id
WHERE FibraFK = _FibraFK
AND PresentacionFK = _PresentacionFK
AND PatioFK = _Patio
AND e.Fecha <= _Fecha
AND e.Estado = \'A\'
AND p.Estado = \'A\'
ORDER BY Id
LIMIT ', _Limite, ';
'))
;
prepared statement helper implementation:
DELIMITER $$
CREATE PROCEDURE statement(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END;
DELIMITER ;
I think it will be better to do it by direct consultation and not by stored procedure, since this version does not allow me to do it

MYSQL Stored Procedure only updates on record

MYSQL Stored Procedure only updates one record when it should update all the ids in the WHERE id IN clause. When I run the SELECT and UPDATE outside of the stored procedure it works fine. Any suggestions?
DELIMITER $$
CREATE PROCEDURE update_ids(IN source int(10),IN target int(10))
BEGIN
DECLARE idList varchar(5000) DEFAULT NULL;
SET idList = (SELECT GROUP_CONCAT(id SEPARATOR ', ') FROM myTable ii WHERE ii.generic_id = source);
UPDATE myTable i SET i.generic_id = target WHERE i.id IN (idList);
END$$
DELIMITER ;
Then I call it by -->
CALL update_generic_ids(63, 1258);
Update : 1 row effected.
Thanks in advance,
once try below chunk of code, hope it will solve your problems!
If any question reach me!
DELIMITER $$
CREATE PROCEDURE update_ids(IN source int(10),IN target int(10))
BEGIN
DECLARE idList varchar(5000) DEFAULT NULL;
--SET idList = (SELECT GROUP_CONCAT(id SEPARATOR ', ') FROM myTable ii WHERE ii.generic_id = source);
UPDATE myTable i SET i.generic_id = target WHERE i.id IN (
SELECT id FROM myTable ii WHERE ii.generic_id = source
);
END$$
DELIMITER ;
Actually, as per my comments, I think is may be the answer..
There's a world of difference between these two queries:
UPDATE a SET b = c WHERE d IN ('1,2,3,4')
UPDATE a SET b = c WHERE d IN (1,2,3,4)
I'd recommend you amend your procedure so you're not storing your list of ids in a string variable. Actually you shouldn't need to store anything, just put the list generated by the select into the update:
UPDATE myTable i SET i.generic_id = target
WHERE i.id IN
(SELECT id SEPARATOR FROM myTable ii WHERE ii.generic_id = source);

if condition inside a sql query

following is a part of my stored proceedure im using it to extract data from my db.
query
BEGIN
SET #sqlstring = CONCAT("SELECT b.ID, c.name, c.accountID,, b.total_logs, a.time_start, a.time_end ,COUNT(a.id) AS number_of_users
FROM ",logtable," a INNER JOIN users b on a.ID = b.ID INNER JOIN accounts c on b.accountID = c.accountID
GROUP BY ID;");
PREPARE stmt FROM #sqlstring;
EXECUTE stmt;
END
At times in the db, the logtable(table is passed in a variable like logtable_1, logtable_2 .... ) can be non existent, currently when the perticuler table is missing it crashes and throws an error because a.time_start, a.time_end cannot have values without the log table.
but what i want is just to assign NULL on values a.time_start, a.time_end without throwing an error,
So can any body tell is there a way i could modify this code like
BEGIN
if logtable exists
\\ the query
else
\\ the query
END
Find existence of the table by querying information_schema.tables. If it returns a count equals to 1 then you can proceed executing your query on the table. Otherwise go with your Else block.
Sample:
declare table_exists int default 0;
select count(1) into table_exists
from information_schema.tables
where table_schema='your_table_schema_name'
and table_name = 'your_table_name';
if table_exists then
-- do something
else
-- do something else
end if;

Change schema of multiple PostgreSQL functions in one operation?

Recently I needed to move objects from PostgreSQL's default schema "public" to another schema. I found this post which shows how to move tables which was great, but I also need to move the functions.
You could refine the loop some more (demonstrating only the second query):
DO
$do$
DECLARE
r record;
sql text = '';
BEGIN
FOR r IN
SELECT p.proname, pg_get_function_identity_arguments(p.oid) AS params
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE nspname = 'public'
-- and other conditions, if needed
LOOP
sql := sql
|| format(E'\nALTER FUNCTION public.%I(%s) SET SCHEMA new_schema;'
,r.proname, r.params);
END LOOP;
RAISE NOTICE '%', sql; -- for viewing the sql before executing it
-- EXECUTE sql; -- for executing the sql
END
$do$;
Major points
Assignment operator in plpgsql is :=. = works, but is undocumented.
Remove unneeded tables from FROM.
concat() may be overkill, but format() simplifies the syntax.
Better set-based alternative
Re-casting the problem as set-based operation is more effective. One SELECT with string_agg() does the job:
DO
$do$
DECLARE
sql text;
BEGIN
SELECT INTO sql
string_agg(format('ALTER FUNCTION public.%I(%s) SET SCHEMA new_schema;'
,p.proname, pg_get_function_identity_arguments(p.oid)), E'\n')
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE nspname = 'public';
-- and other conditions, if needed
RAISE NOTICE '%', sql; -- for viewing the sql before executing it
-- EXECUTE sql; -- for executing the sql
END
$do$;
DO$$
DECLARE
row record;
BEGIN
FOR row IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' -- and other conditions, if needed
LOOP
EXECUTE 'ALTER TABLE public.' || quote_ident(row.tablename) || ' SET SCHEMA [new_schema];';
END LOOP;
END;
$$;
DO$$
DECLARE
row record;
sql text = E'\n';
BEGIN
FOR row IN
SELECT
proname::text as proname,
pg_get_function_identity_arguments(p.oid) AS params
FROM pg_proc p
JOIN pg_namespace n on n.oid = p.pronamespace
WHERE nspname = 'public'
-- and other conditions, if needed
LOOP
sql = CONCAT(sql, E'\n',
'ALTER FUNCTION public.', row.proname,
'(', row.params, ') SET SCHEMA [new_schema];');
END LOOP;
RAISE NOTICE '%', sql; -- for viewing the sql before executing it
-- EXECUTE sql; -- for executing the sql
END;$$;

How do you use the "WITH" clause in MySQL?

I am converting all my SQL Server queries to MySQL and my queries that have WITH in them are all failing. Here's an example:
WITH t1 AS
(
SELECT article.*, userinfo.*, category.*
FROM question
INNER JOIN userinfo ON userinfo.user_userid = article.article_ownerid
INNER JOIN category ON article.article_categoryid = category.catid
WHERE article.article_isdeleted = 0
)
SELECT t1.*
FROM t1
ORDER BY t1.article_date DESC
LIMIT 1, 3
MySQL prior to version 8.0 doesn't support the WITH clause (CTE in SQL Server parlance; Subquery Factoring in Oracle), so you are left with using:
TEMPORARY tables
DERIVED tables
inline views (effectively what the WITH clause represents - they are interchangeable)
The request for the feature dates back to 2006.
As mentioned, you provided a poor example - there's no need to perform a subselect if you aren't altering the output of the columns in any way:
SELECT *
FROM ARTICLE t
JOIN USERINFO ui ON ui.user_userid = t.article_ownerid
JOIN CATEGORY c ON c.catid = t.article_categoryid
WHERE t.published_ind = 0
ORDER BY t.article_date DESC
LIMIT 1, 3
Here's a better example:
SELECT t.name,
t.num
FROM TABLE t
JOIN (SELECT c.id
COUNT(*) 'num'
FROM TABLE c
WHERE c.column = 'a'
GROUP BY c.id) ta ON ta.id = t.id
Mysql Developers Team announced that version 8.0 will have Common Table Expressions in MySQL (CTEs). So it will be possible to write queries like this:
WITH RECURSIVE my_cte AS
(
SELECT 1 AS n
UNION ALL
SELECT 1+n FROM my_cte WHERE n<10
)
SELECT * FROM my_cte;
+------+
| n |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+------+
10 rows in set (0,00 sec)
In Sql the with statement specifies a temporary named result set, known as a common table expression (CTE). It can be used for recursive queries, but in this case, it specifies as subset. If mysql allows for subselectes i would try
select t1.*
from (
SELECT article.*,
userinfo.*,
category.*
FROM question INNER JOIN
userinfo ON userinfo.user_userid=article.article_ownerid INNER JOIN category ON article.article_categoryid=category.catid
WHERE article.article_isdeleted = 0
) t1
ORDER BY t1.article_date DESC Limit 1, 3
I followed the link shared by lisachenko and found another link to this blog:
http://guilhembichot.blogspot.co.uk/2013/11/with-recursive-and-mysql.html
The post lays out ways of emulating the 2 uses of SQL WITH. Really good explanation on how these work to do a similar query as SQL WITH.
1) Use WITH so you don't have to perform the same sub query multiple times
CREATE VIEW D AS (SELECT YEAR, SUM(SALES) AS S FROM T1 GROUP BY YEAR);
SELECT D1.YEAR, (CASE WHEN D1.S>D2.S THEN 'INCREASE' ELSE 'DECREASE' END) AS TREND
FROM
D AS D1,
D AS D2
WHERE D1.YEAR = D2.YEAR-1;
DROP VIEW D;
2) Recursive queries can be done with a stored procedure that makes the call similar to a recursive with query.
CALL WITH_EMULATOR(
"EMPLOYEES_EXTENDED",
"
SELECT ID, NAME, MANAGER_ID, 0 AS REPORTS
FROM EMPLOYEES
WHERE ID NOT IN (SELECT MANAGER_ID FROM EMPLOYEES WHERE MANAGER_ID IS NOT NULL)
",
"
SELECT M.ID, M.NAME, M.MANAGER_ID, SUM(1+E.REPORTS) AS REPORTS
FROM EMPLOYEES M JOIN EMPLOYEES_EXTENDED E ON M.ID=E.MANAGER_ID
GROUP BY M.ID, M.NAME, M.MANAGER_ID
",
"SELECT * FROM EMPLOYEES_EXTENDED",
0,
""
);
And this is the code or the stored procedure
# Usage: the standard syntax:
# WITH RECURSIVE recursive_table AS
# (initial_SELECT
# UNION ALL
# recursive_SELECT)
# final_SELECT;
# should be translated by you to
# CALL WITH_EMULATOR(recursive_table, initial_SELECT, recursive_SELECT,
# final_SELECT, 0, "").
# ALGORITHM:
# 1) we have an initial table T0 (actual name is an argument
# "recursive_table"), we fill it with result of initial_SELECT.
# 2) We have a union table U, initially empty.
# 3) Loop:
# add rows of T0 to U,
# run recursive_SELECT based on T0 and put result into table T1,
# if T1 is empty
# then leave loop,
# else swap T0 and T1 (renaming) and empty T1
# 4) Drop T0, T1
# 5) Rename U to T0
# 6) run final select, send relult to client
# This is for *one* recursive table.
# It would be possible to write a SP creating multiple recursive tables.
delimiter |
CREATE PROCEDURE WITH_EMULATOR(
recursive_table varchar(100), # name of recursive table
initial_SELECT varchar(65530), # seed a.k.a. anchor
recursive_SELECT varchar(65530), # recursive member
final_SELECT varchar(65530), # final SELECT on UNION result
max_recursion int unsigned, # safety against infinite loop, use 0 for default
create_table_options varchar(65530) # you can add CREATE-TABLE-time options
# to your recursive_table, to speed up initial/recursive/final SELECTs; example:
# "(KEY(some_column)) ENGINE=MEMORY"
)
BEGIN
declare new_rows int unsigned;
declare show_progress int default 0; # set to 1 to trace/debug execution
declare recursive_table_next varchar(120);
declare recursive_table_union varchar(120);
declare recursive_table_tmp varchar(120);
set recursive_table_next = concat(recursive_table, "_next");
set recursive_table_union = concat(recursive_table, "_union");
set recursive_table_tmp = concat(recursive_table, "_tmp");
# Cleanup any previous failed runs
SET #str =
CONCAT("DROP TEMPORARY TABLE IF EXISTS ", recursive_table, ",",
recursive_table_next, ",", recursive_table_union,
",", recursive_table_tmp);
PREPARE stmt FROM #str;
EXECUTE stmt;
# If you need to reference recursive_table more than
# once in recursive_SELECT, remove the TEMPORARY word.
SET #str = # create and fill T0
CONCAT("CREATE TEMPORARY TABLE ", recursive_table, " ",
create_table_options, " AS ", initial_SELECT);
PREPARE stmt FROM #str;
EXECUTE stmt;
SET #str = # create U
CONCAT("CREATE TEMPORARY TABLE ", recursive_table_union, " LIKE ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
SET #str = # create T1
CONCAT("CREATE TEMPORARY TABLE ", recursive_table_next, " LIKE ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
if max_recursion = 0 then
set max_recursion = 100; # a default to protect the innocent
end if;
recursion: repeat
# add T0 to U (this is always UNION ALL)
SET #str =
CONCAT("INSERT INTO ", recursive_table_union, " SELECT * FROM ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
# we are done if max depth reached
set max_recursion = max_recursion - 1;
if not max_recursion then
if show_progress then
select concat("max recursion exceeded");
end if;
leave recursion;
end if;
# fill T1 by applying the recursive SELECT on T0
SET #str =
CONCAT("INSERT INTO ", recursive_table_next, " ", recursive_SELECT);
PREPARE stmt FROM #str;
EXECUTE stmt;
# we are done if no rows in T1
select row_count() into new_rows;
if show_progress then
select concat(new_rows, " new rows found");
end if;
if not new_rows then
leave recursion;
end if;
# Prepare next iteration:
# T1 becomes T0, to be the source of next run of recursive_SELECT,
# T0 is recycled to be T1.
SET #str =
CONCAT("ALTER TABLE ", recursive_table, " RENAME ", recursive_table_tmp);
PREPARE stmt FROM #str;
EXECUTE stmt;
# we use ALTER TABLE RENAME because RENAME TABLE does not support temp tables
SET #str =
CONCAT("ALTER TABLE ", recursive_table_next, " RENAME ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
SET #str =
CONCAT("ALTER TABLE ", recursive_table_tmp, " RENAME ", recursive_table_next);
PREPARE stmt FROM #str;
EXECUTE stmt;
# empty T1
SET #str =
CONCAT("TRUNCATE TABLE ", recursive_table_next);
PREPARE stmt FROM #str;
EXECUTE stmt;
until 0 end repeat;
# eliminate T0 and T1
SET #str =
CONCAT("DROP TEMPORARY TABLE ", recursive_table_next, ", ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
# Final (output) SELECT uses recursive_table name
SET #str =
CONCAT("ALTER TABLE ", recursive_table_union, " RENAME ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
# Run final SELECT on UNION
SET #str = final_SELECT;
PREPARE stmt FROM #str;
EXECUTE stmt;
# No temporary tables may survive:
SET #str =
CONCAT("DROP TEMPORARY TABLE ", recursive_table);
PREPARE stmt FROM #str;
EXECUTE stmt;
# We are done :-)
END|
delimiter ;
'Common Table Expression' feature is not available in MySQL, so you have to go to make a view or temporary table to solve, here I have used a temporary table.
The stored procedure mentioned here will solve your need. If I want to get all my team members and their associated members, this stored procedure will help:
----------------------------------
user_id | team_id
----------------------------------
admin | NULL
ramu | admin
suresh | admin
kumar | ramu
mahesh | ramu
randiv | suresh
-----------------------------------
Code:
DROP PROCEDURE `user_hier`//
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_hier`(in team_id varchar(50))
BEGIN
declare count int;
declare tmp_team_id varchar(50);
CREATE TEMPORARY TABLE res_hier(user_id varchar(50),team_id varchar(50))engine=memory;
CREATE TEMPORARY TABLE tmp_hier(user_id varchar(50),team_id varchar(50))engine=memory;
set tmp_team_id = team_id;
SELECT COUNT(*) INTO count FROM user_table WHERE user_table.team_id=tmp_team_id;
WHILE count>0 DO
insert into res_hier select user_table.user_id,user_table.team_id from user_table where user_table.team_id=tmp_team_id;
insert into tmp_hier select user_table.user_id,user_table.team_id from user_table where user_table.team_id=tmp_team_id;
select user_id into tmp_team_id from tmp_hier limit 0,1;
select count(*) into count from tmp_hier;
delete from tmp_hier where user_id=tmp_team_id;
end while;
select * from res_hier;
drop temporary table if exists res_hier;
drop temporary table if exists tmp_hier;
end
This can be called using:
mysql>call user_hier ('admin')//
That feature is called a common table expression
http://msdn.microsoft.com/en-us/library/ms190766.aspx
You won't be able to do the exact thing in mySQL, the easiest thing would to probably make a view that mirrors that CTE and just select from the view. You can do it with subqueries, but that will perform really poorly. If you run into any CTEs that do recursion, I don't know how you'd be able to recreate that without using stored procedures.
EDIT:
As I said in my comment, that example you posted has no need for a CTE, so you must have simplified it for the question since it can be just written as
SELECT article.*, userinfo.*, category.* FROM question
INNER JOIN userinfo ON userinfo.user_userid=article.article_ownerid
INNER JOIN category ON article.article_categoryid=category.catid
WHERE article.article_isdeleted = 0
ORDER BY article_date DESC Limit 1, 3
I liked #Brad's answer from this thread, but wanted a way to save the results for further processing (MySql 8):
-- May need to adjust the recursion depth first
SET ##cte_max_recursion_depth = 10000 ; -- permit deeper recursion
-- Some boundaries
set #startDate = '2015-01-01'
, #endDate = '2020-12-31' ;
-- Save it to a table for later use
drop table if exists tmpDates ;
create temporary table tmpDates as -- this has to go _before_ the "with", Duh-oh!
WITH RECURSIVE t as (
select #startDate as dt
UNION
SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= #endDate
)
select * FROM t -- need this to get the "with"'s results as a "result set", into the "create"
;
-- Exists?
select * from tmpDates ;
Which produces:
dt |
----------|
2015-01-01|
2015-01-02|
2015-01-03|
2015-01-04|
2015-01-05|
2015-01-06|