PLS-00103 creating an external table with dynamic SQL - oracle

I'm trying to dynamically create an external table but I'm getting error message PLS-00103: Encountered the symbol "EXTERNAL". I'm also using ora_hash in the external table definition; please let me know it is the right way to get the ora_hash value.
create or replace procedure CHECKTABLEEXIST1 (p_tab_name in varchar2,DATAFILE in varchar2) --user_tables.table_name%type)
is
tab_name varchar2(100) := p_tab_name;
n Number(3);
ext_table varchar(100) := tab_name|| ' as select * from xyz WHERE 1=0';
begin
select count(*) into n from tab where TName=upper(tab_name);
--dbms_output.put_line(n);
if n=0 then
execute immediate 'create table ' || ext_table ;
else
execute immediate 'drop table ' || tab_name;
execute immediate 'create table ' || ext_table;
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DE_DUBFILE
ACCESS PARAMETER
(
RECORDS DELIMITED BY NEWLINE
CHARACTERSET US7ASCII
BADFILE 'UPLOAD':'p_tab_name.bad'
DISCARDFILE 'UPLOAD':'p_tab_name.dis'
LOGFILE 'UPLOAD':'p_tab_name.log'
FILEDS TERMINATED BY ','
optionally enclosed by '"'
TRAILING NULLCOLS
MISSING FIELD VALUES ARE NULL
(
t1 ,t2,t3,t4,t5 date "YYYYMMDD" ,t6,t7,
t8 ,t9 ,
DETL_CLMNS_HASH "ORA_HASH( :t4||:t7 )",
KEY_CLMNS_HASH "ORA_HASH(:t1||:t2||:t5)", t10,t11)
)
LOCATION (DATAFILE)
);
end if;
end;
I'm getting error message :
LINE/COL ERROR
-------- -----------------------------------------------------------------
16/14 PLS-00103: Encountered the symbol "EXTERNAL" when expecting one o
f the following:
:= . ( # % ;

Everything from ORGANIZATION onwards is being seen as PL/SQL code, not part of your dynamic SQL statement. You're appending the table name to the create table but then not appending the rest as part of that statement string. You need to do something like:
execute immediate 'create table ' || p_tab_name || '
( /* put column names and types here */ )
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DE_DUBFILE
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE
CHARACTERSET US7ASCII
BADFILE UPLOAD:''' || p_tab_name || '.bad''
DISCARDFILE UPLOAD:''' || p_tab_name || '.dis''
LOGFILE UPLOAD:''' || p_tab_name || '.log''
FIELDS TERMINATED BY '',''
optionally enclosed by ''"''
MISSING FIELD VALUES ARE NULL
(
t1 ,t2,t3,t4,t5 date mask "YYYYMMDD" ,t6,t7,
t8 ,t9, t10,t11
)
LOCATION (''' || DATAFILE || ''')
)';
In the first line the terminating semicolon has been replaced with concatenation of a new string literal. The references to variables p_tab_name and DATAFILE have to be broken out from that literal too, requiring more single quotes and concatenation; and the single quotes that are actually part of the statement need to be escaped by doubling them up. There were various other quotes missing as well. What's shown should now run.
I've also change the table name being used to just p_tab_name, but you need to specify the column names and data types explicitly. It doesn't make sense to use as select * ... for an external table. That isn't legal syntax, either before organization or after the rest if the current statement. I suppose you could extract that information from all_tab_columns and build that part dynamically too, but if you're basing it on a fixed table you ought to know those anyway.
Your logic for dropping/creating is off too - I think you just want:
if n>0 then
execute immediate 'drop table ' || p_tab_name;
end if;
execute immediate 'create table ' || p_tab_name || '
...
... so you don't have to repeat the create statement in both branches.
I've also corrected a couple of other mistakes; PARAMETERS rather then PARAMETER; FIELDS rather then FILEDS; removed TRAILING NULLCOLS. Try to execite the command as static SQL before converting it to dynamic. There may still be other issues.
And I've removed the last two calculated columns:
DETL_CLMNS_HASH "ORA_HASH( :t4||:t7 )",
KEY_CLMNS_HASH "ORA_HASH(:t1||:t2||:t5)")
The ORACLE_LOADER driver doesn't allow manipulations like that; SQL*Loader does but they are not exactly the same. You also can't define virtual columns on an external table. If you're using this as a staging table to load data into another (real) table then you can calculate those hashes during the transfer; otherwise you can create a view over this external table which includes the calculated columns.

Related

concatenate VArray to String and use in dynamic SQL - Oracle

I need to dynamically create a query and execute using Execute Immediate, I am facing the problem in appending the Vaaray variable. Getting Error
pls-00306 wrong number or types of arguments in call to ||
Vaaray //Its a type number
select ver_id bulk collect into Ver_Array from ( Select id from table)
No issue with below query as only id variable is used:
Execute Immediate 'Select ID, name , Date, time
from table
where id = ' || v_UC2_id
Error with below query
Execute Immediate 'Select ID, name , Date, time
from table
where id = ' || v_UC2_id
|| ' and ver_id in ( SELECT * FROM TABLE ( '
|| Ver_Array
|| ' )'
Tried to extract the query and concatenate in comma saperated values but the final result comes as String but field used in query is Number
Not sure how to handle this in dynamic query
The SQL you're writing is concatenating an array with a string, so you get an error.
You can do it like this:
create or replace type vat is varray(10) of number:
/
declare
ivat vat:=vat(1,2,3);
res number;
begin
execute immediate 'select sum(rn) from tarr where rn in (select column_value from table (:varrt))' into res using ivat;
dbms_output.put_line(res);
end;
/
Here I'm selecting just one row and value. If you have multiple rows and columns, then you better declare a cursor for this SQL, and loop thru it.

SQLLDR default character length & ways to fix it

I faced with a situation where one the columns data to be uploaded exceeded 255 and it threw Error Field in data file exceeds maximum length error.
I found a way to fix it. Please find link here explaining the logic.
The question is that in a control file, is there any difference between
Comments CHAR(255) "TRIM(:Comments)" ,
and
Comments CHAR "TRIM(:Comments)" ,
when it comes to the internal workings of sqlldr or it means the same right ?
Also while uploading, because the integrity of the data file cannot be trusted, we create a table with all columns as 255 [which i will fix for columns > 255 length] and I never specify the CHAR length in the control file.
I would like to know if the difference between
using default 255 for all columns
vs
keeping little extra than expected length [actual target table column length values] eg. actual expected length [which might range from 10 to 150 etc.] + 50/100
is very significant if i use sqlldr atleast 2000 times a day on small data files with average of 250 records.
Thanks in advance for the clarification.
I think they are the same as internally that is what the buffer size is as well.
After dealing with this enough times I created a real rough utility script that generates a skeleton control file that uses the column's actual size. It gets me 90% of the way there and no column sizing issues. It may be better on memory as its not going to use the full 255 chars available if the column is smaller. Give it a try if you want. It may give you some ideas anyway.
/********************************************************************************************************
Name: GEN_CTL_FILE
Desc: Generates a skeleton control file for loading data via SQL*Loader.
Args: tablename_in IN VARCHAR2, delim_in IN VARCHAR2 DEFAULT '|'
Returns: None.
Usage: utilities.gen_ctl_file('tablename');
Notes: Prints a skeleton control file.
If a template for a fixed-length data file is desired, use 'FIXED' for the delim_in string.
FIXED needs more work to put actual lengths in. For now just placeholders.
Example usage:
set serveroutput on;
execute utilities.gen_ctl_file('test', utilities.TAB);
REVISIONS:
Ver Date Author Description
--------- ---------- --------------- ------------------------------------
1.1 6/6/2013 gary_w - Created procedure.
1.2 10/8/2013 gary_w - Fixed decode statement.
- Added option to generate a fixed-length template.
************************************************************************************************************************/
PROCEDURE GEN_CTL_FILE(tablename_in IN VARCHAR2, delim_in VARCHAR2 DEFAULT '|') IS
ERRNULLTABLENAME CONSTANT NUMBER := -20103; -- User-defined error numbers and messages.
ERRNULLTABLENAMEMSG CONSTANT VARCHAR2(100) := 'A table name is required.';
USAGE CONSTANT VARCHAR2(100) := '* USAGE: UTILITIES.GEN_CTL_FILE(tablename_in IN VARCHAR2, fieldsep_in VARCHAR2 DEFAULT ''|'')';
v_delim VARCHAR2(20) := NVL(delim_in, '|');
CURSOR COL_CUR IS
SELECT COLUMN_NAME,
DECODE(COLUMN_ID, 1, ' ', ',') || RPAD(COLUMN_NAME, 32) || case upper(v_delim)
when 'FIXED' then 'POSITION(99:99) '
else NULL
end|| DECODE(DATA_TYPE,
'VARCHAR2', 'CHAR('||DATA_LENGTH||') NULLIF(' || COLUMN_NAME || '=BLANKS)',
'FLOAT', 'DECIMAL EXTERNAL NULLIF(' || COLUMN_NAME || '=BLANKS)',
'NUMBER', DECODE( DATA_PRECISION,
0, 'INTEGER EXTERNAL NULLIF (' || COLUMN_NAME || '=BLANKS)',
DECODE(DATA_SCALE, 0, 'INTEGER EXTERNAL NULLIF (' || COLUMN_NAME || '=BLANKS)', 'DECIMAL EXTERNAL NULLIF (' || COLUMN_NAME || '=BLANKS)')),
'DATE', 'DATE "MM/DD/YYYY" NULLIF (' || COLUMN_NAME || '=BLANKS)',
data_type)
AS COL_DATA
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = UPPER(tablename_in)
ORDER BY COLUMN_ID;
BEGIN
IF tablename_in IS NULL THEN
RAISE_APPLICATION_ERROR(ERRNULLTABLENAME, ERRNULLTABLENAMEMSG || CR || USAGE);
END IF;
DBMS_OUTPUT.PUT_LINE('--');
DBMS_OUTPUT.PUT_LINE('-- NOTE - When using DIRECT=TRUE to perform block inserts to a table,');
DBMS_OUTPUT.PUT_LINE('-- the table''s triggers will not be used! Plan accordingly to');
DBMS_OUTPUT.PUT_LINE('-- manually perform the trigger actions after loading, if needed.');
DBMS_OUTPUT.PUT_LINE('--');
DBMS_OUTPUT.PUT_LINE('OPTIONS (DIRECT=TRUE)');
DBMS_OUTPUT.PUT_LINE('UNRECOVERABLE');
DBMS_OUTPUT.PUT_LINE('LOAD DATA');
DBMS_OUTPUT.PUT_LINE('APPEND');
DBMS_OUTPUT.PUT_LINE('INTO TABLE ' || UPPER(tablename_in));
DBMS_OUTPUT.PUT_LINE('EVALUATE CHECK_CONSTRAINTS');
if upper(v_delim) != 'FIXED' then
DBMS_OUTPUT.PUT_LINE('FIELDS TERMINATED BY ' || '''' || v_delim || '''');
DBMS_OUTPUT.PUT_LINE('OPTIONALLY ENCLOSED BY ''"'' ');
DBMS_OUTPUT.PUT_LINE('TRAILING NULLCOLS');
end if;
DBMS_OUTPUT.PUT_LINE('(');
-- The cursor for loop construct implicitly opens and closes the cursor.
FOR COL IN COL_CUR
LOOP
IF COL.COLUMN_NAME != 'LOAD_DATE' THEN
IF COL.COLUMN_NAME = 'LOAD_SEQ_ID' THEN
dbms_output.put_line(','||RPAD('LOAD_SEQ_ID', 32)||'CONSTANT 0');
ELSE
DBMS_OUTPUT.PUT_LINE(COL.COL_DATA);
END IF;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(')' || CHR(10));
EXCEPTION
WHEN OTHERS THEN
RASIE;
END; -- GEN_CTL_FILE
The output looks like this:
--
-- NOTE - When using DIRECT=TRUE to perform block inserts to a table,
-- the table's triggers will not be used! Plan accordingly to
-- manually perform the trigger actions after loading, if needed.
--
OPTIONS (DIRECT=TRUE)
UNRECOVERABLE
LOAD DATA
APPEND
INTO TABLE TEST
EVALUATE CHECK_CONSTRAINTS
FIELDS TERMINATED BY '|'
OPTIONALLY ENCLOSED BY '"'
TRAILING NULLCOLS
(
COLA CHAR(200) NULLIF(COLA=BLANKS)
,COLB CHAR(100) NULLIF(COLB=BLANKS)
,COLC CHAR(500) NULLIF(COLC=BLANKS)
,COLD DECIMAL EXTERNAL NULLIF (COLD=BLANKS)
,COLE CLOB
)
If you tweak it, please share your changes.

Creating external tables in Oracle procedure

I'm using Oracle 11g and I'm having in issue with creating an external table in a procedure. It gets created with no errors but when I execute the procedure I'm having errors.
The first parameter is the name of the file and the second is a comma because I was having issues with using single quotations to surround the comma where I specify the fields terminated by section. DATA_DIR was declared.
Here's what I tried.
CREATE OR REPLACE PROCEDURE LOADTABLE
(
FILENAME VARCHAR2,
COMMA VARCHAR
)
AS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE LOAD
(
USERNAME VARCHAR2(30)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DATA_DIR
ACCESS PARAMETERS
( FIELDS TERMINATED BY :COMMA)
LOCATION (:FILENAME)
)' USING IN COMMA, FILENAME;
END;
This is how I call the procedure
EXEC LOADTABLE('username.csv',',');
This is the error I get
ERROR at line 1:
ORA-00931: missing identifier
ORA-06512: at "DATA_ADMIN.LOADTABLE", line 9
ORA-06512: at line 1
Any help will be appreciated.
You can only bind variables, and the external table creation syntax requires text literals for the elements you're trying to bind.
You'll have to use concatenation instead:
CREATE OR REPLACE PROCEDURE LOADTABLE
(
FILENAME VARCHAR2,
COMMA VARCHAR
)
AS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE LOAD
(
USERNAME VARCHAR2(30)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DATA_DIR
ACCESS PARAMETERS
(FIELDS TERMINATED BY ''' || COMMA || ''')
LOCATION (''' || FILENAME || ''')
)';
END;
/
Procedure LOADTABLE compiled
EXEC LOADTABLE('username.csv',',');
PL/SQL procedure successfully completed.
In that I've escaped the single quotes around the concatenated string values. In the question you mentioned that you're only passing the comma because you were "having issues with using single quotations to surround the comma"; escaping them by doubling them up is the way to do that, so if you do always want a comma separator you can instead do:
CREATE OR REPLACE PROCEDURE LOADTABLE
(
FILENAME VARCHAR2
)
AS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE LOAD
(
USERNAME VARCHAR2(30)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DATA_DIR
ACCESS PARAMETERS
(FIELDS TERMINATED BY '','')
LOCATION (''' || FILENAME || ''')
)';
END;
/
EXEC LOADTABLE('username.csv');
However, creating (and presumably dropping) objects on the fly isn't generally a good idea. It would be better to create the external table once, which would be done with static DDL:
CREATE TABLE LOAD
(
USERNAME VARCHAR2(30)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DATA_DIR
ACCESS PARAMETERS
(FIELDS TERMINATED BY ',')
LOCATION ('dummy')
);
and then just alter the table to have a new file name statically too:
alter table load location ('username.csv');
or if you really want a procedure to do it:
CREATE OR REPLACE PROCEDURE LOADTABLE
(
FILENAME VARCHAR2
)
AS
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE LOAD LOCATION (''' || FILENAME || ''')';
END;
/
EXEC LOADTABLE('username.csv');

01735. 00000 - "invalid ALTER TABLE option"

I am getting the following error:
00000 - "missing right parenthesis"
when I execute my procedure:
CREATE OR REPLACE PROCEDURE ALTER_TABLE_COLUMN_NOT_NULL(
var_tabname IN VARCHAR2,
var_clname IN VARCHAR2,
var_defvalue IN VARCHAR2 )
IS
l_isnull VARCHAR2(1);
BEGIN
SELECT isnull INTO l_isnull FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = var_tabname AND COLUMN_NAME = var_clname;
IF l_isnull = 'Y' THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || var_tabname ||
' MODIFY COLUMN (' || var_clname ||
' DEFAULT ' || var_defvalue || ' NOT NULL)';
END IF;
END;
I know that according to the error, the right parenthesis is missing. I tried many ways of rewriting it, but I can't manage to fix it.
I am executing my procedure the following way:
BEGIN
ALTER_TABLE_COLUMN_NOT_NULL('FIRSTNAME', 'PRICE', '-');
END;
Writing dynamic SQL is hard, because compilation errors become runtime errors.
In this case I think the problem is that MODIFY COLUMN is wrong syntax. It's just MODIFY.
You may also run into some problems with your default of '-'. If price is a number that will fail because - is an invalid number. If price is a string you'll need to escape the passed value with additional quotes.
But probably you want to make this generic, so you need to write some more sophisticated handling which tests for datatype of the target column and formats default value appropriately.
"Can u give me a hint or any link how one can determine the datatype of a passed value in plsql?"
It's not the passed value which matters, it's the datatype of the modified column. You can get that from the USER_TAB_COLUMNS view which you're already querying.
Print your query to make sure it written correctly
DBMS_OUTPUT.PUT_LINE('ALTER TABLE ' || var_tabname || ' MODIFY COLUMN (' || var_clname || ' DEFAULT ' || var_defvalue || ' NOT NULL)');

Replace character in all columns using Oracle Data Modeler

Is it possible to simply change character in name of tables columns? At the moment columns have seperator - and I want to change it with underscore (_) . Is it possible to do it easy with 1 click or should I go through all tables and change it by hand?
I don't know oracle SQL data modeler. If you have access to SQL*Plus or another developer tool, you can run script, it will change all columns of tables in your schema:
begin
for i in (select table_name, column_name from user_tab_columns) loop
if instr(i.column_name, 'A') > 0 then
execute immediate 'alter table ' || i.table_name || ' rename column ' ||
i.column_name || ' to ' || replace(i.column, 'A', 'B');
end if;
end loop;
end;
/
Here 'A' is symbol that you want to replace and 'B' - new symbol to use. But be careful, such scripts can be dangerous. Try it on test schema first.

Resources