I have a stored procedure that drops/creates a table. The first time the procedure is created it succeeds and the procedure runs fine. However, if I try and replace the procedure it gives the error:
The name of the object to be created is identical to the existing name "TABLENAME" of type "TABLE".
It does this even though the procedure has a drop statement for "TABLENAME" immediately before the table creation statement. A stripped down example:
create or replace procedure example
LANGUAGE SQL
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '42704'
BEGIN END;
drop table TABLENAME;
create table TABLENAME(AN INTEGER);
END;
Is there any way to either get DB2 to recognize the drop statement or to ignore the error?
I'm on DB2 v10.1.0.3.
To run a series of commands as a stand-alone block of SQL, make sure that you use a different SQL delimiter instead of the semi-colon. The semi-colon is used as a statement terminator in SQL PL so you will need to use a different character to delimit multiple commands within your scripts. Change the semi-colon (";") at the end of procedure to something else like "#"
See SQL Procedural Language (SQL PL)
Related
In Informix, I can create procedures like these:
CREATE TABLE t (i INT);
CREATE PROCEDURE p1()
RETURNING INT
RETURN 1;
END PROCEDURE;
CREATE PROCEDURE p2()
RETURNING INT
INSERT INTO t VALUES (1);
RETURN 1;
END PROCEDURE;
Now, I can call both procedures using the EXECUTE syntax:
EXECUTE PROCEDURE p1();
EXECUTE PROCEDURE p2();
But I can only execute the first one in a SELECT statement:
-- Works
SELECT p1();
-- Fails with SQL Error [IX000]: Illegal SQL statement in SPL routine.
SELECT p2();
This is documented here in the manual.
I cannot seem to distinguish the two types of procedure from the dictionary, e.g.
SELECT procname, isproc
FROM sysprocedures
WHERE procname IN ('p1', 'p2');
Yields:
|procname|isproc|
|--------|------|
|p1 |f |
|p2 |f |
Other fields from sysprocname are also not helpful. How can I recognise a procedure that contains such "illegal SQL statements"? (I'd like to avoid parsing the sysprocbody contents)
i try to get an SP to run on DB2 connected with Squirrel
CREATE OR REPLACE PROCEDURE BOCA.TESTSP
(IN CASID INTEGER)
READS SQL DATA
DETERMINISTIC
LANGUAGE SQL
BEGIN
SELECT * FROM BOCA.TCASE C WHERE C.ID = CASID
END;
I get various errors based on where I put the ; (at end of statement etc)
i tried to follow this approach:
CREATE PROCEDURE [qualifier.]<procedure_name>
([IN | OUT | INOUT <argument_name> <datatype>,...])
{MODIFIES SQL DATA |
NO SQL |
CONTAINS SQL |
READS SQL DATA}
[[NOT] DETERMINISTIC]
LANGUAGE SQL
BEGIN [ATOMIC]
<procedure_body>
END
But did not succeed.
Anyone have a simple select that runs?
Stange is that an sample update I was able to create
Take some time to study the sample code that IBM supplies for SQL PL procedures, get these samples built and working in your environment. The samples are in the documentation, also they are on github, also they are in the SAMPLES directory of your Db2-server installation (for DB2 on Linux/Unix/Windows).
Your procedure has some mistakes:
missing statement separator after the SELECT statement
incorrect usage of SELECT in a routine. You either want to declare and open a cursor to return a result set to the caller or client, or you want to use SELECT...INTO to process the result of the query inside your routine.
missing valid separator at the end of the block of code (after the final END)
For SQuirrel SQL Client, before you connect to the database:
File > New Session Properties > SQL
(scroll down the list of properties until you see:
Statement Separator ;
Change the Statement Separator to #
Click OK.
Now connect to the database.
When you then type any SQL statement inside Squirrel (or a block, such as a trigger, stored-procedure, user defined function etc), you must now use the new statement separator instead of the previous default value at the end of the whole statement.
Inside of your routines , you will still need to use the semicolon to delimit statements inside the block, but remember to specify the new statement separator at the end of the block (after the final END in the stored procedure in your case).
I need to write a procedure in Redshift that will write to a table, but the table name comes from the input string. Then I declare a variable that puts together the table name.
CREATE OR REPLACE PROCEDURE my_schema.data_test(current "varchar")
LANGUAGE plpgsql
AS $$
declare new_table varchar(50) = 'new_tab' || '_' || current;
BEGIN
select 'somestring' as colname into new_table;
commit;
END;
$$
This code runs but it doesn't create a new table, no errors. If I remove the declare statement then it works, creating a table called "new_table". It's just not using the declared variable name.
It's hard to find good examples because Redshift is postgresql and all the postgresql pages say that it only has functions, not procedures. But Redshift procedures were introduced last year and I don't see many examples.
Well, when you are declaring a variable "new_table", and performing a SELECT ..INTO "new_table", the value is getting assigned to the variable "new_table". You will see that if you return your variable using a OUT parameter.
And when you remove the declaration, it simply work as a SELECT INTO syntax of Redshift SQL and creates a table.
Now to the solution:
Create a table using the CREATE TABLE AS...syntax.
Also you need to pass the value of declared variable, so use the EXECUTE command.
CREATE OR REPLACE PROCEDURE public.ct_tab (vname varchar)
AS $$
DECLARE tname VARCHAR(50):='public.swap_'||vname;
BEGIN
execute 'create table ' || tname || ' as select ''name''';
END;
$$ LANGUAGE plpgsql
;
Now if you call the procedure passing 'abc', a table named "swap_abc" will be created in public schema.
call public.ct_tab('abc');
Let me know if it helps :)
I am trying to write a stored procedure in AWS Redshift SQL and one of my parameters needs the possibility to have an integer list (will be using 'IN(0,100,200,...)' inside there WHERE clause). How would I write the input parameter in the header of the procedure so that this is possible (if at all?)
I've tried passing them in as a VARCHAR "integer list" type thing but wasn't sure then how to parse that back into ints.
Update: I found a way to parse the string and loop through it using the SPLIT_PART function and store all of those into a table. Then just use a SELECT * FROM table with the IN() call
What I ended up doing was as follows. I took in the integers that I was expecting as a comma-separated string. I then ran the following on it.
CREATE OR REPLACE PROCEDURE test_string_to_int(VARCHAR)
AS $$
DECLARE
split_me ALIAS FOR $1;
loop_var INT;
BEGIN
DROP TABLE IF EXISTS int_list;
CREATE TEMPORARY TABLE int_list (
integer_to_store INT
);
FOR loop_var IN 1..(REGEXP_COUNT(split_me,',') + 1) LOOP
INSERT INTO int_list VALUES (CAST(SPLIT_PART(split_me,',',loop_var) AS INT));
END LOOP;
END;
$$ LANGUAGE plpgsql;
So I would call the procedure with something like:
CALL test_string_to_int('1,2,3');
and could do a select statement on it to see all the values stored into the table. Then in my queries the need this parameter I ran:
.........................
WHERE num_items IN(SELECT integer_to_store FROM int_list);
with AdoQuery do
begin
Close;
SQL.Clear;
SQL.Add('SELECT (name+' '+surname+' '+father) as initihal, address from user');
Open;
end;
How to add space between name surname and father ?
i want to see result in DbGrid like that
Lionel Andrés Messi
The ADO layer/backend server should be fine with returning a column value which is an expression including a number of column values and string literals.
As you are trying to construct your SQL statement in Delphi source code (rarely a good idea, ime) you need to express what you want in a way which is syntactically correct from the compiler's point of view, as well as producing the desired result from your RDMS. To do this, all of the single quotes inside SQL.Add(...) except the outer two need to be doubled-up, otherwise you will get a syntax error on compilation.
However, even once you've done that, your SQL still may not execute correctly at run-time depending on whether your naming of the columns in your result-set is digestible by your back-end server. It's best to create and test your Select statement using whatever query tool is available for your RDMS, then attempt to set it up in Delphi only after you've got it working correctly from the RDMS's pov.
Btw, you will encounter fewer problems if you get into the habit of using the QuotedStr function in constructing the column values you want - you obviously need the syntax practice so I leave you to look that up in the Online Help.
Also, if you need to constrain the result set by specifying a Where clause, be sure to do it in a way which minimizes the risk of Sql Injection (https://en.wikipedia.org/wiki/SQL_injection) which arises when the SQL includes input text from the user. The bet way to do this is to use a parameterized Where clause.
If using inline SQL you have escape the quote characters or use a function that returns spaces. You will not be able to update the result. Another option is to add a calculated field in your Delphi ADOQuery component and do the combine client side in OnCalcFields.
with AdoQuery do
begin
Close;
SQL.Clear;
SQL.Add('SELECT name+'' ''+surname+'' ''+father as initihal, address from user');
Open;
end;
If using SQL Server for a database you can use the space() function.
with AdoQuery do
begin
Close;
SQL.Clear;
SQL.Add('SELECT name+ space(1) + surname+ space(1) +father as initihal, address from user');
Open;
end;