I have the following procedure:
CREATE PROCEDURE foo ()
SELECT * FROM fooBar INTO TEMP tempTable;
-- do something with tempTable here
DROP TABLE tempTable;
END PROCEDURE;
What happens if there is an exception before the DROP TABLE is called? Will tempTable still be around after foo exits?
If so, foo could fail the next time it is called, because tempTable would already exist. How should that be handled.
EDIT: I am using informix 11.5
According to the documentation, temporary tables are dropped when the session ends.
As others stated, temporary tables last until you drop them explicitly or the session ends.
If the stored procedure fails because the table already exists, SPL generates an exception.
You can deal with exceptions by adding an ON EXCEPTION clause -— but you are entering one of the more baroque parts of SPL, Stored Procedure Language.
Here is a mildly modified version of your stored procedure - one that generates a divide by zero exception (SQL -1202):
CREATE PROCEDURE foo ()
define i integer;
SELECT * FROM 'informix'.systables INTO TEMP tempTable;
-- do something with tempTable here
let i = 1 / 0;
DROP TABLE tempTable;
END PROCEDURE;
execute procedure foo();
SQL -1202: An attempt was made to divide by zero.
execute procedure foo();
SQL -958: Temp table temptable already exists in session.
This shows that the first time through the code executed the SELECT, creating the table, and then ran foul of the divide by zero. The second time, though, the SELECT failed because the temp table already existed, hence the different error message.
drop procedure foo;
CREATE PROCEDURE foo()
define i integer;
BEGIN
ON EXCEPTION
DROP TABLE tempTable;
SELECT * FROM 'informix'.systables INTO TEMP tempTable;
END EXCEPTION WITH RESUME;
SELECT * FROM 'informix'.systables INTO TEMP tempTable;
END;
-- do something with tempTable here
let i = 1 / 0;
DROP TABLE tempTable;
END PROCEDURE;
The BEGIN/END block limits the exception handling to the trapped statement. Without the BEGIN/END, the exception handling covers the entire procedure, reacting to the divide by zero error too (and therefore letting the DROP TABLE work and the procedure seems to run successfully).
Note that temptable still exists at this point:
+ execute procedure foo();
SQL -1202: An attempt was made to divide by zero.
+ execute procedure foo();
SQL -1202: An attempt was made to divide by zero.
This shows that the procedure no longer fails because the temp table is present.
You can limit the ON EXCEPTION block to selected error codes (-958 seems plausible for this one) by:
ON EXCEPTION IN (-958) ...
See the IBM Informix Guide to SQL: Syntax manual, chapter 3 'SPL Statements'.
For Informix 12.10 SPL Statements
For Informix 11.70 SPL Statements
For Informix 11.50 SPL Statements
Note that Informix 11.70 added the 'IF EXISTS' and 'IF NOT EXISTS' clauses to CREATE and DROP statements. Thus, you might use the modified DROP TABLE statement:
DROP TABLE IF EXISTS tempTable;
Thus, with Informix 11.70 or later, the easiest way to write the procedure is:
DROP PROCEDURE IF EXISTS foo;
CREATE PROCEDURE foo()
define i integer;
DROP TABLE IF EXISTS tempTable;
SELECT * FROM 'informix'.systables INTO TEMP tempTable;
-- do something with tempTable here
let i = 1 / 0;
DROP TABLE tempTable; -- Still a good idea
END PROCEDURE;
You could also use this, but then you get the previous definition of the procedure, whatever it was, and it might not be what you expected.
CREATE PROCEDURE IF NOT EXISTS foo()
define i integer;
DROP TABLE IF EXISTS tempTable;
SELECT * FROM 'informix'.systables INTO TEMP tempTable;
-- do something with tempTable here
let i = 1 / 0;
DROP TABLE tempTable; -- Still a good idea
END PROCEDURE;
I finally used a variation of Jonathan's and RET's solution:
CREATE PROCEDURE foo ()
ON EXCEPTION IN (-206)
END EXCEPTION WITH RESUME;
DROP TABLE tempTable;
SELECT * FROM fooBar INTO TEMP tempTable;
-- do something with tempTable here
DROP TABLE tempTable;
END PROCEDURE;
Yes, the temp table will still exist. Temp tables by definition have a lifetime of the session that created them, unless explicitly dropped.
The temp table can only be seen by the session that created it, and there is no impediment to the same procedure being run in parallel by multiple users. Adam's answer to test for the existence of the temp table will return a non-zero result if any user is running the procedure. You need to test that the session that owns the temp table is the current session as well. Given that this question is within the scope of a stored procedure, it might be simpler to add an explicit DROP, wrapped in some exception handling.
SELECT count(*)
INTO w_count
FROM sysmaster:systabnames s,sysmaster:systabinfo i
WHERE i.ti_partnum = s.partnum
AND sysmaster:BITVAL(i.ti_flags,'0x0020') = 1
AND s.tabname = 'tempTable' ;
If w_count is 1, delete table before SELECT ... INTO. Same with DROP TABLE.
Related
I discovered a mysterious table named num in my database which has one column named count. I had no idea how it got there, then I realized it might be caused by a misbehaving trigger.
I have a trigger function:
DECLARE num integer := 0;
BEGIN
IF ... THEN
SELECT COUNT(*) INTO num FROM ...
END IF;
IF num > 1 THEN
DELETE FROM ...
END IF;
RETURN NEW;
END;
As you can see my purpose is to count the rows returned by a query and perform some operation if it is greater than one.
Can this faulty code be responsible for the unwanted table created? If so, how to fix this?
SELECT ... INTO foo in PL/pgSQL stores the result of the SELECT in a PL/pgSQL variable foo. Whereas SELECT ... INTO foo run as an ordinary SQL statement creates a table foo to store the result.
This is what caused the confusion, the table was created when I was testing the SQL statements from the trigger function manually against the DB.
I have this procedure to create a table 'circle' and insert some radius and corresponding area to it, this is my code
create or replace procedure table1
is
BEGIN
execute immediate'drop table circle';
execute immediate'create table circle (r int, a int)';
end;
declare
r int;
ar float;
begin
for r in 3 .. 7 loop
ar:=3.14*r*r;
INSERT INTO circle VALUES(r,ar);
end loop;
execute immediate 'select * from circle';
end;
But when I run this I get this warning
Warning: Procedure created with compilation errors.
and when I try to find the table I get
SQL> select * from circle;
select * from circle
*
ERROR at line 1:
ORA-00942: table or view does not exist
what is wrong in my code?
In the code mentioned above, you're just creating a procedure. It also needs to be executed successfully before using the table in anonymous block.
I've created your procedure here and the procedure is created successfully.
Just when you try to execute it, handle the exception for when the table circle doesn't exist in your procedure's code and then execute (or call) it. Further, you could use the anonymous block to insert the values in your table.
If the table doesn't exist, attempting to drop it will fail. The rest of the code won't run, so your table is never created
Carry out your drop attempt and catch the error, then proceed
This answer has more info: Oracle: If Table Exists
I am running an oracle JOB which will run a PROCEDURE to CREATE TRUNCATE INSERT DROP some relevant tables.
Is this the best way to do a functionality like this ?
Should I Commit at the end of the procedure or not ?
CREATE OR REPLACE Procedure PR_NAME
IS
BEGIN
CREATE TABLE TABLE_1_BAC AS SELECT * FROM TABLE1_VIA_DBLINK;
TRUNCATE TABLE TABLE_1;
INSERT INTO TABLE_1 SELECT * FROM TABLE_1_BAC;
DROP TABLE TABLE_1_BAC;
--COMMIT;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
To create TABLE_1 only once for present data :
CREATE TABLE TABLE_1 AS SELECT * FROM TABLE1_VIA_DBLINK;
and creating an insert trigger for TABLE1_VIA_DBLINK, populating TABLE_1 through this trigger for new datas, and to get rid of this job and procedure seems more feasible.
As you stay in this job, perhaps you'll wait for huge data to be inserted.
By the way, if you insist on using this job, you don't need to issue commit, since and there's already an implicit commit exists inside job mechanism.
What kind of job do you use? JOB or SCHEDULER JOB?
I don't see any reason to DROP/CREATE the table. I don't see any reason why you use the intermediate table at all.
Simply make
CREATE OR REPLACE Procedure PR_NAME
IS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TABLE_1';
INSERT INTO TABLE_1 SELECT * FROM TABLE1_VIA_DBLINK;
COMMIT;
END;
You don't need any exception handler. In case of JOB you will not see the exception anyway. In case of SCHEDULER JOB you can see exception in views
*_SCHEDULER_JOB_LOG
*_SCHEDULER_JOB_RUN_DETAILS
If you make just this kind of operation you should consider MATERIALIZED VIEW which basically make the same: TRUNCATE and INSERT INTO ... SELECT * FROM ...
No. You don't need to commit.
Those commands are DDL (Data Definition Language) in SQL. So Oracle Database will issue a commit together with the command.
DML (Data Manipulating Language) - like SELECT, UPDATE, INSERT, DELETE requires a commit.
In a scenario, where you will update, delete and insert records. Then you ran a create table command. The records inserted, updated and deleted will be committed (save) to the database.
I have a transaction which reduces the variable with amount of money in loop, if the variable with money is below 0, the money amount should return to the value before transaction. How can I appropriately use rollback in MariaDB in this case?
---edit
I have something like that, and it doesn't work, check out the lines in if(budget<0) because if the money is below 0 and some, but not all of them, iterations were made and saved to the temp table, the table shows them
BEGIN
DECLARE temppesel text;
DECLARE tempsalary int;
DECLARE budget int DEFAULT cash;
DECLARE done bool DEFAULT false;
DECLARE occ CURSOR FOR (SELECT pesel, pensja FROM pracownicy where zawod=occupation);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
START TRANSACTION;
DROP TABLE IF EXISTS temp;
CREATE TABLE temp ( Result text );
OPEN occ;
occ : LOOP
FETCH occ INTO temppesel, tempsalary;
SET budget = budget - tempsalary;
IF(done) THEN
LEAVE occ;
END IF;
IF(budget<0) THEN
ROLLBACK;
LEAVE occ;
END IF;
INSERT INTO temp VALUES (concat('********',substr(temppesel,9,3), ', wyplacono'));
END LOOP;
CLOSE occ;
SELECT * FROM temp;
DROP TABLE temp;
COMMIT;
END
I believe that the CREATE TABLE statements are causing the transaction to be committed. Here is a list of commands that cause an implicit COMMIT.
As the aforementioned link describes, you can either move the START TRANSACTION statement after the DROP and CREATE commands or use the CREATE TEMPORARY TABLE syntax to create a temporary table:
CREATE TEMPORARY TABLE temp ( Result text );
BEGIN;
do some SQL
Loop:
do some SQL
if something is wrong, ROLLBACK and exit the loop and transaction
do some SQL
if something, go back to Loop
do some SQL
COMMIT;
That is, let ROLLBACK undo everything since the BEGIN.
More
Now that the SP is visible...
What engine is temp? If it MyISAM, then it is not rolled back. SHOW VARIABLES LIKE 'default_storage_engine';.
Please don't use occ for 2 different things, it confuses the reader.
Do you want the output to be part of the rows of pracownicy when the budget is blown? Or do you want no rows?
If you have multiple connections doing the same thing, there is a serious problem -- temp is visible to all connections, and they could step on each other. Change to CREATE TEMPORARY TABLE temp ...
However, with the pre-test (below), you can completely avoid the use of temp. First test for its need, then (if needed) simply do a single SELECT for all the rows.
If you want nothing, then a simple test something like this would pre-test whether it will overflow, therey obviating the need for testing in the loop:
..
IF ( SELECT SUM(pensja)
FROM pracownicy
where zawod=occupation ) > budget )
I have a question regarding ORACLE, I wrote a PLSQL CODE that checks if a table exists, if it exists then I select something from this table..pseudocode is like:
if (table exists)
Select from table where....
the problem is that I always get an error if the table does not exist, even if the if condition is never met and the select statement is never executed.
I think it is because my code is checked at compile time: "select from.." and then it prints an error if the table does not exist. How can I solve such an issue?.. here is how my code looks like (I used generic names):
DECLARE
v_table_exists NUMBER;
BEGIN
SELECT NVL(MAX(1), 0)
INTO v_table_exists
FROM ALL_TABLES
WHERE TABLE_NAME = 'TABLE_TEST';
IF v_table_exists = 1 THEN
INSERT INTO MY_TABLE(COLUMN1, COLUMN2, COLUMN3, COLUMN4)
SELECT 1234,
5678,
T.COLUMN_TEST1,
T.COLUMN_TEST2
FROM TABLE_TEST T
WHERE T.FLAG = 1;
END IF;
END;
The issue is exactly in the fact that your procedure con not be compiled as it refers to a non existing object; you may need some dynamic SQL for this; for example:
create or replace procedure checkTable is
vCheckExists number;
vNum number;
begin
-- check if the table exists
select count(1)
into vCheckExists
from user_tables
where table_name = 'NON_EXISTING_TABLE';
--
if vCheckExists = 1 then
-- query the table with dynamic SQL
execute immediate 'select count(1) from NON_EXISTING_TABLE'
into vNum;
else
vNum := -1;
end if;
dbms_output.put_line(vNum);
end;
The procedure compiles even if the table does not exist; if you call it now, you get:
SQL> select count(1) from NON_EXISTING_TABLE;
select count(1) from NON_EXISTING_TABLE
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> exec checkTable;
-1
PL/SQL procedure successfully completed.
Then, if you create the table and call the procedure again:
SQL> create table NON_EXISTING_TABLE(a) as select 1 from dual;
Table created.
SQL> exec checkTable;
1
PL/SQL procedure successfully completed.
The same way I showed a SELECT, you can do an UPDATE or whatever SQL query you need; if you do something different from a SELECT, the INTO clause has to be removed.
For example, say you need to insert into a different table, the above code should be edited this way:
if vCheckExists = 1 then
execute immediate 'insert into target(a, b, c) select a, 1, 100 from NON_EXISTING_TABLE';
end if;
Everything will need to be done in Dynamic SQL (DBMS_SQL) or EXECUTE_IMMEDIATE otherwise your code will never compile (or package will be invalided) if table does not exists.
DBMS_SQL Example
EXECUTE_IMMEDIATE Example
According to this article, in Oracle Database Server static SQL is indeed checked at compile time to ensure referenced objects exist.
So I advise you to use dynamic SQL instead of static SQL, through a varchar for example.