Error creating trigger - sql

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.

Related

Error in procedure which updates master table after deleting records in detail table

In Interbase 2009 db I have master and detail tables(Tmaster, Tdetails).
Tmaster:
master_id(pk)
DocSumma
DocSummaDol
Tdetails:
det_id
master_id(fk)
price1,price2
qnt
After I delete/update records in child table(Tdetails) procedure must update summa in master table(Tmaster).
I have 2 problems:
If procedure contains this if-clause:
if (m.DocSumma=0) then begin delete from Tmaster m where m.master_id=:master_id; end
it returns this error:
Column does not belong to referenced table. Dynamic SQL Error. SQL
error code = -206. Column unknown.
Without if-clause I have the second problem: procedure works very slow. It would be end after 13hrs :)
Sometimes in IBExpert I get this error:
Arithmetic overflow or division by zero has occurred. arithmetic
exception, numeric overflow, or string truncation.
SQLCODE: -802 GDSCODE: 335544321
what is wrong in my Psql code?
alter procedure sp_recalculate_summa
as
declare variable master_id integer;
declare variable det_id integer;
declare variable sum1 decimal(8,4) ;
declare variable sum2 decimal(8,4) ;
begin
for select m.master_id
from Tmaster m
into :master_id
do begin
sum1=0;
sum2=0;
for select det_id,
sum(d.price1*d.qnt)as summa1,
sum(d.price2*d.qnt)as summa2
from Tdetails d, Tmaster m
where d.det_id=:master_id
group by det_id
into :det_id,:sum1,:sum2
do
if (m.DocSumma=0) then begin
delete from Tmaster m where m.master_id=:master_id;
end
Else begin
update Tmaster set DocSumma=:sum1 where master_id=:master_id;
update Tmaster set DocSummaDol=:sum2 where master_id=:master_id;
end
end
end
The problem is not with the delete statement, the problem is with if (m.DocSumma=0) then begin. You cannot refer to a table like that inside a PSQL block. You need to explicitly assign that column value to a local variable.
For example, something like:
alter procedure sp_recalculate_summa
as
declare variable master_id integer;
declare variable DocSumma decimal(8,4);
declare variable det_id integer;
declare variable sum1 decimal(8,4) ;
declare variable sum2 decimal(8,4) ;
begin
for select m.master_id, m.DocSumma
from Tmaster m
into master_id, DocSumma
do begin
sum1=0;
sum2=0;
for select det_id,
sum(d.price1*d.qnt)as summa1,
sum(d.price2*d.qnt)as summa2
from Tdetails d, Tmaster m
where d.det_id=:master_id
group by det_id
into det_id,sum1,sum2
do
if (DocSumma=0) then begin
-- etc..
end
end
end
As additional remarks:
I question the correctness of the condition if (m.DocSumma=0) then begin (if (DocSumma=0) then begin in my proposed change), shouldn't this be if (sum1 = 0) then begin? As in, it should use the updated sum, instead of the old sum.
Also, why are you updating TMASTER twice? It would be more efficient to use a single update:
update Tmaster set DocSumma=:sum1, DocSummaDol=:sum2 where master_id=:master_id;
Some of these changes might better be done through triggers on TDETAILS and maybe TMASTER, instead of using a delayed explicit recalculation.

Drop table if it exists with DB2/400 SQL

My goal is pretty straightforward - if table has rows, drop it.
Despite the fact that currently there are several similar answers none of them worked for me.
DB2 Drop table if exists equivalent
Suggested solution:
IF EXISTS (SELECT name FROM sysibm.systables WHERE name = 'mylib.mytable') THEN
DROP TABLE mylib.mytable;END IF;
Result:
SQL State: 42601 Vendor Code: -199 Message: [SQL0199] Keyword IF not expected.
Valid tokens: ( CL END GET SET CALL DROP FREE HOLD LOCK OPEN WITH ALTER BEGIN
Drop DB2 table if exists
Suggested solution:
--#SET TERMINATOR #
begin
declare statement varchar(128);
declare continue handle for sqlstate '42710' BEGIN END;
SET STATEMENT = 'DROP TABLE MYLIB.MYTABLE';
EXECUTE IMMEDIATE STATEMENT;
end #
Result:
Message: [SQL0104] Token HANDLE was not invalid. Valid tokens: HANDLER or, if replace handle with handler:
Message: [SQL0199] Keyword STATEMENT not expected. Valid tokens: SQL PATH RESULT SCHEMA CURRENT CONNECTION DESCRIPTOR.
From answer about views
Suggested solution:
DROP TABLE MY_TABLE ONLY IF EXISTS source.
Result:
Message: [SQL0104] Token ONLY was not invalid. Valid tokens: RESTRICT CASCADE
So, I wonder if an alternate solution exists. CL solution is also interesting.
I'm assuming you may want to do this more than once, so a procedure might be in order.
CREATE or replace PROCEDURE DROP_LIVE_TABLE
(in #table varchar(10)
,in #library varchar(10)
)
BEGIN
declare #stmt varchar(100);
declare #cnt int;
IF exists( select * from systables
where sys_dname = #library
and sys_tname = #table
and table_type in ('P','T')
) THEN
SELECT int(sum(number_rows))
INTO #cnt
from SYSTABLESTAT
where sys_dname = #library
and sys_tname = #table
;
IF #cnt > 0 THEN
set #stmt = 'DROP TABLE '||#library||'.'||#table||' CASCADE';
execute immediate #stmt;
END IF;
END IF;
RETURN;
END;
The CASCADE keyword causes any dependent objects such as indexes, logical files, views, or such to be deleted as well.
Here is a CL answer to this question:
PGM PARM(&FILENAME)
DCL VAR(&FILENAME) TYPE(*CHAR) LEN(10)
DCL VAR(&NUMRECS) TYPE(*DEC) LEN(10 0)
RTVMBRD FILE(&FILENAME) NBRCURRCD(&NUMRECS)
IF COND(&NUMRECS > 0) THEN(DLTF +
FILE(&FILENAME))
OUT: ENDPGM
This solution would have trouble if the physical file has dependencies such as indexes or logical files. Those dependencies would have to be deleted first.
The solution by #danny117 on the other hand does not work in all environments. For example I was unable to coerce it to work in SQuirreL client. But it does work in i Navigator. It also works in RUNSQLSTM, but I was unable to determine how to make it work with unqualified table references. If the tables are unqualified, RUNSQLSTM uses the default collection from DFTRDBCOL. The CURRENT_SCHEMA special register does not return the value from DFTRDBCOL.
Here is the if table has rows drop it solution using a compound statement:
begin
if( exists(
select 1 from qsys2.systables
where table_schema = 'MYLIB'
and table_name = 'MYTABLE'
)) then
if( exists(
select 1 from mylib.mytable
)) then
drop table mylib.mytable;
end if;
end if;
end;
I am guessing at the reason you would want to do this, but if it is to allow creation of a new table, then best way may be with a CREATE OR REPLACE TABLE if you are at IBM i v7.2 or greater.
If all you want to do is make sure you have an empty table, TRUNCATE (v7.2+) or DELETE may be better options.
Drop table if exists using atomic statement.
BEGIN ATOMIC
IF( EXISTS(
SELECT 1 FROM TABLES
WHERE TABLE_SCHEMA = 'MYLIB'
AND TABLE_NAME = 'MYTABLE'
)) THEN
DROP TABLE MYLIB/MYTABLE;
END IF;
END;
try this:
BEGIN
IF EXISTS (SELECT NAME FROM QSYS2.SYSTABLES WHERE TABLE_SCHEMA = 'YOURLIBINUPPER' AND TABLE_NAME = 'YOURTABLEINUPPER') THEN
DROP TABLE YOURLIB.YOURTABLE;
END IF;
END ;

Oracle Trigger - update table B based on a condition of table A

I'm attempting to create an Oracle trigger which sets the values of a column on table B based on a select statement run within the trigger.
I want to be able to set the values of the 'is_active' column in table B to 'N' based on the select statement after an insert on table A has been executed.
My query is as follows:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
FOR EACH ROW
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=:inactive_id
END INACTIVE_STATE;
/
When I try and complpile this trigger, I get the following errors:
Error(15,1): PL/SQL: SQL Statement ignored
Error(17,10): PLS-00049: bad bind variable 'INACTIVE_ID'
Error(17,15): PL/SQL: ORA-00933: SQL command not properly ended
Error(19,1): PLS-00103: Encountered the symbol "/" 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
It seems it doesn't like the update statement, or the bind variable isn't being parsed within this process.
If I seperate these statements into 2 commands (using var to handle the bind variable :inactive_id) it works as expected.
VAR INACTIVE_ID number
begin
select distinct b.id into :INACTIVE_ID
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
end;
/
PL/SQL procedure successfully completed.
SQL>
SQL> update modules
set is_active='N'
where ID=:INACTIVE_ID
/
1 row updated.
Any ideas what I might be overlooking?
As Tony Andrews pointed out in the comments of the original post, I was incorrectly using a colon before the "inactive_id" variable in the where clause.
The correct code should have been:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=inactive_id
END INACTIVE_STATE;
/
Try using
PRAGMA AUTONOMOUS_TRANSACTION;
before begin

Updating another table in an Oracle Trigger

I've prepared the following trigger create statement yet it does not compile.
CREATE OR REPLACE TRIGGER UpdIssueTypScrSchmSeq
AFTER INSERT
ON ISSUETYPESCREENSCHEME
FOR EACH ROW
DECLARE
maxID NUMBER(18,0);
BEGIN
select max(ID) into maxID from ISSUETYPESCREENSCHEME;
UPDATE SEQUENCE_VALUE_ITEM
SET SEQ_ID = maxID + 1
WHERE SEQ_NAME = 'IssueTypeScreenSchemeEntity';
END;
I keep getting error messsages saying "maxID must be declared".
Anybody know what is wrong?
Propbably does not make a difference but I am working with SQL Developer on Windows.

ORA-04071: missing BEFORE, AFTER or INSTEAD OF keyword Error

CREATE TRIGGER TRG_LOADMONEY
ON KIOSK
AFTER INSERT
AS
DECLARE #LOADEDCARDID INT
DECLARE #LOADEDAMOUNT INT
SELECT #LOADEDCARDID = LOADEDCARDID, #LOADEDAMOUNT = LOADEDAMOUNT FROM INSERTED
UPGRADE CARD SET CHARGE = CHARGE + #LOADEDAMOUNT
WHERE CARDID = #LOADEDCARDID
I run this code, but i am having an error which is "ORA-04071: missing BEFORE, AFTER or INSTEAD OF keyword". I have AFTER statement, i dont understand what is the problem.
You have used syntax is for sql server.
Try this:
CREATE OR REPLACE TRIGGER TRG_LOADMONEY
AFTER INSERT
ON KIOSK FOR EACH ROW
/*
These variables are not required because Oracle triggers can reference the table's
columns directly using the :OLD and :NEW prefixes.
DECLARE
LOADEDCARDID PLS_INTEGER;
LOADEDAMOUNT PLS_INTEGER;
*/
BEGIN
UPDATE CARD
SET CHARGE = CHARGE + :NEW.LOADEDAMOUNT
WHERE CARDID = :NEW.LOADEDCARDID;
END;