PostgreSQL: Declare a cursor for prepared statement - sql

The following DECLARE fails:
PREPARE stmt(bigint) AS SELECT ...;
DECLARE crs CURSOR FOR stmt;
According to https://www.postgresql.org/docs/9.6/static/sql-declare.html,
stmt has to be either SELECT or VALUES command.
I use PREPARE statement in a latency-critical section of the code in which thousands of quick queries are emitted. Parsing and generating a query plan each time would be performance killer. However, in some rare cases the query can return millions of records and the result doesn't fit into memory.
Is there a way to declare a cursor for prepared statement in PostgreSQL? If not, are there any workarounds?

https://www.postgresql.org/docs/9.1/static/ecpg-commands.html#ECPG-EXECUTING
This can be done through ECPG C library in c code.
One can create prepared statement in C code as:
EXEC SQL PREPARE stmt1 FROM "SELECT oid,datname FROM pg_database WHERE oid > ?";
and then create cursor for that statement as:
EXEC SQL DECLARE foo_bar CURSOR FOR stmt1;
fetch result form cursor within infinite loop.
EXEC SQL OPEN foo_bar USING 100;
while(1){
EXEC SQL FETCH NEXT FROM foo_bar INTO :dboid, :dbname;
}
This is available with 9.1 version of postgresql.

Related

prepare fetch statement in db2

I have been trying to prepare a Fetch statement in db2 as below
Stmt = %trim('FETCH EXSQCRS INTO ?');
EXEC SQL PREPARE SQLSTMT2 FROM :STMT ;
EXSQCRS is a cursor.
But this is failing with -104 error. Any clue on how to write this?
FETCH isn't a statement that can be prepared. It's simply an executable statement. See Actions allowed on SQL statements
You need to read up on Embedded SQL programming
Simple example with dynamic SQL
D EMPNUM S 6A
D NAME S 15A
D STMT S 500A INZ('SELECT LASTNAME -
D FROM CORPDATA.EMPLOYEE WHERE -
D EMPNO = ?')
//************************************************************
// Prepare STMT as initialized in declare section *
//************************************************************
/FREE
EXEC SQL
PREPARE S1 FROM :STMT;
//
//************************************
// Declare Cursor for STMT *
//************************************
EXEC SQL
DECLARE C1 CURSOR FOR S1;
//
//****************************************************
// Assign employee number to use in select statement *
//****************************************************
EMPNUM = '000110';
//*********************
// Open Cursor *
//*********************
EXEC SQL
OPEN C1 USING :EMPNUM;
//
//**********************************************
// Fetch record and put value of *
// LASTNAME into NAME *
//**********************************************
EXEC SQL
FETCH C1 INTO :NAME;
You can't prepare the fetch. You can prepare the SQL statement that is used to define the cursor though. So it would look like this:
dcl-s stmt Varchar(256) Inz('');
exec sql declare S1 statement;
exec sql declare C1 cursor for S1;
stmt = 'select * from customer where cusno = ?';
exec sql prepare S1 from :stmt;
exec sql open C1 using :customerNumber;
exec sql fetch C1 into :customerDS;
dow %subst(sqlstate:1:2) = '00'
or %subst(sqlstate:1:2) = '01';
... process it here ...
exec sql fetch C1 into :customerDS;
enddo;
exec sql close C1;
Note there is no error checking here. You really want to do that on all executable sql statements. Note also that the exec sql declare ... statements are not executable. Not even the declare C1 cursor. I will usually put my sql in their own procedures. The prepare and open will go in one procedure, the fetch in another, and the close in another.
Some notes about using procedures and local variables with embedded sql. When using static cursors, you need to put the declare in the same procedure as the open because the host variables are in the declare statement, and they need to be the same variables in scope when you do the open. That is because the declare is totally commented out, and generates no code. It is not executable. The host variables appear in the code generated for the open in this case.
With prepared cursors, you do not need to put the declare in the same procedure as the open like you do with static cursors. That is because the host variables that are bound to parameter markers in the prepared statement appear in the open statement. The declare can be up at the head of the program with the global declarations. It is a global declaration after all regardless of where the declare is physically located. I like to explicitly declare my prepared statements even though you don't have to. I put them up with the cursor declarations.
I generally will have the fetch procedure return a boolean (indicator), or a record count so that I don't have to repeat the call in the code. The fetch is indeed repeated here just because I did not use procedures.
And finally, the one exception I have with testing errors is that I do not test for an error after the close because the only error that will throw is that the cursor isn't open which is not an error in my mind, it is what I want the state of the cursor to be when I am finished with it.

Execute string as SQL query in C

it's possible to execute a string as a SQL query in C and get the result? I'm trying to compile this code:
EXEC SQL
EXECUTE IMMEDIATE : sql_formula INTO :sql_prec_desconto_mp;
But I've this error:
Error at line 10548, column 35 in file
D:\syncs\AIX\fuentes\funcn\niv2\src\rutin as.pc
EXECUTE IMMEDIATE : sql_formula INTO :sql_prec_desconto_mp;
PCC-S-02201, Encountered the symbol "INTO" when expecting one of the
following:
; ( [ . ++ -- ->
How can I get the result of the SQL query? If I remove the INTO clause I can compile without errors.
Thanks in advance.
That format is described in the documentation:
To prepare and execute a DELETE, INSERT, or UPDATE statement or a PL/SQL block containing no host variables.
You aren't doing one of those DML types, and those don't recognise INTO. When you run a dynamic SQL query without an INTO the query is never actually executed (see the note in the docs).
The quickest way that immediately comes to mind to do what I think you want, is to prepare a cursor and fetch the result:
EXEC SQL PREPARE stmt FROM :sql_formula;
EXEC SQL DECLARE cur CURSOR FOR stmt;
EXEC SQL OPEN cur;
EXEC SQL FETCH cur INTO :sql_prec_desconto_mp;

Execute SQL command from within a resultset in stored procedure in DB2

I have the following stored procedure:
CREATE PROCEDURE SQLTEST()
LANGUAGE SQL
BEGIN ATOMIC
DECLARE SQLCMD VARCHAR(1024);
FOR v AS cur1 CURSOR FOR SELECT ID, CMD from COMMANDTBL
DO
SET SQLCMD= v.CMD;
"CALL" SQLCMD;
END FOR;
END?
The COMMANDTBL has the columns ID and CMD of which CMD has SQL Commands.
CMD is 1024 VARCHAR.
In this test they are 2 inserts for another table
INSERT INTO TARGETTBL(TARGET, TARINT) VALUES ('TEST', 100);
INSERT INTO TARGETTBL(TARGET, TARINT) VALUES ('TEST2', 200);
for example.
My problem is the "CALL" part. I have DB2 v9.7 but it would be great if there was a solution running on lower versions as well.
I have not found any pointers on how to run this and a simple EXEC or EXECUTE does not work.
Thank you for your help.
TheVagabond
You need to prepare and execute the statements because they are deemed "dynamic SQL".
PREPARE myStmt FROM SQLCMD;
EXECUTE myStmt;

can we use cursor for a UPDATE query?

Can we declare and open a cursor for UPDATE query also or is it only for SELECT queries?
EXEC SQL PREPARE S FROM :query;
EXEC SQL DECLARE C CURSOR FOR S;
DbUtilT::set_bind_variables(bind_dp,&paramList);
EXEC SQL OPEN C USING DESCRIPTOR bind_dp;
EXEC SQL WHENEVER NOT FOUND GOTO end_update_loop;
EXEC SQL FETCH C USING DESCRIPTOR bind_dp;
EXEC SQL COMMIT WORK;
Is this fine? Or should we use cursor only for SELECT statments then how do we execute UPDATE queries?
If query is something like:
SELECT id FROM mytable WHERE ... FOR UPDATE OF id
then you can do:
...
EXEC SQL FETCH C USING DESCRIPTOR bind_dp;
EXEC SQL UPDATE mytable SET id = <something> WHERE CURRENT OF C;
I'm not quite sure what you mean though; you don't have to use a cursor to do an update, you can do:
EXEC SQL UPDATE mytable SET id = <something> WHERE ...;
... or the equivalent prepared statement.
Have I completely misunderstood the question?

How can I truncate all tables from a MySQL Database?

Is there any way to truncate all tables from a specific MySQL database name without using any other language than SQL? I mean no linux shell scripts. (why? because it will run on windows, MacOSX and linux servers).
the problem is that the client its selecting the database name from a list in a control panel webpage (wich will be displaying MySQL databases from different servers *nix and windows), and then he will want to truncate all the tables inside that database (yes that is the main task of the web form).
Alex
Ok, I solved it by myself here is the stored procedure :)
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE truncatestmnt TEXT; -- this is where the truncate statement will be retrieved from cursor
-- This is the magic query that will bring all the table names from the database
DECLARE c1 CURSOR FOR SELECT Concat('TRUNCATE TABLE ', TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = "#DatabaseName";
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;
OPEN c1;
c1_loop: LOOP
FETCH c1 INTO truncatestmnt;
IF `done` THEN LEAVE c1_loop; END IF;
SET #x = truncatestmnt;
PREPARE stm1 FROM #x;
EXECUTE stm1;
END LOOP c1_loop;
CLOSE c1;
END
What I am making its calling all tables from the given database, this will help if the tables inside the given database have no pattern to follow.
So by calling DECLARE c1 CURSOR FOR SELECT Concat('TRUNCATE TABLE ', TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = "#DatabaseName"; and saving results into a cursor I can fetch all the TRUNCATE TABLE x statements generated by the "n" quantity of tables inside the given database, then by just preparing and executing each statement in the cursor it will truncate all the tables inside the given database.
BTW #DatabaseName must be given as parameter to the stored procedure
Hope this helps someone else too :)
Alex
create procedure drop_tables_like(pattern varchar(255), db varchar(255))
begin
select #str_sql:=concat('drop table ', group_concat(table_name))
from information_schema.tables
where table_schema=db and table_name like pattern;
prepare stmt from #str_sql;
execute stmt;
drop prepare stmt;
end
then call
call drop_tables_like('%', 'dababase_name')