create dynamic table within procedure? - oracle11g

I am trying to create dynamic table within procedure but i am getting error please
tell me whats the error
CREATE OR REPLACE PROCEDURE check_sms_bundle_25 (
MON VARCHAR2,
YEAR_P VARCHAR2 DEFAULT TO_CHAR (SYSDATE, 'YY'),
QUARTER VARCHAR2,
TYPE VARCHAR2 DEFAULT 'NEW')
IS
BEGIN
IF UPPER (QUARTER) = 1
THEN
EXECUTE IMMEDIATE 'BEGIN CREATE OR REPLACE TABLE (''SMS_Bundle_25_'''|| UPPER (MON)|| '''_Q'''|| UPPER (QUARTER)|| '||''_''||'''|| UPPER (YEAR_P)|| ''')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''
END';
END IF;
END;
The error msg is
ORA-06550: line 2, column 4: PLS-00103: Encountered the symbol
"CREATE" when expecting one of the following:
begin case declare exit for goto if loop mod null pragma raise
return select update while with << close current delete
fetch lock insert open rollback savepoint set sql execute commit
forall merge pipe ORA-06512: at "FI_SDINE.CHECK_SMS_BUNDLE_25", line 9
ORA-06512: at line 1

As well as 'or replace' not being valid as part of the create table syntax, you're enclosing the DDL statement inside another anonymous PL/SQL block, you're trying to put the table name inside parentheses, and you're including quote marks - which are not allowed in an (unquoted) object identifier. So if you pass in argukments AUG, 14, 1 NEW then your dynamic statement is trying to run:
BEGIN CREATE OR REPLACE TABLE ('SMS_Bundle_25_'AUG'_Q'1||'_'||'14')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
END
The BEGIN/END make is a block and the DDL statement isn't valid in PL/SQL; that's why you're having to use execute immediate in the first place. If you change your construction to:
EXECUTE IMMEDIATE 'CREATE TABLE SMS_Bundle_25_'|| UPPER (MON)
|| '_Q'|| UPPER (QUARTER) ||'_' || UPPER (YEAR_P)|| '
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''');
you'd then be trying to run:
CREATE TABLE SMS_Bundle_25_AUG_Q1_14
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
which at least looks more viable, assuming the tables you're selecting from exist. No idea why you're passing the year and quarter numbers as strings or applying upper() to them. And presumably you really want the select to be filtered on the same year and month as the table name.
It's useful to display the command you're trying to execute, for example with dbms_output, to see exactly what you've created; and you can then also run that manually to see where it's going wrong more clearly.
But as noted in comments, it's unusual to create objects from a procedure like this. Your schema should usually be static, not modified on the fly. You won't be able to refer to this table from other code unless that is also being built dynamically. You seem to be creating a table as a snapshot of the other tables; a view or a materialised view might be more appropriate. But I'm not really sure why you're doing this so it's not entirely clear what you should be doing instead.

Related

Teradata dynamic SQL getting code from another table

I am trying to recreate a query that uses a subquery in the left join to get the value of a KPI. I am trying to store that subquery in another table as a string and use dynamic sql to retrieve that string code and insert it into the left join, but I am getting an error:
[5526] SPL2010:E(L27), Variable of CHARACTER type expected in PREPARE or EXECUTE IMMEDIATE statement.
Here is the procedure I am trying to create. In the variable metric_code, the column SQL is a string that contains the sql code.
Create PROCEDURE dyn_sql_test()
BEGIN
DECLARE metric_code VARCHAR(5000);
Set metric_code = (Select SQL_CODE from mytable_with_Sql_code where ID=1234);
BEGIN
set get_value='
SELECT
e.*
,a.value
FROM Metric_table AS e
LEFT JOIN ('||metric_code||') a
on e.id=a.id
WHERE e.ID = 1234;';
EXECUTE IMMEDIATE get_value;
END;
END;
In case it helps, the string I am retrieving for SQL_CODE, which used to go in the Left Join as a subquery looks something like this:
sel id,type,values
from my_values_table
where type='kpi'

Create a table as, where 'date condition' in dynamic PL/SQL

I got assigned the following task.
Assume we have a table A structured with an id column and a date column.
Write a procedure in PL/SQL that: takes as parameters the table name (in our case A) and a date D, creates a backup table named A_bck containing only the records of A with dates < D and removes from the table A all the records inserted in A_bck.
Here there is my code.
Unluckily I get this error:
Error report -
ORA-00904: "MAY": invalid identifier
ORA-06512: at line 41
ORA-06512: at line 80
00904. 00000 - "%s: invalid identifier"
If I try to achieve the same result using a where condition on the id column instead that on the date one, I have no problems.
Where is the mistake? Am I implementing it completely in the wrong way?
The problem you have is that as you're executing dynamic sql you're query is built up as a string. Oracle does not know that the date you've given is actually a date, it is simply being treated as part of the string. To solve this you should be able to do the following:
my_query := 'CREATE TABLE ' || table_name_backup || ' AS (SELECT * FROM ' || table_name || ' WHERE table_date < to_date(''' || backup_date || '''))';
This should sort out your issue for you. As a side note, you will probably want to change your "table_exists" query, as table names are all stored in upper case, e.g.
SELECT COUNT(*) INTO table_exists FROM USER_TABLES WHERE TABLE_NAME = upper(my_table);
Edit: Further explanation following comment
To explain why you don't have the above problem when using integers, it is important to remember that using execute immediate simply executes the given string as an SQL query.
For example:
declare
x INTEGER := 1;
i integer;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where 1 = ' || x;
EXECUTE IMMEDIATE my_query INTO i;
end;
my_query in the above example would be executed as:
select 1 from dual where 1 = 1
which is perfectly valid sql. In your example however, you were ending up with something like this:
CREATE TABLE abaco_bck AS (SELECT * FROM abaco WHERE table_date < 27-MAY-17)
As it isn't wrapped in quotes, or explicitly converted to a date, the SQL engine is trying to subtract "MAY" from 27, but it doesn't know what "MAY" is.
One other thing to mention, is that for some operations you could use bind variables instead of quotes (although you can't for DDL) e.g.
declare
lToday DATE := SYSDATE;
i INTEGER;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where sysdate = :1';
EXECUTE IMMEDIATE my_query INTO i USING lToday;
end;

How to use a bind variable in trigger body?

I'm new to PL/SQL. I'm using oracle 11g XE along with sql developer. I'm trying to create to create an after insert trigger as follows
create or replace trigger tr1
after
insert ON
employee
for each row
begin
print :new.emp_id;
end;
The employee table is as follows
create table employee
( emp_id varchar2(5) primary key,
emp_name varchar2(10),
salary number,
company varchar2(10) foreign key references companies(comp_name)
);
When I run the statement I got a 'enter binds' window for the bind variable :new. But I was confused that why do I need to enter the value for :new since it is pseudorecord. Then I entered 'employee' as the values for :new. Now I'm getting the following error.
Error(2,8): PLS-00103: Encountered the symbol "" when expecting one of the following: := . ( # % ; The symbol ":=" was substituted for "" to continue.
Your problem is not in the :new pseudorecord. The error is coming from the usage of print, which is used to print the bind variable used in successful PL/SQL block or used in an EXECUTE command. For example, you can use it this way:
VARIABLE n NUMBER
BEGIN
:n := 1;
END;
/
Then
PRINT n;
But if you want to test the value being inserted, you can use DBMS_OUTPUT.PUT_LINE like this:
create or replace trigger tr1
after
insert ON
employee
for each row
BEGIN
dbms_output.put_line(:new.emp_id);
END;
/
Enable DBMS_OUTPUT window in your SQL Developer, then run
insert into employee values(1, 'empName', 1000, 'ABC');
You'll see 1 printed out.
However, you can always test the value from the table. Because the value should be already inserted into table. You can just query.

PlSQL trigger error ORA-0000 ORA-06512:

create or replace
TRIGGER "SUP" AFTER INSERT ON "EMP_REPORT" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW
DECLARE
miclaim_supervisor_count number;
employee_company_code VARCHAR2(10);
employee_businessunit number;
projMgrs NUMBER;
supId NUMBER;
cursor projMgrsCursor is select b.BU_MEMBER_ID
from BU_MEMBER b, EMP_SUB_DIV s
where s.EMP_NO = :NEW.EMP_NO
and s.SUB_DIVISION_CODE = '01' and s.DIV_CODE = '2'
and b.BU_ID IN (select BU_ID from BU_MEMBER where BU_MEMBER_ID = :NEW.EMP_NO);
BEGIN
delete from MICL_SUPERVISORS where EMP_NO = :NEW.EMP_NO and IS_OVVERRIDDEN = 0;
select count(*) into miclaim_supervisor_count from MICL_SUPERVISORS where EMP_NO = :NEW.EMP_NO and IS_OVVERRIDDEN = 1;
select COMPANY_CODE into employee_company_code from EMPLOYEE_MASTER where EMP_NO = :NEW.EMP_NO;
projMgrs := 0;
if (employee_company_code ='F')then
OPEN projMgrsCursor;
LOOP
select micl_sup_id_seq.nextval into supId from dual;
FETCH projMgrsCursor INTO projMgrs;
EXIT WHEN projMgrsCursor%NOTFOUND;
insert into SUP VALUES ((supId), (SELECT SYSDATE FROM DUAL), :NEW.ENTRYADDEDBY_EMP_NO, 3000, 0,projMgrs, NULL,:NEW.EMP_NO);
END LOOP;
CLOSE projMgrsCursor;
else
if(miclaim_supervisor_count IS NULL or miclaim_supervisor_count<1) then
insert into SUP VALUES ((:NEW.ID), (SELECT SYSDATE FROM DUAL), :NEW.ENTRYADDEDBY_EMP_NO, 3000, 0, :NEW.SUP_EMP_NO, NULL,:NEW.EMP_NO);
end if;
end if;
END;
I created this trigger a week go but no compilation errors
But when I enter a record to EMP_REPORT it pop up an error msg saying
*INSERT INTO"EMP_REPORT" (ID, ADDEDDATE, ENTRYADDEDBY_EMP_NO, SUP_EMP_NO, EMP_NO) VALUES ('71', TO_TIMESTAMP('19-MAR-13 09.55.57.983000000 AM', 'DD-MON-RR HH.MI.SS.FF AM'), '81', '841', '5295')
ORA-00001: unique constraint (SYS_C0023329) violated
ORA-06512: at line 1
One error saving changes to table "EMP_REPORT":
Row 51: ORA-00001: unique constraint (SYS_C0023329) violated
ORA-06512: at line 1*
I cant figure out where I went wrong. pls help me :(
pls note that I cant remove constraint and its a primary key
Are you sure that the error occurs in the trigger? It looks to me like the INSERT which might be failing is the one into EMP_REPORT - possibly because there's already a row in EMP_REPORT with ID = '71'. It would be helpful if you could confirm what table SYS_C0023329 is on.
However, assuming that the message is being generated by the trigger - I only see two INSERTs in your trigger, both inserting into the SUP table, so the problem must be coming from one of these INSERTs. Either
There's already a row in SUP which has a key value (you don't show the names of the columns in your INSERT statements against SUP so I don't know what the field's name might be) greater than the current value of MICL_SUP_ID_SEQ.NEXTVAL, or
There's a row in SUP with a key value equal to the new EMP_REPORT.ID field you've supplied in the second INSERT.
This second issue, supplying a key value from one table (EMP_REPORT) as the key value for a second table (SUP) seems a bit suspicious to me, and I'd suggest looking there first. I'd think you'd want to get a value from MICL_SUP_ID_SEQ to use in the SUP table.
To verify that the issue is caused by an insert into SUP, you might try executing the following:
SELECT *
FROM ALL_CONSTRAINTS c
WHERE c.CONSTRAINT_NAME = 'SYS_C0023329';
This will tell you what table the constraint is on.
Share and enjoy.

How i can pass column names from variables in plsql update statement

DECLARE
v_name A.TRANSACTION_TYPE%TYPE :='SALARY';
v_salary A.SALARY%TYPE := 1000;
BEGIN
update A set v_name= v_salary where EMPID = 517;
-- PL/SQL: ORA-00904: "v_name": invalid identifier
--update A set SALARY = 1000 where EMPID = 517;
END;
/
My idea is to update table columns , but these column names are stored in variable. Is there any way to pass column names from variable ? Is there any options apart from Execute Immediate
Not sure if this will work in your situation, but I've written solutions where I wrote a script in SQLPlus and it "wrote" (using dbms_output.put_line or even just prompt) another script that did queries, and the columns/tables in those queries was determined by the logic in the SQLPlus script. Then I would execute as a script the output from my first script, and it would execute dynamically generated queries without ever needing execute immediate.
The following idea may work for multiple columns that are typed the same... As written, it will update all columns every time for a given record, but only the column specified by v_name will be changed to the value set in v_value; the other columns are simply updated to their existing value. The idea can be played with using DECODE, NVL or other similar conditional operators.
declare
v_name varchar2(20):= 'SAL';
v_value emptest.sal%TYPE := 5000;
begin
update emptest
set sal = ( select case when v_name = 'SAL' then v_value else sal end from dual),
comm = ( select case when v_name = 'COMM' then v_value else comm end from dual)
where empno = 7369;
commit;
end;

Resources