Load into oracle table from cobol flat file with occurs times - oracle

Incoming file format mainframe/cobol record layout, one single record that is more than 21000 characters long. Please be aware of the occurs 350 times , which is making the record length very long, a horizontal layout, instead of a row-like layout in incoming file.
id pic x(23).
idnum pic 9(04).
filler pic x(10).
grp occurs 350 times
grpkey1 PIC X(25).
grpkeynum PIC X(09).
grpsubkey PIC X(01).
grptyp PIC X(01).
grpst PIC X(08).
grpend PIC X(08).
filler PIC X(10).
Target Table Definition (Preferably Oracle External Table)
create table grpkeys (
id CHAR(23),
idnum CHAR(04),
filler10 CHAR(10),
grpkey1 CHAR(25),
grpkeynum CHAR(09),
grpsubkey CHAR(01),
grptyp CHAR(01),
grpst CHAR(08),
grpend CHAR(08),
filler20 CHAR(10)
)
I have to load above record format in a file into a table (preferably a working oracle external table, if possible). id, idnum, filler10 values need to be copied into all 350 records created in oracle table (preferably external table) for a single record of incoming file. Please suggest the easiest way to accomplish this.

I'll stick to 5 columns for this example, but there should be no syntactic nor performance restriction to scale from 005 up to 350. Example assumes your file is on Oracle database server in file /tmp/test/test.txt.
My recommendation is to use an Oracle external table definition that effectively reads the data file "as is" (in all its 350-column glory), but does not worry about parsing out the 350-repeat field into components like grpkey1, grpsubkey, etc.
CREATE DIRECTORY TEST_DIR AS '/tmp/test';
CREATE TABLE TEST_XT
(
id VARCHAR2(23),
idnum INTEGER,
filler10 VARCHAR2(10),
GRP_001 varchar2(62),
GRP_002 varchar2(62),
GRP_003 varchar2(62),
GRP_004 varchar2(62),
GRP_005 varchar2(62)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY TEST_DIR
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE
FIELDS
(
ID CHAR(23),
IDNUM INTEGER EXTERNAL (4),
FILLER10 CHAR(10),
GRP_001 CHAR(62),
GRP_002 CHAR(62),
GRP_003 CHAR(62),
GRP_004 CHAR(62),
GRP_005 CHAR(62)
)
)
LOCATION ('test.txt')
);
Then wrap that external table definition with a view definition that performs an UNPIVOT operation (plus some basic SUBSTR functions to slice each of the 350 unpivoted GRP fields into its constituent pieces).
CREATE OR REPLACE VIEW TEST_V AS
SELECT ID, IDNUM, FILLER10, GRP_NUM,
SUBSTR(GRP_STR,01,25) GRPKEY1,
SUBSTR(GRP_STR,26,09) GRPKEYNUM,
SUBSTR(GRP_STR,35,01) GRPSUBKEY,
SUBSTR(GRP_STR,36,01) GRPTYP,
SUBSTR(GRP_STR,37,08) GRPST,
SUBSTR(GRP_STR,45,08) GRPEND,
SUBSTR(GRP_STR,53,10) FILLER20
FROM
(
SELECT *
FROM TEST_XT
UNPIVOT
(GRP_STR FOR GRP_NUM IN
(
GRP_001 as 1,
GRP_002 as 2,
GRP_003 as 3,
GRP_004 as 4,
GRP_005 as 5
)
)
);
Of course, you can query view directly, or load to standard table (insert into standard_table select * from test_v) for indexing, partitioning, etc needs.
Also, you can scale to desired level of performance by adding parallelism to external table:
ALTER TABLE TEST_XT PARALLEL 8;

Related

Oracle External Table - No Rows Selected

I've created an external table using the following definition
CREATE TABLE EXT_TABLE (CID NUMBER, CNAME VARCHAR2(20), FEES NUMBER)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY TEST_DIR
ACCESS PARAMETERS
(RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
(
CID INTEGER,
CNAME CHAR(20),
FEES INTEGER
)
)
LOCATION ('DATA.TXT'))
REJECT LIMIT UNLIMITED;
table has been created. But, when i try to select data from table, I don't find any records
SQL> select * from ext_table;
no rows selected
I've made sure directory and table have sufficient privileges for the user.
The data in text file;
1,JAVA,300
2,LINUX,400
3,ORACLE,400
4,EXCEL,500
RECORD is not the keyword you should use here.
it must be RECORDS.
Use this:
RECORDS DELIMITED BY NEWLINE
Cheers!!

Oracle:inner join between file and table

I'm new to oracle and plsql, so just bear with me.
I have a file TYPES.txt,
id,name,values
1,aaa,32
2,bbb,23
3,cvv,12
4,fff,54
I also have a table in my db, PARTS.ATTRIBUTES
id,name,props,crops
1,aaa,100,zzzz
2,bbb,200,yyyy
3,cvv,300,xxxx
4,fff,400,wwww
5,sasa,343,gfgg
6,uyuy,897,hhdf
I'd like to do an INNER JOIN on the file TYPES and ATTRIBUTES based on the column name. Now, i have done this by initially loading file TYPES into a temp table and then doing INNER JOIN between the temp table and ATTRIBUTES table.
But i'd like to know whether it is possible to do INNER JOIN between TYPES file and ATTRIBUTES table without making use of a temp table.
I understand that i can load the file using and get respective rows using following script:
declare
file utl_file.file_type;
line varchar2(500);
begin
file :=utl_file.fopen('USER_DIR','TYPES.txt','r');
loop
utl_file.get_line(file ,line);
dbms_output.put_line(line);
end loop;
exception
when others then
utl_file.fclose(file);
end;
Could someone be kind enough to explain to me how i can do the join between file contents and the db table?
P.S. The file TYPES.txt is dynamically generated and can have different number of columns at different times.
One cleaner approach is to use an EXTERNAL TABLE.
Use a create statement like this to create TYPES_external table.
CREATE TABLE TYPES_external (
id NUMBER(5),
name VARCHAR2(50),
Values VARCHAR2(50)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY USER_DIR
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
(
id NUMBER(5),
name VARCHAR2(50),
Values VARCHAR2(50)
)
)
LOCATION ('TYPES.txt','TYPES.txt')
)
PARALLEL 5
REJECT LIMIT UNLIMITED;
Once created , you can use this external table(TYPES_external) just as you
use any database table for select operation.

Modify a nested table in Oracle

I have the below Nested table created :
create or replace TYPE access_t AS OBJECT (
AccessID VARCHAR2(50),
Eligibility char(1)
);
/
create or replace TYPE Access_tab IS TABLE OF access_t;
/
create or replace TYPE add_t AS OBJECT (
city VARCHAR2(100),
state VARCHAR2(100),
zip VARCHAR2(10),
APOINTSARRAY Access_tab )
;
/
create or replace TYPE add_tab IS TABLE OF add_t;
/
CREATE TABLE RQST_STATUS
( RQST_ID NUMBER,
ADDRESS add_tab
)
NESTED TABLE ADDRESS STORE AS RQST_STATUS_ADDRESS
( NESTED TABLE APOINTSARRAY STORE AS RQST_STATUS_AP)
;
If i need to change ADDRESS type to new_add_tab with some additional columns instead of add_tab , Can i just use ALTER TABLE .. MODIFY .. command ?
I am getting ORA-00922 or ORA-22913 errors . I cannot change the type directly because it is used somewhere else too. Also, the table is already loaded with data.
Please suggest.
You can do that but you have to alter the TYPE not the TABLE.
Check the documentation ALTER TYPE Statement: alter_method_spec
Most important is the CASCADE key word.
Examples:
ALTER TYPE access_t ADD ATTRIBUTE NEW_Eligibility INTEGER CASCADE;
ALTER TYPE access_t DROP ATTRIBUTE Eligibility CASCADE;
ALTER TYPE access_t MODIFY ATTRIBUTE AccessID VARCHAR2(100) CASCADE;
Here is a step-by-step description of my suggestion. It might not be the most elegant, but I think that it would be best for you to have something you can fully understand (as opposed to an obscure trick).
Also, and since I don't really know what kind of changes you need to do for the internal table, I'm leaving the maximal flexibility for you to do any change you may wish to do.
Let's call your table T1 that contains a columns C_T which is your internal table.
The internal table contains columns C_1, C-2 and C_3, and you want the new structure for the record to be D_1, D_2, D_3, D_4 and D_5, where the mapping is:
C_1 -> D_5,
C_2 -> D_1,
C_3 -> D_2,
{new} -> D_3,
{new} -> D_4.
Create a tempo table TEMPO_T with a column SOURCE_ROWID (varchar2(64)) and the new columns D_1,..., D5.
Write a small anonymous block having a cursor that selects the ROWID of each row of table T1 and all the records within the internal table in column C_T (order by ROWID). The result would look like (this is just an example of course):
ROWID C_1 C_2 C_2
wwereeedffff 1 a ww
wwereeedffff 2 b xx
wwereeedffff 7 l yy
ertrtrrrtrrr 5 d PP
ertrtrrrtrrr 99 h mm
...
[Note: The use of ROWID is under the assumption that you don't have a column that can serve as a unique identifier for each row in table T1; if there is such column - one defined as UNIQUE INDEX - you can use that field instead]
Having this query ready, convert it into an INSERT into the temporary table TEMPO_T along with whatever values you need to store for columns D_3 and D_4.
Now, you have a backup of the original contents of column C_T and hence can delete the column.
Now, you can update the type that defines the structure of column C_T to its new form (i.e. D_1,...,D_5) and alter table T1 by adding a column whose type is the updated one.
Finally, you can insert the contents of column C_T with that stored in the temporary table (since you already have this, I assume that you know how to implement it - inserting a table within a cell column of the outer table).
That's it.
Needless to say, I would make a backup of your data before engaging into this.
Hope this description is detailed enough to enable you to complete the task at hand.

filter tablix without datasource values in ssrs

I have SSRS report and I need to filter a static table that I created inside the report based on parameter. There is no data source to this table and I'm entering the data manually.
The tablix contain 3 columns.
How can I filter the columns based on parameter?
I tried in the expression =#param1 for example but it doesn't work.
For now I only manage to filter if the expression is on data source fields.
Do you literally have a table with a number of values in it written directly into the report? If so I don't think you will be able to perform any filtering on it as effectively all you've done it write data into textboxes that are displayed.
I would imagine your best option would be to instead create a new dataset and populate this with your static data, e.g.
SELECT 'A' AS Letter, 'English' AS Language
UNION
SELECT 'B' AS Letter, 'French' AS Language
UNION
SELECT 'A' AS Letter, 'German' AS Language
To give you a table as follows
Letter | Language
-------+----------
A | English
B | French
A | German
That you could then filter on Letter = A
So essentially you have a Tablix that has 3 columns pre-populated with information you have manually entered into the text boxes themselves? Since you've already entered that data, I don't believe there is a way to filter that at run time. That data is hard coded in essence. The Filter ability in SSRS is used as a WHERE clause so it restricts what is brought forth into the report from the query.
I would create a data source connection to a dummy database, create a DataSet, and create a query that fills a temporary table will all the information that you've manually entered. Once you create the temporary table and inserted values into it, you can then perform a SELECT with a parameter. Your Tablix will only be populated with information that matches the parameter. Something to the effect of this:
CREATE TABLE #TempTable (
ID INT
,Name VARCHAR(MAX)
,Email VARCHAR(MAX)
)
INSERT INTO #TempTable (
ID
,Name
,Email
)
VALUES (
1
,'Bob'
,'bob#email.com'
)
,(
2
,'Frank'
,'frank#email.com'
)
,(
3
,'Jim'
,'jim#email.com'
)
SELECT
*
FROM
#TempTable
WHERE
ID = #ID
DROP TABLE #TempTable

Can variables be passed to a SQL*Loader control file?

Suppose you have a table:
CREATE TABLE Customer
(
batch_id NUMBER,
customer_name VARCHAR2(20),
customer_address VARCHAR2(100)
)
And suppose you have a control file to populate this table:
LOAD DATA INFILE 'customers.dat'
REPLACE
INTO TABLE Customer
(
batch_id ??????,
customer_name POSITION(001:020),
customer_address POSITION(021:120)
)
Is it possible to pass a value for batch_id to my control file when I run SQL*Loader? For example, is it possible to specify a bind variable (turning the question marks into :MY_AWESOME_BATCH_ID)?
A relatively easy way to archive that is to create a stored function that returns the batch number and use it in the loader file.
create or replace function getBatchNumber return number as
begin
return 815;
end;
/
LOAD DATA INFILE 'customers.dat'
REPLACE
INTO TABLE Customer
(
batch_id "getBatchNumber",
customer_name POSITION(001:020),
customer_address POSITION(021:120)
)
Not easily, if I remember right, but here are a couple of alternatives:
If there's only going to be one process running SQLLoader at a time, use nulls or a fixed value and then run a SQLPlus script as part of the process afterwards to do the update to a sequence value.
Call a script which will grab the next sequence value for your batch ID and then spool out the control file, including the batch_id constant.
If it's acceptable to have BATCH_ID values generated automatically by incrementing on each load, than this worked for me. The 10 minutes interval in the sample would need to be adjusted to the specific load - to be accurate, the loading must complete within the specified interval and the next loading must not be started in less than time specified.
A drawback is that it slows down noticeably on large volumes - that's the price running the MAX aggregate on every line.
LOAD DATA
...
INTO TABLE XYZ
(
...
BATCH_ID expression "(select nvl(max(batch_id) + 1, 1) from extra_instruments_party_to where create_date < (sysdate - interval '10' minute))",
CREATE_DATE SYSDATE
)

Resources