Error when set auto increment in prodceduce - mysql

CREATE DEFINER = 'root'#'localhost'
PROCEDURE client_logging_system.Proc_client_Delete(IN in_clientID int)
COMMENT '
-- Parameter:
-- in_clientID: ID of client
'
BEGIN
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
end;
START TRANSACTION;
DELETE FROM `client` WHERE `client`.ID = in_clientID;
ALTER TABLE `client` AUTO_INCREMENT = in_clientID;
COMMIT;
END
My proceduce get error on line:
ALTER TABLE `client` AUTO_INCREMENT = in_clientID;
Any suggestion for this problem?

You can't use variables in ALTER statements, it needs a literal number there. You'll need to create dynamic SQL using PREPARE.
SET #st = CONCAT('ALTER TABLE `client` AUTO_INCREMENT = ', in_clientID);
PREPARE stmt FROM #st;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

You can only use limited set of DDL statements inside a routine.
What are you trying to achive with the ALTER TABLE-statement? If you delete a client with id that is not the biggest one, the ALTER TABLE would not make sense.

Related

Create user after insert in SQL table

I want to create a new user with the same initial password every time I insert a row with the username in the table.
I tried but it doesn't work:
CREATE TABLE IF NOT EXISTS Professeur (
professeur_id int NOT NULL AUTO_INCREMENT,
prenom varchar(40) COLLATE utf8_bin NOT NULL,
name varchar(30) COLLATE utf8_bin NOT NULL,
titre char DEFAULT NULL,
PRIMARY KEY (professeur_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE trigger trig_prof AFTER INSERT ON Professeur
FOR EACH ROW CREATE OR replace USER NEW.name#localhost identified BY pwd0;
ERROR:
ER_PARSE_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
'USER NEW.name#localhost identified BY pwd0' at line 2
Triggers have several limitations as described in the fine manual:
"Triggers cannot operate on any tables in the mysql, information_schema or performance_schema database."
Since users are stored in mysql schema (either in global_priv or user) this can't work.
Do things a different way...
Write a Stored Procedure that both creates the table and adds the user. See SQL SECURITY DEFINER for how to temporarily give an enduser root permission. But be aware of the security implications.
Then, to add the table and the user, it is one CALL statement.
Problem solved with a procedure as Rick James has suggested and with Prepared Statements https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html
I put the following code in my script and I execute the source command SOURCE initdb_gestiondesnotes.SQL
After that I call the procedure with: CALL create_professeur();
DELIMITER $$
CREATE PROCEDURE CREATE_PROFESSEUR()
BEGIN
DECLARE v_a INT Default 1 ;
DECLARE v_nom VARCHAR(42);
DECLARE v_prenom VARCHAR(42);
DECLARE v_login VARCHAR(84);
DECLARE v_titre VARCHAR(5);
DECLARE v_pwd VARCHAR(5);
SET v_pwd = 'p';
simple_loop: LOOP
SET v_nom = CONCAT("prof", LPAD(CAST(v_a AS CHAR), 3, '0'));
SET v_prenom = CONCAT("prenom", LPAD(CAST(v_a AS CHAR), 3, '0'));
SET v_titre = CASE WHEN RAND() > .5
THEN 'M'
ELSE 'F' END;
INSERT INTO Professeur (prenom, nom, titre) VALUES (v_prenom, v_nom, v_titre);
SET v_login = CONCAT(v_prenom, v_nom);
SET #sql1 = CONCAT('CREATE OR REPLACE USER ', v_login, '#localhost identified BY \'p\' ');
PREPARE stm1 FROM #sql1;
EXECUTE stm1;
SET #sql2 = CONCAT('GRANT role_professeur TO ', v_login, '#localhost');
PREPARE stm2 FROM #sql2;
EXECUTE stm2;
SET #sql3 = CONCAT('SET DEFAULT ROLE role_professeur FOR ', v_login, '#localhost');
PREPARE stm3 FROM #sql3;
EXECUTE stm3;
SET v_a=v_a+1;
IF v_a=51 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
DEALLOCATE PREPARE stm1;
DEALLOCATE PREPARE stm2;
DEALLOCATE PREPARE stm3;
END $$
DELIMITER ;
Now I hove 50 user with prenom001prof001, prenom002prof002... as login and 'p' as password. Again with prepared statements I gave each user the role of role_professeur.

Conditional change of database engine

I would like to change the table format of my database to InnoDB. But I only want the action to run if the table format is not InnoDB yet.
If you execute the following command, a time-intensive action is started again and again (similar to a repair), if the table format is already InnoDB:
ALTER TABLE `MyTable` ENGINE InnoDB;
Is there a condition, which you can insert for this case, so that the operation runs faster, if the target format is already InnoDB?
I'm thinking about something like:
ALTER TABLE `MyTable` ENGINE InnoDB WHERE ENGINE != 'InnoDB';
ALTER TABLE IGNORE `MyTable` ENGINE InnoDB;
You can use information_schema.TABLES, to generate the script:
set #db_schema = 'test';
set #alter_query = 'alter table `{table}` engine=innodb;';
select group_concat(replace(
#alter_query,
'{table}',
replace(t.TABLE_NAME, '`', '``')
) separator '\n') as script
from information_schema.TABLES t
where t.TABLE_SCHEMA = #db_schema
and t.ENGINE = 'MyISAM';
Then you need to copy the result and execute it.
Demo
Update
If you need to execute it in one run, you can define a stored procedure with a cursor on information_schema.TABLES and execute it:
drop procedure if exists tmp_alter_myisam_to_innodb;
delimiter //
create procedure tmp_alter_myisam_to_innodb(in db_name text)
begin
declare done int default 0;
declare tbl text;
declare cur cursor for
select t.TABLE_NAME
from information_schema.TABLES t
where t.TABLE_SCHEMA = db_name
and t.ENGINE = 'MyISAM';
declare continue handler for not found set done = 1;
open cur;
fetch_loop: loop
fetch cur into tbl;
if done then
leave fetch_loop;
end if;
set #stmt = 'alter table `{table}` engine=innodb;';
set tbl = replace(tbl, '`', '``');
set #stmt = replace(#stmt, '{table}', tbl);
prepare stmt from #stmt;
execute stmt;
deallocate prepare stmt;
end loop;
close cur;
end //
delimiter ;
call tmp_alter_myisam_to_innodb('my_db');
drop procedure tmp_alter_myisam_to_innodb;

TRUNCATE all tables matching name pattern

This is the sql i'm using based from this answer:
SET #pattern = '%_movielist';
SELECT concat('TRUNCATE TABLE ', GROUP_CONCAT(concat(TABLE_NAME)), ';')
INTO #truncatelike FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern;
SELECT #truncatelike;
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
but I get this error Access denied for user 'root'#'%' to database 'information_schema'.
What am I doing wrong? It seems to work for other users
You trying to execute this statement on "information_schema" database. Read more about this database [https://dev.mysql.com/doc/refman/5.7/en/information-schema.html]
You should not be running statements on the information_schema database (unless you REALLY know what you're doing). The database serves as a "meta" repository that dictates how the server operates. Chances are that you have no need to touch it and you'll likely brick your server if you do.
This is already answered here. [#1044 - Access denied for user 'root'#'localhost' to database 'information_schema'
Restriction to above: This query will work only if the no of table returned by the statement is 1 for more than 1 tables, you will require to use it in iteration.
To make this work for all the table matching the pattern we would require to use stored procedure.
Please change the Procedure name
CREATE PROCEDURE `new_procedure`()
BEGIN
-- Pattern to Match
SET #pattern = '%_movielist';
-- Temporary Table to Store the Result of The Select Statement
CREATE TEMPORARY TABLE IF NOT EXISTS Table_ToBeTruncated
(
Id int NOT NULL AUTO_INCREMENT,TableName varchar(100),
PRIMARY KEY (id)
);
-- Insert all the TableName to be Truncated
insert Table_ToBeTruncated(TableName)
SELECT distinct concat('TRUNCATE TABLE `', TABLE_NAME, '`;')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern and TABLE_SCHEMA = 'movielist';
-- Declare a variable to count the no of records to be truncated.
SET #count=(Select count(*)from Table_ToBeTruncated);
-- Iterate the list
WHILE #count> 0 DO
-- Pick One table from the Temporary Table List;
SELECT TableName into #truncatelike from Table_ToBeTruncated where ID= #count;
-- Prepare the statement
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Decrease the counter.
set #count = #count- 1;
END WHILE;
drop TEMPORARY TABLE IF EXISTS Table_ToBeTruncated ;
END

Logic condition to avoid duplicate entries in stored procedure

I have the following stored procedure . I'm trying to insert the users from the table usuaris, whose admin variable is equal to 1, into the table that the stored procedure creates with the name( nombre varchar(50)) that is passed as a parameter.
When the procedure is called, it duplicates the user 'mary' with id 4. I've tried a couple of ways to implement the logic condition in order to avoid the duplication, but still, I'm missing something and I can't get the desired result. In the code below, the logic condition before the insertion is the last thing I've tried. Any ideas?
Thanks.
CREATE DEFINER=`root`#`localhost` PROCEDURE `createNewtable`(nombre varchar(50))
BEGIN
/*variable declaration*/
declare centinela int ;
declare id1 int ;
declare nom1 varchar(50);
declare admin1 enum('0','1') ;
declare cadena varchar(100); /*string to concatenate table creation and insertion*/
/*cursor declaration*/
declare cursor1 cursor for select * from users.usuaris where admin = '1' ;
declare continue handler for not found set #centinela = 1 ;
/*create the table with the name that's passed as parameter*/
set #cadena=concat("create table ",nombre,
"(
id2 int not null primary key,
nom2 varchar(50),
admin2 enum ('0','1')
)" );
prepare stmt from #cadena ;
execute stmt ;
deallocate prepare stmt;
/* loop that fetches the data from the table usuaris and
inserts them into the newly created table. */
set #centinela = 0 ;
open cursor1 ;
bucle: loop
fetch cursor1 into id1,nom1,admin1 ;
if ( centinela = 1 ) then
leave bucle ;
end if ;
/*logic condition to avoid entry duplication */
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
end if;
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end loop bucle;
close cursor1;
END
Here is the single-table database of users :
create database if not exists `users` ;
use `users` ;
create table usuaris(
id int not null auto_increment primary key ,
nom varchar(50),
admin enum ('0','1')
);
insert into usuaris(id,nom,admin)
values
(1,'jose','1'),
(2,'maria','0'),
(3,'frank','1'),
(4,'mary','1'),
(5,'godfrey','0') ;
Also it has to duplicate jose. The reason of duplication - if the IF statement isn't TRUE then you don't set the new #cadena variable BUT anyway execute PREVIOUS #cadena statement. You should move execution into the IF statement also:
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end if;
Also in SQL you should always try to avoid loops if it possible and use SQL statements instead.
You can replace your loop with one SQL statement:
INSERET INTO NEW_TABLE_NAME_HERE
SELECT id1,nom1,admin1
FROM users.usuaris where admin<>'1'
Further more you can use SELECT INTO statement syntax to automatically create new table without CREATE TABLE statement:
SELECT id1 as id2,
nom1 as nom2,
admin1 as admin2
INTO NEW_TABLE_NAME_HERE
FROM users.usuaris where admin<>'1'
Change ur below code to my new code and try-
Existing Code
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
end if;
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
New Code-
SET #cnt=SELECT count(*) FROM users.usuaris WHERE admin='1' AND id=#id1
IF #cnt>0 THEN
SET #cadena=CONCAT("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end if;

How to use variable as the table identifier in MYSQL

If i have this:
CREATE TABLE ftable (
id INT,
fvalue VARCHAR(14)
);
INSERT INTO ftable VALUES (1,'tableB'),(2,'tableA');
CREATE TABLE tableA (
value VARCHAR(14)
);
SELECT #tmp:=fvalue FROM ftable WHERE id=2;
How do I make it so I can do this:
INSERT INTO #tmp VALUES ('buhambug');
Becuase as far I know that throws a mysql error.Can someone show me a sqlfiddle of the solution? Or maybe I'm thinking about this the wrong way?
You need to use dynamic SQL to use a variable as an object name:
SET #tmp = (SELECT fvalue FROM ftable WHERE id=2);
SET #SQL = CONCAT('INSERT INTO ',#tmp,' VALUES (''buhambug'')');
PREPARE stmt FROM #SQL;
EXECUTE stmt;
SQL FIDDLE
You can't do in static sql.
You can do it in stored procedure:
delimiter $$
drop procedure if exists test_call$$
create procedure test_call(table_in varchar(100))
begin
set #q = concat("select * from ", table_in);
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end$$
delimiter ;
call test_call('TeableA');
drop procedure if exists test_call;
In general dynamic read from dynamic tables is not a good decision