exec home_lending_cus('9999999999', 'HOME LENDING', '11111111')
I am trying to run the above statement but it is throwing a error mentioned below.
ORA-00933: SQL Command not properly ended.
ORA-06512: at 'HOME_LENDING_CUS', line 6
ORA-06512: at line1
All the variables of type varchar2
CREATE OR REPLACE PROCEDURE home_lending_cus(
id_no VARCHAR2,
prod_nme VARCHAR2,
rpt_dte NUMBER) authid current_user
AS
BEGIN
EXECUTE immediate
' CREATE TABLE abc AS
SELECT bt.id,
ct.cus_id
FROM bnkr_tbl bt ,
cus_tbl ct ,
base_tbl bt
WHERE bt.id =ct.id
AND ct.id =bt.c_id
AND bt.pr_nme='||prod_nme|| '
AND bt.dte ='||rpt_dte|| '
AND bt.id ='||id_no|| '
GROUP BY bt.id,
ct.cus_id';
END home_lending_cus;
As #Aramillo pointed out, the quotes around the string variables are wrong, which is making the dynamic SQL not work. However, I would strongly recommend shifting to bind variables, rather than concatenation. Not only will this prevent that kind of error, it will protect you from SQL injection:
CREATE OR REPLACE PROCEDURE home_lending_cus(
id_no VARCHAR2,
prod_nme VARCHAR2,
rpt_dte NUMBER) authid current_user
AS
BEGIN
EXECUTE immediate
' CREATE TABLE abc AS
SELECT bt.id,
ct.cus_id
FROM bnkr_tbl bt ,
cus_tbl ct ,
base_tbl bt
WHERE bt.id =ct.id
AND ct.id =bt.c_id
AND bt.pr_nme= :1
AND bt.dte = :2
AND bt.id = :3
GROUP BY bt.id,
ct.cus_id' using prod_nme, rpt_dte, id_no;
END home_lending_cus;
Or, rather, it would if you were allowed to use bind variables in DDL. Given that limitation, my inclination would be to split this into two commands.
CREATE OR REPLACE PROCEDURE home_lending_cus(
id_no VARCHAR2,
prod_nme VARCHAR2,
rpt_dte NUMBER) authid current_user
AS
BEGIN
EXECUTE immediate
' CREATE TABLE abc (bt_id number, cus_id number)'
EXECUTE IMMEDIATE
'INSERT INTO abc (bt_id, cus_id)
SELECT bt.id,
ct.cus_id
FROM bnkr_tbl bt ,
cus_tbl ct ,
base_tbl bt
WHERE bt.id =ct.id
AND ct.id =bt.c_id
AND bt.pr_nme= :1
AND bt.dte = :2
AND bt.id = :3
GROUP BY bt.id,
ct.cus_id' using prod_nme, rpt_dte, id_no;
END home_lending_cus;
There are a couple other issues with this code that you may want to consider:
Your query is also invalid because you're using the alias bt twice in the same FROM clause.
I'd recommend using SQL-99 style joins, rather than the comma-separated list of tables.
Oracle code that creates a table on the fly is always a little suspect. do you really need to create a table, or can you use a global temporary table? The latter is almost always going to be the better option.
Related
I want to replace the domain name using cursor without a loop, which is an explicit cursor. but I also want to changes all the same domain names in the database by given pass a string.
example: exec PR_Q3( 'usa.com','hotmail.com'); all the domain name with 'usa.com'in database will change to 'hotmail.com'.
create or replace procedure PR_Q3
(old_email in varchar2, new_email in varchar2)
authid current_user
is
cursor E_info is select Email_Address from broker
where REGEXP_LIKE (substr(Email_Address, instr(Email_Address,'#')+1), old_email);
v_email E_info%rowtype;
begin
open E_info;
loop
fetch E_info into v_email;
exit when E_info%notfound;
update broker set
Email_Address = replace(Email_Address,substr(Email_Address,instr(Email_Address,'#')+1),new_email)
where Email_Address = v_email.Email_Address;
end loop;
close E_info;
end PR_Q3;
it works if I delete the loop, but it only changes the domain name once.
I need changes all the same domain name.
I want to do the same thing without a loop. Can I?
You can use the following simple update in your procedure:
CREATE OR REPLACE PROCEDURE PR_Q3 (
OLD_EMAIL IN VARCHAR2,
NEW_EMAIL IN VARCHAR2
)
AUTHID CURRENT_USER
IS
BEGIN
UPDATE BROKER
SET
EMAIL_ADDRESS = REPLACE(EMAIL_ADDRESS, OLD_EMAIL, NEW_EMAIL)
WHERE
REGEXP_LIKE ( EMAIL_ADDRESS,
'.*#' || OLD_EMAIL || '$' );
COMMIT;
END PR_Q3;
/
Cheers!!
[EDIT]Editing the code to reflect changes coming from comments
I have a problem with one of the stored procedures I'm trying to create in an Oracle database.
The goal is to update every table which has an indiv column.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
AS
BEGIN
FOR item IN (
select table_name , owner
from all_tab_columns
where column_name = 'INDIV' AND OWNER ='K'
)
LOOP
EXECUTE IMMEDIATE 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue' USING newU, lastU;
END LOOP;
END sp_majUserOnAllK;
exec sp_majUserOnAllK( 'hum','hum');
Problem is, when I try to execute the stored procedure, I got an error message with no detail at all ('non valid SQL').
I tried taking the code out of the stored procedure. And there, it works. Only the beginning is changing to :
DECLARE
newU NVARCHAR2(50);
lastU NVARCHAR2(50);
req VARCHAR2(100);
CURSOR ctable IS
select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='KEXPLOIT';
BEGIN
newU := 'hum';
lastU := 'hum';
FOR item IN ctable
....
Like that, it works perfectly and does exactly what it is supposed to do.
As the only difference is the assignation of the variable, I think I may have a problem with my procedure declaration but I can't find a solution. The compilation is ok.
Any idea ?
Your procedure's syntax is not correct. Try this.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
IS
req VARCHAR2(100);
BEGIN
FOR item IN (select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='K')
LOOP
req := 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue';
EXECUTE IMMEDIATE req USING newU, lastU;
END LOOP;
-- return 1; -- note: procedures do not return values
END;
/
A five-second Google search on "dbeaver exec command" brought this up among the first few hits:
https://github.com/dbeaver/dbeaver/issues/749
In it, we learn that EXEC is not supported by dbeaver.
EXEC is an SQL*Plus command. It is not Oracle SQL, and it is not PL/SQL. SQL*Plus is a shell program of sorts for interacting with Oracle databases; it has its own language, distinct from SQL and PL/SQL.
SQL Developer and Toad (and perhaps other similar programs) support (most of) SQL*Plus, but apparently dbeaver (with which I am not familiar) does not.
The link I copied above suggests using the CALL command instead. See the link for examples.
As an aside, when we use EXEC in SQL*Plus and SQL Developer, there is no semicolon at the end of the procedure call. Adding an unnecessary semicolon, however, does not throw an error (SQL*Plus is, apparently, smart enough to simply ignore it).
I'm trying to write a procedure that can take a column name as an input parameter and select that column from a table.
I know you can do this in a regular query by simply creating a string variable and referencing the column like such:
DEFINE mycol = 'column1'
SELECT a.&mycol FROM table1 a
This does however not work within a procedure. Variables in procedures don't appear to be able to be referenced by the '&' symbol, and as such when I attempt to pull a column using an input parameter it tells me that it's an invalid identifier.
Searching around on the internet, I can't find an example of someone doing this without dynamic SQL, when inside a proc. I would prefer not to use dynamic SQL if possible.
Does anyone know a workaround for this, or is dynamic SQL required in procs for this to occur?
Cheers,
Ollie
Dynamic SQL in named block is inevitable as ampersand construct are not compliant with SQL standard. It's only feature of SQL Plus engine.
I have tested the scenarios as described.
CREATE TABLE SCOTT.COLUMN_LIST
(
COLUMN_ID NUMBER,
COLUMN_NAME VARCHAR2(200),
TABLE_NAME VARCHAR2(50 )
);
Insert into SCOTT.COLUMN_LIST
(COLUMN_ID, COLUMN_NAME, TABLE_NAME)
Values
(1, 'DEPTNO', 'DEPT');
COMMIT;
CREATE TABLE SCOTT.TEST1
(
A VARCHAR2(300 BYTE)
);
CREATE OR REPLACE PROCEDURE PROC1
IS
v_Select_Column VARCHAR2(200);
v_Table_Name VARCHAR2(50 );
V_SQL VARCHAR2(300);
BEGIN
SELECT COLUMN_NAME,TABLE_NAME
INTO v_Select_Column,v_Table_Name
FROM COLUMN_LIST
WHERE COLUMN_ID=1;
V_SQL:= 'SELECT '||v_Select_Column||
' FROM '||v_Table_Name;
EXECUTE IMMEDIATE V_SQL;
INSERT INTO TEST1 values(V_SQL);
COMMIT;
END PROC1;
/
Please test at your end. You can see the output on test1 table.
If I run this static SQL in ORACLE SQL DEVELOPER:
SELECT appl_id
FROM grant_appls where full_appl_num IN(
'1R01HL129077-01','2R01HL075494-10A1','2P01HL062426-16') AND SUBPROJECT_ID is not null;
I get these results:
APPL_ID
8855105
8855112
8855104
8855108
8855109
8855107
8855106
Now I write the PROCUDERE and put the Static SQL in there:
create or replace PROCEDURE GET_APPLIDS_BY_FULL_GRANT_NUM (
fullGrantNumList IN VARCHAR2,
applIdRecordSet OUT SYS_REFCURSOR)
AS
BEGIN
OPEN applIdRecordSet FOR
SELECT appl_id
FROM grant_appls where full_appl_num IN
(
'1R01HL129077-01','2R01HL075494-10A1','2P01HL062426-16'
) AND SUBPROJECT_ID is not null;
END GET_APPLIDS_BY_FULL_GRANT_NUM;
I could have sworn at one point I was getting results since I have the static comma delimeted list.
But now I can't even get results with this.
The Ouput Variables window has the variable APPLIDRECORDSET but there are no values for APPL_ID.
The final version should look something like this:
create or replace PROCEDURE GET_APPLIDS_BY_FULL_GRANT_NUM (
fullGrantNumList IN VARCHAR2,
applIdRecordSet OUT SYS_REFCURSOR)
AS
BEGIN
OPEN applIdRecordSet FOR
SELECT appl_id
FROM grant_appls where full_appl_num IN
(
fullGrantNumList
) AND SUBPROJECT_ID is not null;
END GET_APPLIDS_BY_FULL_GRANT_NUM;
So of course when I run this I am getting back null:
VARIABLE cur REFCURSOR
EXECUTE GET_APPLIDS_BY_FULL_GRANT_NUM("'1R01HL129077-01','2R01HL075494- 10A1','2P01HL062426-16'",:cur);
SELECT :cur FROM dual;
Your parameter fullGrantNumList won't be used by Oracle the way you think. Oracle takes the bind variables and treats it as one value, it doesn't do a text replace like you think. Here is what is actually happening to your query:
select appl_id from grant_appls where full_appl_num in ('''1R01HL129077-01'',''2R01HL075494- 10A1'',''2P01HL062426-16''') amd subproject_id is null;
This is actually one of the nice things about bind variables is that they protect you from SQL Injection attacks.
My recommendation would be to either pass in a list of values as a table type or convert the statement to a string and use dynamic SQL to execute it.
Dynamic SQL
I have got stuck in below and getting syntax error - Please help.
Basically I am using a collection to store few department ids and then would like to use these department ids as a filter condition while inserting data into emp table in FORALL statement.
Below is sample code:
while compiling this code i am getting error, my requirement is to use INSERT INTO table select * from table and cannot avoid it so please suggest.
create or replace Procedure abc(dblink VARCHAR2)
CURSOR dept_id is select dept_ids from dept;
TYPE nt_dept_detail IS TABLE OF VARCHAR2(25);
l_dept_array nt_dept_detail;
Begin
OPEN dept_id;
FETCH dept_id BULK COLLECT INTO l_dept_array;
IF l_dept_array.COUNT() > 0 THEN
FORALL i IN 1..l_dept_array.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO stg_emp SELECT
Dept,''DEPT_10'' FROM dept_emp'||dblink||' WHERE
dept_id = '||l_dept_array(i)||'';
COMMIT;
END IF;
CLOSE dept_id;
end abc;
Why are you bothering to use cursors, arrays etc in the first place? Why can't you just do a simple insert as select?
Problems with your procedure as listed above:
You don't declare procedures like Procedure abc () - for a standalone procedure, you would do create or replace procedure abc as, or in a package: procedure abc is
You reference a variable called "dblink" that isn't declared anywhere.
You didn't put end abc; at the end of your procedure (I hope that was just a mis-c&p?)
You're effectively doing a simple insert as select, but you're way over-complicating it, plus you're making your code less performant.
You've not listed the column names that you're trying to insert into; if stg_emp has more than two columns or ends up having columns added, your code is going to fail.
Assuming your dblink name isn't known until runtime, then here's something that would do what you're after:
create Procedure abc (dblink in varchar2)
is
begin
execute immediate 'insert into stg_emp select dept, ''DEPT_10'' from dept_emp#'||dblink||
' where dept_id in (select dept_ids from dept)';
commit;
end abc;
/
If, however, you do know the dblink name, then you'd just get rid of the execute immediate and do:
create Procedure abc (dblink in varchar2)
is
begin
insert into stg_emp -- best to list the column names you're inserting into here
select dept, 'DEPT_10'
from dept_emp#dblink
where dept_id in (select dept_ids from dept);
commit;
end abc;
/
There appears te be a lot wrong with this code.
1) why the execute immediate? Is there any explicit requirement for that? No, than don't use it
2) where is the dblink variable declared?
3) as Boneist already stated, why not a simple subselect in the insert statement?
INSERT INTO stg_emp SELECT
Dept,'DEPT_10' FROM dept_emp#dblink WHERE
dept_id in (select dept_ids from dept );
For one, it would make the code actually readable ;)