I'm doing something very similar to dnoeth's second comment on this question:
Insert Into table Teradata dynamic stored procedure SQL
I need to run it multiple times to loop the same insert statement but with different values for the "?" and I'm not sure how to go about that.
The dynamic value in my version is a date span. I can't run a massive insert without spooling out so I've broken the data into segments.
Thanks.
William,
As you are aware that you need to use cursor to iterate through each value you want to process.
For that purpose store dynamically created INSERT statement into a variable.
Final step: Use EXECUTE IMMEDIATE command to run insert statement stored in teradata variable
Here is the working sample that you may refer
REPLACE PROCEDURE td_user.sp_dynamic_insert( OUT proc_msg VARCHAR(5000) )
BEGIN
DECLARE lv_insert_txt VARCHAR(20000);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
/* Error handling code if required */
END;
L0:
FOR insert_cursor AS select_list
CURSOR FOR
SELECT Col2
FROM test_1
DO
SET lv_insert_txt = 'INSERT INTO test_2(Col1,Col2) VALUES('||TRIM(insert_cursor.Col2)||','||TRIM(insert_cursor.Col2)||')';
EXECUTE IMMEDIATE lv_insert_txt;
END FOR L0;
SET proc_msg = 'Procedure completed successfully';
END;
Related
I'm a SQL Server DBA currently getting up to speed on Oracle. I'm trying to create something very similar to sp_WhoIsActive for SQL Server but for Oracle without reinventing the wheel. Essentially all I'm doing is selecting some values from v$session and inserting them into a table (poor man's ASH/AWR).
It would seem that in Oracle 12.1, there's a bug when querying dictionary views where it can take forever due to bad parsing logic (Bug 22225899 : SLOW PARSE FOR COMPLEX QUERY). The work-around is to set a session parameter:
alter session set "_optimizer_squ_bottomup"=false;
In T-SQL, I could very easily execute a stored procedure in-session and set this variable at runtime. However in Oracle, it wouldn't seem thats the case.
Sample Code:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(
v_temp NUMBER :=1
) IS
BEGIN
alter session set "_optimizer_squ_bottomup"=false;
INSERT INTO SY_DB_ACTIVITY
SELECT
<fields>
FROM
v$session;
commit;
When I run this, I get the error:
"PLS-00103: Encountered symbol 'ALTER' when expecting one of the following..."
Right now, the only way I know how to do this is via a utility like SQL Plus that initiates an interactive user session. Can anyone give me some direction as to how Oracle handles this situation? I'd like to bundle this up into a SP or a Package and then call it from Oracle Scheduler.
Hre is a simple example how to execute alter session inside of the procedure:
CREATE PROCEDURE SP_DB_ACTIVITY IS
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
END;
/
Here is the way you can combine that with your select and insert statement:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
SELECT 1
INTO v_Id
FROM dual;
INSERT INTO SY_DB_ACTIVITY (id) VALUES(v_Id);
END SP_DB_ACTIVITY;
/
Here is a small DEMO where you can see what will procedure do when you call it and how you can call it. Also, in this example you are calling procedure with and IN parameter. So you can use that parameter for something and in the example above is the procedure without any parameters...
You can also, of course, insert into table directly:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
INSERT INTO SY_DB_ACTIVITY(id)
select 1
from dual;
END SP_DB_ACTIVITY;
/
The Problem: I have many delete lines in a PostgreSQL script where I am deleting data related to the same item in the database. Example:
delete from <table> where <column>=180;
delete from <anothertable> where <column>=180;
...
delete from <table> where <column>=180;
commit work;
There are about 15 delete statements deleting data that references <column>=180.
I have tried to replace the 180 with a variable so that I only have to change the variable, instead of all the lines in the code (like any good programmer would do). I can't seem to figure out how to do it, and it's not working.
NOTE: I am very much a SQL novice (I rarely use it), so I know there's probably a better way to do this, but please enlighten me on how I can fix this problem.
I have used these answers to try and fix it with no luck: first second third. I've even gone to the official PostgreSQL documentation, with no luck.
This is what I'm trying (these lines are just for testing and not in the actual script):
DO $$
DECLARE
variable INTEGER:
BEGIN
variable := 101;
SELECT * FROM <table> WHERE <column> = variable;
END $$;
I've also tried just delcaring it like this:
DECLARE variable INTEGER := 101;
Whenever I run the script after replacing one of the numbers with a variable this is the error I get:
SQL Error [42601]: ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
Can someone tell me where I'm going wrong? It would be nice to only have to change the number in the variable, instead of in all the lines in the script, and I just can't seem to figure it out.
As #Vao Tsun said, you must define a destination to your SELECT statement. Use PERFORM otherwise:
--Test data
CREATE TEMP TABLE my_table (id, description) AS
VALUES (1, 'test 1'), (2, 'test 2'), (101, 'test 101');
--Example procedure
CREATE OR REPLACE FUNCTION my_procedure(my_arg my_table) RETURNS VOID AS $$
BEGIN
RAISE INFO 'Procedure: %,%', my_arg.id, my_arg.description;
END
$$ LANGUAGE plpgsql;
DO $$
DECLARE
variable INTEGER;
my_record my_table%rowtype;
BEGIN
variable := 101;
--Use your SELECT inside a LOOP to work with result
FOR my_record IN SELECT * FROM my_table WHERE id = variable LOOP
RAISE INFO 'Loop: %,%', my_record.id, my_record.description;
END LOOP;
--Use SELECT to populate a variable.
--In this case you MUST define a destination to your result data
SELECT * INTO STRICT my_record FROM my_table WHERE id = variable;
RAISE INFO 'Select: %,%', my_record.id, my_record.description;
--Use PERFORM instead of SELECT if you want to discard result data
--It's often used to call a procedure
PERFORM my_procedure(t) FROM my_table AS t WHERE id = variable;
END $$;
--DROP FUNCTION my_procedure(my_table);
In my stored procedures I'm doing a lot of:
EXEC SQL EXECUTE ...
EXEC SQL FETCH ...
However, I'm getting some errors (Error 23506: End of table in cursor) when the execute statement doesn't return anything and I'm trying to fetch the result.
Are there anyway to check if the result contains anything before trying to fetch it?
I've tried SQLSUCCESS and SQLROWCOUNT, but SQLSUCCESS only tells me if the statement doesn't fail, not if it returns anything, and SQLROWCOUNT apparently only works for inserts, updates and deletes. Not select statements.
This reference compares DB2 and SolidDB procedures.
In the section "Listing 15. SQLSUCCESS showing end of result set", the IBM reference shows this snippet.
EXEC SQL FETCH sel_tab;
WHILE SQLSUCCESS LOOP
EXEC SQL FETCH sel_tab;
END LOOP
It also says
When the FETCH cursor statement fails
and does not find another row to
retrieve, the value of SQLSUCCESS is
set to 0 and the WHILE LOOP ends.
That pretty much tells me that the WHILE loop shouldn't even be entered if that first EXEC SQL FETCH doesn't find a row. But you seem to be suggesting that isn't happening in your code.
Later, in "Table 7. solidDB SQLERROR of cursorname statement", it shows this code. (My annotations.)
"CREATE PROCEDURE tabs_in_schema (schema_nm
VARCHAR) RETURNS (nr_of_rows INTEGER)
BEGIN
DECLARE tab_nm VARCHAR;
EXEC SQL PREPARE sel_tab -- A SELECT statement
SELECT table_name FROM sys_tables
WHERE table_schema = ?;
EXEC SQL PREPARE ins_tab
INSERT INTO my_table (table_name,
schema) VALUES (?,?);
nr_of_rows := 0;
EXEC SQL EXECUTE sel_tab USING -- Executes the SELECT
(schema_nm)INTO (tab_nm);
EXEC SQL FETCH sel_tab; -- EXEC SQL FETCH first row
WHILE SQLSUCCESS LOOP -- Like listing 15
nr_of_rows := nr_of_rows + 1;
EXEC SQL EXECUTE ins_tab USING
(tab_nm, schema_nm);
IF SQLROWCOUNT <> 1 THEN
RETURN SQLERROR OF ins_tab;
END IF;
EXEC SQL FETCH sel_tab; -- FETCH subsequent rows
END LOOP
END";
I suppose you could execute something like
SELECT COUNT(your_column_name)
FROM your_table
WHERE ...;
COUNT() will always return at least one row as long as your query is valid. But that involves more round trips to the database. I think you're better off sticking to the idiom of trying to fetch a row, and trapping the error.
I need to run big queries (that was a part of SP) and look at their results (just trying to find a bug in a big SP with many unions. I want to break it into parts and run them separately).
How can I do that if this SP have few parameters? I don't want to replace them in code, it would be great just to add declare in a header with a hardcode for this parameter.
I've tried something like this:
DECLARE
p_asOfDate DATE := '22-Feb-2011';
BEGIN
SELECT * from myTable where dateInTable < p_asOfDate;
END
But it says that I should use INTO keyword. How can I view this results in my IDE? (I'm using Aqua data studio)
I need to do that very often, so will be very happy if will find a simple solution
You are using an anonymous block of pl/sql code.
In pl/sql procedures you need to specify a target variable for the result.
So you first need to define a variable to hold the result in the declare section
and then insert the result data into it.
DECLARE
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
select * into p_result from myTable where dateInTable < p_asOfDate;
END
That said you will probaply get more than one row returned, so I would use
a cursor to get the rows separately.
DECLARE
CURSOR c_cursor (asOfDate IN DATE) is
select * from myTable where dateInTable < asOfDate;
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
OPEN c_cursor(p_asOfDate);
loop
FETCH c_cursor into p_result;
exit when c_cursor%NOTFOUND;
/* do something with the result row here */
end loop;
CLOSE c_cursor;
END
To output the results you can use something like this for example:
dbms_output.put_line('some text' || p_result.someColumn);
Alternatively you can execute the query on an sql command-line (like sqlplus)
and get the result as a table immediately.
I hope I understood your question correctly...
update
Here is a different way to inject your test data:
Use your tools sql execution environemnt to submit your sql statement directly without a pl/sql block.
Use a "&" in front of the variable part to trigger a prompt for the variable.
select * from myTable where dateInTable < &p_asOfDate;
The Result should be displayed in a formatted way by your tool this way.
I do not know about Aqua, but some tools have functions to define those parameters outside the sql code.
Can i use insert into tables in a procedure (on oracle) ? example:
procedure my_procedure (aa1 number ,aa2 number ) is
begin
insert into lam_table values(aa1,aa2,null) ;(*ofcourse depending on the tables )
...
...
end ;
** note
i tried it and it worked but there were a message in the bottom that said (successfully compiled not modified )
Yes, you can. Just be aware of the difference between creating the procedure and executing it. Once the procedure is created, you can execute it with:
begin
my_procedure(aa1, aa2);
end;
where aa1 and aa2 are the supplied values for the args.
Just as dpbradley says.
Also, any insert performed by your insert statement will only be visible in that session unless you do a
commit;