I have the following code in Maria DB ,
I want to use my function in a query to create a sequence that starts with the count(*) +1 of a my TABLE1
It gives me an error in the CREATE SEQUENCE query :
CREATE FUNCTION myFuntion() RETURNS INT
BEGIN
DECLARE lastID INT DEFAULT 1;
SELECT COUNT(*) INTO lastID FROM TABLE1;
RETURN lastID+1;
END;
CREATE SEQUENCE seq101 START WITH myFuntion() INCREMENT BY 1 ;
Error :
MySqlError { ERROR 1064 (42000): You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for
the right syntax to use near 'myFuntion() INCREMENT BY 1' }
I think the only way is to use a prepared statement and a variable, since user functions are not allowed in PREPARE FROM/EXECUTE IMMEDIATE.
SELECT count(*) + 1 INTO #lastid FROM yourtable
EXECUTE IMMEDIATE CONCAT("CREATE SEQUENCE seq101 START WITH ", #lastid," INCREMENT BY 1");
Related
I want to get an integer from a database query:
SELECT CAST(concat('id_',substr_of_jointable) AS INTEGER) into integervalue
FROM books_authors where id_books = booksvalue
ORDER BY id_books DESC
LIMIT 1;
subsr_of_jointable is a TEXT with value authors. However I always get an error:
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ERROR: invalid input syntax for integer: "id_authors"
Wobei: SQL statement "SELECT CAST(concat('id_',substr_of_jointable)AS INTEGER) FROM books_authors where id_books = books_value ORDER BY id_books DESC LIMIT 1"
PL/pgSQL function books_ins_trig_proc() line 125 at SQL statement
Does anyone have an idea why? The column id_books id_authors is an integer value in the database.
Assuming you're trying to build a dynamic query inside a PL/pgSQL function, you might want to take a look at this approach.
Data sample
CREATE TABLE t (id_authors INT);
INSERT INTO t VALUES (1);
Function
CREATE OR REPLACE FUNCTION myfunction(TEXT) RETURNS INT
LANGUAGE 'plpgsql' AS $BODY$
DECLARE i INT;
BEGIN
EXECUTE 'SELECT id_'|| $1 ||' FROM t LIMIT 1;' INTO i;
RETURN i;
END;
$BODY$;
This example is only to shows how you can concatenate your strings to create a column name inside your dynamic query.
Calling the function
SELECT * FROM myfunction('authors');
myfunction
------------
1
(1 Zeile)
Further reading:
Execute Dynamic Commands
PL/pgSQL Function Parameters
We want to run a simple SQL statement (dropping and creating an index), but only if the old index has not already been deleted. After looking up the syntax for IF in DB2, I came up with this:
IF EXISTS (SELECT indname FROM SYSCAT.INDEXES WHERE INDNAME = 'TEST_CREATE_INDEX_OLD')
THEN
DROP INDEX TEST_CREATE_INDEX_OLD;
create index TEST_CREATE_INDEX_NEW on example_table
(
id,
another_field
);
END IF;
When run with either SQuirrel (already setup to run with db2) or via command line, this script results in an error:
An unexpected token "IF EXISTS (SELECT indname FROM SYSCAT.INDEX" was
found following "BEGIN-OF-STATEMENT". Expected tokens may include:
"".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.23.42 SQL Code:
-104, SQL State: 42601
So - what am I doing wrong? Am I missing something, or is there another way to achieve my goal (check for $thing in database, execute appropriate query) that so far has not occured to me?
If IF statement is valid only in a compound-SQL block (i.e. inside a stored-procedure/routine/function/anonymous-block).
It's not valid standalone as your question shows, and that is why Db2 throws the -104 error.
You can look up the explanation of the sqlcode -104 by looking up SQL0104N in the free online Db2 Knowledge Center at this link.
To be able to use compound-SQL in your Squirrel-SQL tool, you need to configure squirrel to use an alternative statement terminator. Google that. In the examples below, I show a statement terminator of # (to delimit the block).
Here are two different ways to do what you want with Db2-Linux/Unix/Windows, each uses an anonymous block. Other approaches are possible.
In this example the drop-index will only run if the index-name exists in the current schema:
begin
declare v_index_exists integer default 0;
select 1 into v_index_exists
from syscat.indexes
where indname = 'TEST_CREATE_INDEX_OLD';
if v_index_exists = 1
then
execute immediate('drop index TEST_CREATE_INDEX_OLD');
execute immediate('create index TEST_CREATE_INDEX_NEW on example_table ( id, another_field)');
end if;
end#
In this example the drop-index will always run, but the block won't abort if the index does'nt exist (i.e. it will continue and not throw any error).
begin
declare v_no_such_index integer default 0;
declare not_exists condition for sqlstate '42704';
declare continue handler for not_exists set v_no_such_index=1;
execute immediate('drop index TEST_CREATE_INDEX_OLD');
execute immediate('create index TEST_CREATE_INDEX_NEW on example_table ( id, another_field)');
end#
You must use another statement delimiter, if you want to use db2 compound statement.
In Squirrel: Session -> Session Properties -> SQL -> Statement Separator = #.
Indexes in Db2 are fully qualified by 2 SYSCAT.INDEXES columns: INDSCHEMA and INDNAME. So, it's advisable to use both these fields in a SELECT statement on SYSCAT.INDEXEX as in example.
You can't use static DDL statements in a compound statement. Use EXECUTE IMMEDIATE statements instead.
Below is an example emulating UPDATE INDEX for the index in a schema equal to CURRENT SCHEMA special registry set in the session.
BEGIN
IF EXISTS (SELECT indname FROM SYSCAT.INDEXES WHERE INDSCHEMA = CURRENT SCHEMA AND INDNAME = 'TEST_CREATE_INDEX_OLD')
THEN
EXECUTE IMMEDIATE 'DROP INDEX TEST_CREATE_INDEX_OLD';
EXECUTE IMMEDIATE'
create index TEST_CREATE_INDEX_NEW on example_table
(
id,
another_field
)
';
END IF;
END
#
I need to write a trigger in my SQL code that changes values in table A(Asortyment) that is connected with table B(Historia_Zamowien) with relation Many-Many. To connect A and B I use table C(Zamowienia_Asortyment).
How it looks like in relational model
I need to get to Asortyment.Dostepnosc through Zamowienia_Asortyment after INSERT ON Historia_Zamowien and change values to 0. I wrote some code that doesnt work and i have no idea what is wrong. Would you help?
CREATE TRIGGER "Zmiana_Dostepnosci_Po_Zamowieniu"
AFTER INSERT ON "Historia_Zamowien"
FOR EACH ROW
BEGIN
UPDATE "Asortyment"
SET tab1."Dostepnosc" = 0
FROM "Asortyment" tab1 JOIN "Zamowienia_Asortyment" tab2 ON tab1."ID_sprzetu" = tab2."ID_sprzetu"
JOIN inserted tab3 ON tab2."Numer_zamowienia" = tab3."Numer_zamowienia"
WHERE tab1."ID_sprzetu" = tab2."ID_sprzetu" AND tab2."Numer_zamowienia" = inserted."Numer_Zamowienia"
END;
/
After i run the code i get:
Error(1,5): PL/SQL: SQL Statement ignored
Error(3,5): PL/SQL: ORA-00933: SQL command not properly ended
Error(7): PLS-00103: Endountered symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge json_exists json_value json_query json_object json_array
There are several issues with your SQL :
in Oracle you cannot use JOIN within an UPDATE ; I replaced it with a WHERE EXISTS correlated subquery
you have repeated conditions in JOINs and WHERE clause, I simplified that
to refer to the newly inserted row in Historia_Zamowien, use the :NEW keyword (you seem to use inserted)
Try :
CREATE TRIGGER "Zmiana_Dostepnosci_Po_Zamowieniu"
AFTER INSERT ON "Historia_Zamowien"
FOR EACH ROW
BEGIN
UPDATE "Asortyment" tab1 SET tab1."Dostepnosc" = 0
WHERE EXISTS (
SELECT 1
FROM "Zamowienia_Asortyment" tab2
WHERE tab2."ID_sprzetu" = tab1."ID_sprzetu"
AND tab2."Numer_zamowienia" = NEW."Numer_Zamowienia"
)
END
/
When I run this query
DECLARE
num NUMBER;
BEGIN
SELECT COUNT(*) INTO num FROM user_all_tables WHERE TABLE_NAME=upper('DatabaseScriptLog')
;
IF num < 1 THEN
CREATE TABLE DatabaseScriptLog
(ScriptIdentifier VARCHAR(100) NOT NULL,
ScriptType VARCHAR(50),
StartDate TIMESTAMP,
EndDate TIMESTAMP,
PRIMARY KEY (ScriptIdentifier)
);
END IF;
END;
When execute the above, I got the following:
PLS-00103: Encountered the symbol
"CREATE" when expecting one of the
following:
begin case declare exit for goto if
loop mod null pragma raise return
select update while with << close current delete
fetch lock insert open rollback
savepoint set sql execute commit
forall merge pipe
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
You cannot run DDL statements like that. You need to use dynamic SQL (EXECUTE IMMEDIATE).
IF num < 1 THEN
EXECUTE IMMEDIATE 'CREATE TABLE DatabaseScriptLog (ScriptIdentifier VARCHAR(100) NOT NULL, ScriptType VARCHAR(50), StartDate TIMESTAMP, EndDate TIMESTAMP, PRIMARY KEY (ScriptIdentifier))'
END IF;
You cannot do this like you could in SQLServer. You need to execute the create code through a stored procedure that is already in the proper schema. You pass the create code as a parameter and the stored procedure that has the correct privileges does it for you.
I use a version script that updates the schema to the latest by running schema altering operations separated by if-then clauses to check what version the db is at. After altering it increments the version so that the next if statements test passes and so on. If you are up to date and run the script the ifs skip all altering code. If your db is at version 46 and you run the script which has all changes up to 50, you execute only the blocks that represent versions 47-50.
You could execute immediate but would need elevated privileges which I would not recommend.
Hope this helps.
This is a simple trigger I'm trying to create:
CREATE TRIGGER add_item_id BEFORE INSERT ON products
FOR EACH ROW
BEGIN
DECLARE max_id INTEGER;
SELECT MAX(item_id) INTO #max_id FROM products;
SET NEW.item_id = #max_id + 1;
END;
I tried it both on phpMyAdmin SQL window and mysql prompt and get the same error as below:
#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 '' at line 4
delimiter //
CREATE TRIGGER add_item_id BEFORE INSERT ON products
FOR EACH ROW
BEGIN
DECLARE max_id int;
SELECT MAX(item_id) INTO max_id FROM products;
SET NEW.item_id = max_id + 1;
END//
delimiter ;
Some notes:
If you declare (local variable) max_id, use it. #max_id is a GLOBAL variable. Any #variable can be used without declaring it, but it stays with the session as long as the session lives.
Your code is fine, you are just missing the delimiter changes. Without delimiter //, MySQL sees the CREATE TRIGGER statement ending at ..FROM PRODUCTS;, which makes it invalid
You could also do:
CREATE TRIGGER add_item_id
BEFORE INSERT
ON products
FOR EACH ROW
BEGIN
SET NEW.item_id = 1 + ( SELECT MAX(item_id)
FROM products
) ;
END;
Note: you can declare auto_incremented fields in almost all RDBMS.