Use MySql function variables as table name in the query - mysql

I need to have a function that increments the certain ID in a table (like auto_increment)
I have smth like this
DELIMITER $$
DROP FUNCTION IF EXISTS `GetNextID`$$
CREATE FUNCTION `GetNextID`(tblName TEXT, increment INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE NextID INT;
SELECT MAX(concat(tblName, 'ID')) + increment INTO NextID FROM concat('table_', tblName);
## SELECT MAX(articleID) + increment INTO NextID FROM table_article;
RETURN NextID;
END$$
DELIMITER ;
INSERT INTO `table_article` ( articleID, articleAlias ) VALUES ( GetNextID('article', 5), 'TEST' );
So i pass two variables: tblName (without table_ prefix), and the increment number. The commented line - SELECT query inside the function itself - works well, but i want to dynamically pass table name to the function and so get data from a certain col of certain table. What am I doing wrong?
The error is:
#1064 - 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 '('table_', tblName);
RETURN NextID;
END' at line 6
if i simply try to select max value in such a way
SELECT MAX(articleID) + increment INTO NextID FROM tblName;
The error reports that tblName does not exist. How can i tell MySql that this is actually a var passed to the function, not an exact table name? If it is possible.

you need something like
prepare stmp from concat('SELECT MAX(ID) + ', increment, ' INTO NextID FROM table_', tblName);
execute stmp;
deallocate prepare stmp;

Related

Should it be possible to execute an SQL function in a check constraint within DB2 z/OS

Simple version of the DDL:
create function rm00dv1.no_concurrent_schedules()
returns integer
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
NOT DETERMINISTIC
BEGIN
declare num_overlaps integer;
select count(*)
into num_overlaps
from
rm00dv1.schedules a
where
a.id != 0
and
exists (
select 1
from rm00dv1.schedules b
where
b.id = 0 -- matches the key of a given record
and rm00dv1.isConcurrent(b.schdl_eff_dt, b.schdl_trm_dt, a.schdl_eff_dt, a.schdl_trm_dt) != 0
);
return num_overlaps;
end;
Table:
create table rm00dv1.schedules (
id int not null,
schdl_eff_dt date not null,
schdl_trm_dt date not null,
info_chg_ts timestamp(6) not null with default
)
in RM00DV1.TSRMDV01 ;
alter table rm00dv1.schedules add constraint no_schedule_overlap
check ((schdl_trm_dt < '01/01/2015')
or
rm00dv1.no_concurrent_schedules() <= 0);
I am getting an SQL00551N - no execution privilege and that is odd because I can execute the function in a select statement.
Any idea to solve this problem?
Thanks.
Looks like you can't. I'm looking at the DB2 10 for z/OS reference for ALTER TABLE reference and it says the following under CHECK (check-condition): "A check-condition is a search condition, with the following restrictions: ... must not contain... Built-in or user-defined functions...".
Since your function looks like it won't convert to a check condition, defining triggers on the table might be the next best option.
I learned that AFTER triggers do not get a -746 like BEFORE triggers do. I had really wanted to use a CONSTRAINT because that best captures the intent for people who come after me, with a BEFORE trigger to terminate the active schedules. But, it looks like a sequence of triggers is going to be the way to go. It is a bit clunky because the triggers all have to be created separately and you have to look at them together to get the intent, and because correct behavior is dependent on their creation order. Yes, it is documented that they will be executed in the order of their creation.
Happy path termination of rows without a specified termination date:
CREATE TRIGGER terminate_no_trm
after
INSERT ON schedules
referencing new as new
FOR EACH ROW
MODE DB2SQL
BEGIN ATOMIC
update schedules
set
schdl_trm_dt = max(schdl_eff_dt, new.schdl_eff_dt - 1 days) -- prob not necessary, but don't set the trm before the eff
, info_chg_ts = new.info_chg_ts
where
new.keyCombo = keyCombo
and
schdl_trm_dt = '9999-12-31'
and schdl_eff_dt < new.schdl_eff_dt;
end
Prevent insert of rows if that insert causes an overlap:
CREATE TRIGGER no_overlapping_schedules_i
after
insert ON schedules
referencing new as n
FOR EACH ROW
MODE DB2SQL
when (num_concurrent_schedules(n.keyCombo) > 0)
begin atomic
SIGNAL SQLSTATE '75001' (
'Concurrent schedules detected: '
concat ' ' concat cast(n.keyCombo as varchar(32))
concat ': ' concat cast(n.schdl_eff_dt as varchar(32))
concat ' to ' concat cast(n.schdl_trm_dt as varchar(32))
);
end
and prevent UPDATE if that would result in an overlap
CREATE TRIGGER no_overlapping_schedules_u
after
update ON schedules
referencing new as n
FOR EACH ROW
MODE DB2SQL
when (num_concurrent_schedules(n.keyCombo) > 0)
begin atomic
SIGNAL SQLSTATE '75001' (
'Concurrent schedules detected: '
concat ' ' concat cast(n.keyCombo as varchar(32))
concat ': ' concat cast(n.schdl_eff_dt as varchar(32))
concat ' to ' concat cast(n.schdl_trm_dt as varchar(32))
);
end
Thanks for the ideas.

MySQL: Insert with conditional

I must perform the following situation down, but when you run got the error:
SQL Error (1064): 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 'INTO produto_seriais(serial_id) VALUES( SELECT id
FROM seriais WHERE serial =' at line 5
SELECT CASE WHEN (
SELECT COUNT(id) FROM seriais WHERE serial = '2020'
) > 1
THEN
(INSERT INTO produto_seriais(serial_id) VALUES(
SELECT id FROM seriais WHERE serial = '2020'
))
ELSE (
INSERT INTO seriais (serial) VALUE('2020');
SET #last_id_in_table1 = LAST_INSERT_ID();
INSERT INTO produto_seriais (serial_id) VALUES (#last_id_in_table1);
)
END;
The case is as follows:
I'll get in "serial" table by serial "X". If it already exists, unless your ID in the "produto_seriais" table. If there is (serial), I will save the same, recover your ID and save to "produto_seriais". Any suggestions for how to do this?
Important Note: This routine will run will be thousands of times each execution (10,000 or more, depending on the quantity of serial).
P.s.: Sorry for my bad english.
Try a stored procedure
DELIMITER //
CREATE PROCEDURE sp_produto_seriais(IN `p_serial_id`)
IF EXISTS (SELECT * FROM seriais WHERE serial = p_serial_id )
BEGIN
INSERT INTO produto_seriais (serial_id)
SELECT id
FROM seriais
WHERE serial = p_serial_id
END
ELSE
BEGIN
INSERT INTO seriais (serial) VALUE(p_serial_id);
INSERT INTO produto_seriais (serial_id) VALUES (LAST_INSERT_ID());
END //
DELIMITER ;
usage:
CALL sp_produto_seriais('2020')
You could use the if exists..else statement.
If exists (select * from ....)
Begin
Insert into ... Select id from table
End
Else
Begin
Insert..
End
Please fill .... with your statements. You could use the link here to convert it for MySQL.
Convert if exists for MySQL

parameters sql query inside of a stored procedure mysql

I'm working with stored procedures in mysql, so I have the following procedure:
DELIMITER ##
DROP PROCEDURE IF EXISTS generarEstadisticoRD ##
CREATE PROCEDURE generarEstadisticoRD ( mesInicial INT,anualInicial INT, mesFinal INT,anualFinal INT, codigoEntidad CHAR(3),mes INT )
BEGIN
DECLARE controlador INT;
DECLARE tipoDocumento CHAR(2);
DECLARE cursorDocumentos CURSOR FOR SELECT DISTINCT e.claseDocIdentidadFallecido
FROM EstadisticoRD e WHERE e.anual>=anualInicial AND e.anual<=anualFinal
AND e.mes >=mesInicial AND e.mes<=mesFinal AND e.codOficina=codigoEntidad;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET controlador = 1;
DROP TEMPORARY TABLE IF EXISTS estadistico;
CREATE TEMPORARY TABLE IF NOT EXISTS
estadistico( TIPO CHAR(2), MES INT );
OPEN cursorDocumentos;
cursorLoop : LOOP
FETCH cursorDocumentos INTO tipoDocumento;
IF( controlador=1 ) THEN
LEAVE cursorLoop;
END IF
/**
*Lógica
*/
INSERT INTO estadistico(`TIPO`,`MES`)
SELECT DISTINCT
c.descripcion,
IFNULL( (SELECT e.numRegistrosReportados FROM estadisticoRD e WHERE e.codOficina=codigoEntidad
AND e.claseDocIdentidadFallecido=tipoDocumento AND e.mes=mes ), 0)
FROM estadisticoRD e, claseDoc c WHERE e.codOficina=codigoEntidad AND e.claseDocIdentidadFallecido=tipoDocumento
AND c.claseDoc = e.claseDocIdentidadFallecido;
END LOOP cursorLoop;
CLOSE cursorDocumentos;
SELECT * FROM estadistico;
END ##
DELIMITER ;
I get the following messages when I try to execute the procedure:
Executed successfully in 0,001 s, 0 rows affected.
Line 2, column 1
Error code 1064, SQL state 42000: 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 'INSERT INTO estadistico(`TIPO`,`MES`)
SELECT DISTINCT c.descripcion,
' at line 24
Line 3, column 1
So, what am I doing wrong?.
UPDATE 1:
The I corrected the mistake with semicolon thanks #Daniel Victoria
But now I get the following mistake:
Error code 1267, SQL state HY000: Illegal mix of collations (latin1_spanish_ci,IMPLICIT) and (latin1_swedish_ci,IMPLICIT) for operation '='
Exactly I get this mistake when I do
SELECT DISTINCT e.claseDocIdentidadFallecido
FROM EstadisticoRD e WHERE ... AND e.codOficina=codigoEntidad;
why when I do e.codOficina=codigoEntidad I get this mistake, how to fixed it?.
UPDATE 2:
To solve it, I need to put COLLATE latin1_swedish_ci after to the column that has the mistake.
In this case the new query is :
SELECT DISTINCT *
FROM estadisticoRD e WHERE e.anual>=anualInicial AND e.anual<=anualFinal
AND e.mes >=mesInicial AND e.mes<=mesFinal AND e.codOficina = codigoEntidad COLLATE latin1_swedish_ci;
I hope to finish this procedure the best way.
Your are missing a semicolon (;) after the "END IF"

multiple value insert works in mysql procedures?

Multivalue insert example - it works manually but NOT in mySQL stored procedure.
INSERT INTO input_data1(mobile) VALUES (9619825525),(9619825255),(9324198256),(9013000002),(9999999450),(9999999876) ;
i am getting syntax error near "str" word in below proc, Can any one let me know how to implement this multi value INSERT work in procedure?
DELIMITER |
DROP PROCEDURE IF EXISTS mobile_series1;
CREATE PROCEDURE mobile_series1(IN str text)
LANGUAGE SQL READS SQL DATA
BEGIN
DROP TABLE IF EXISTS input_data1 ;
CREATE TEMPORARY TABLE input_data1 (mobile varchar(1000)) engine=memory;
INSERT INTO input_data1(mobile) VALUES str;
END |
DELIMITER ;
Thanks in Advance.
I don't have a MySQL server so there's probably syntax errors and +1 errors (i.e. may not be capturing the last on the list, may not progress past the first item etc, problems fixed by putting a +1 in the code), but you basically want to replace your INSERT statement with something this.
DECLARE INT _CURSOR 0;
DECLARE INT _TOKENLENGTH 0;
DECLARE VARCHAR _TOKEN NULL;
SELECT LOCATE(str, ",", _CURSOR) - _CURSOR INTO _TOKENLENGTH;
LOOP
IF _TOKENLENGTH <= 0 THEN
SELECT RIGHT(str, _CURSOR) INTO _TOKEN;
INSERT INTO input_data1(mobile) VALUE _TOKEN;
LEAVE;
END IF;
SELECT SUBSTRING(str, _CURSOR, _TOKENLENGTH) INTO _TOKEN;
INSERT INTO input_data1(mobile) VALUE _TOKEN;
SELECT _CURSOR + _TOKENLENGTH + 1 INTO _CURSOR;
SELECT LOCATE(str, ",", _CURSOR + 1) - _CURSOR INTO _TOKENLENGTH;
END LOOP;
Your function call would then be something like
EXEC mobile_series1('9619825525,9619825255,9324198256')

MySQL 5: Trying to Generate Dynamic Sequence IDs as Stored Function or Stored Procedure

Am using MySQL 5 on OS X - Snow Leopard...
Have working code in place which obtains the highest sequence number ID from a sequence table and then increments and assigns it to its corresponding table:
The original code's purpose is to dynamically increments a specific table's last sequence id and set its corresponding table's id to that new value.
Notes:
1. Original Code Snippet (which is working):
Get last sequence number
replace into my_sequence_id_s set id =
(select max(CONVERT(sequence_id, signed)) from my_table_t);
Increments the number
insert into my_sequence_id_s set id = null;
Saves the number as a variable
set #dynamicId = last_insert_id();
Print
select #dynamicId;
2. Refactoring:
DROP PROCEDURE IF EXISTS generate_dynamic_id#
CREATE PROCEDURE generate_dynamic_id
(IN _sequence_table varchar(40),
IN _actual_table varchar(40),
IN _id_field VARCHAR(40),
OUT dynamic_id varchar(40))
BEGIN
-- Get Last Sequence Number
set #getLastSequenceNumberSQL =
concat('REPLACE INTO ', _sequence_table, 'SET ID =
(select max(CONVERT(',_id_field,', signed))
from ', _actual_table, ');');
prepare lastRecordStmt from #getLastSequenceNumberSQL;
execute lastRecordStmt;
deallocate prepare lastRecordStmt;
-- Increments the number.
set #createNewSequenceNumberSQL =
concat('insert into ', _sequence_table ,' set id = null;');
prepare newSequenceNumberStmt from #createNewSequenceNumberSQL;
execute newSequenceNumberStmt;
deallocate prepare newSequenceNumberStmt;
-- Set the number as a dynamic variable.
set #dynamic_id = last_insert_id();
END;
#
3. Here's the calling function (which fails):
-- Get dynamically incremented id
call generate_dynamic_id(
'my_sequence_id_s', 'my_table_t', 'table_id', #dynamicId);
Error:
com.mysql.jdbc.exceptions.MySQLSyntaxErrorException:
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
'ID = (select max(CONVERT(id_field, signed)) from my_table_t)' at line 1
For some odd reason, dynamic function calls are not allowed in Stored Functions or Triggers, so that's why a Stored Procedure was used.
As you can see, I am setting up varchars at the parameters and then trying to concatenate them as strings and run them inside prepared statements.
Any help would be greatly appreciated...
concat('REPLACE INTO ', _sequence_table, 'SET ID =
where space between _sequence_table and SET ID?