ORA-30657 when creating an interval (or auto list) external partition table - oracle

When trying to create an Interval (or auto list) external partitioned table with DBMS_CLOUD, I am getting ORA-30657: operation not supported on external organized table.
What am I missing?
SQL> BEGIN
2 DBMS_CLOUD.CREATE_EXTERNAL_PART_TABLE(
3 table_name =>'PETX',
4 credential_name =>'MY_CRED',
5 format => json_object('delimiter' value '#'),
6 column_list => 'deptno number,dname char(14),loc char(13)',
7 partitioning_clause => 'partition by range (deptno) interval (15)
8 (
9 partition xp1 values less than (15) location(''https://swiftobjectstorage.XXXX/xp1_15.txt'') ,
10 partition xp2 values less than (30) location (''https://swiftobjectstorage.XXXX/xp2_30.txt'')
11 )'
12 );
13 END;
14 /
Error starting at line : 5 in command -
BEGIN
DBMS_CLOUD.CREATE_EXTERNAL_PART_TABLE(
table_name =>'PETX',
credential_name =>'MY_CRED',
format => json_object('delimiter' value '#'),
column_list => 'deptno number,dname char(14),loc char(13)',
partitioning_clause => 'partition by range (deptno) interval (15)
(
partition xp1 values less than (15) location(''https://swiftobjectstorage.XXXX/xp1_15.txt'') ,
partition xp2 values less than (30) location (''https://swiftobjectstorage.XXXX/xp2_30.txt'')
)'
);
END;
Error report -
ORA-20000: ORA-30657: operation not supported on external organized table
ORA-06512: at "C##CLOUD$SERVICE.DBMS_CLOUD", line 1289
ORA-06512: at "C##CLOUD$SERVICE.DBMS_CLOUD", line 4115

Interval and auto-list partitioning is functionality that creates new partitions as-needed, based on new data inserted into the database where the partition key(s) of the new data do not map to any existing partition. With external tables the database does not control the data (or insertion of it), thus the concept of auto-generation of a partition at insertion is not applying to external tables.
Adding a new partition to a partitioned external table is equivalent to having new data files that the table has to point at. You have to tell the database about this, which you are doing by adding a new partition to a table (manually or programmatically).
Short example of how to make the above-example work and how to add a new partition (pointing to additional data files):
SQL>
SQL> BEGIN
2 DBMS_CLOUD.CREATE_EXTERNAL_PART_TABLE(
3 table_name =>'PETX',
4 credential_name =>'MY_CRED',
5 format => json_object('delimiter' value '#'),
6 column_list => 'deptno number,dname char(14),loc char(13)',
7 partitioning_clause => 'partition by range (deptno)
8 (
9 partition xp1 values less than (15) location(''https://swiftobjectstorage.XXXX/xp1_15.txt'') ,
10 partition xp2 values less than (30) location (''https://swiftobjectstorage.XXXX/xp2_30.txt'')
11 )'
12 );
13 END;
14 /
PL/SQL procedure successfully completed.
SQL>
SQL> alter table petx add partition xp3 values less than (50) location ('https://swiftobjectstorage.XXXX/xp3_45.txt');
Table PETX altered.
SQL> select count(*) from petx partition (xp3);
COUNT(*)
----------
49999999
PS: The exact same behavior applies to standard Oracle Database.

Related

Repartitioning a table on a different column

We need to change the structure of our DB by re-partitioning a parent table and adding partitions to the child tables. The partitions will be based on range of a date-time field (which is the time of insertion).
The goal is to implement the changes with as little downtime as possible. So we have though on doing it on 2 phases: do the main load while the application is running and load the delta after the application has been shutdown.
I would like to know if the following steps are correct for the plan I mentioned, focusing on the part of re-partitioning the parent table. I have based on this example from Oracle: https://docs.oracle.com/database/121/SUTIL/GUID-56D8448A-0106-4B8C-85E0-11CFED8C71B1.htm
1. create a directory for the export/import of dumps
-- user: SYS
CREATE OR REPLACE DIRECTORY dumps AS '/data/tmp';
GRANT READ, WRITE ON DIRECTORY dumps TO APP_USR;
2. export the table while the application is running
# nohup expdp APP_USR/pwd parfile=export.par &
directory=dumps
dumpfile=parent_table.dmp
parallel=8
logfile=export.log
tables=parent_table
query=parent_table:"WHERE VERSION_TSP <= sysdate"
3. create a duplicate of the table with the new partitioning
CREATE TABLE "PARENT_TABLE_REPARTITIONED"
(
-- same fields as original
CONSTRAINT PK_TABLE_ PRIMARY KEY (foo, boo) -- with different name than original
)
PARALLEL 8
PARTITION BY RANGE (VERSION_TSP)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION PARENT_TABLE_PARTITION_ -- with different name than original (should I?)
VALUES LESS THAN (TO_DATE('2007-07-01', 'YYYY-MM-DD')) COMPRESS FOR OLTP
)
ENABLE ROW MOVEMENT
;
-- with new local indexes (named differently)
CREATE INDEX IDX_VERSION_TSP_ ON PARENT_TABLE_REPARTITIONED (VERSION_TSP) LOCAL;
4. populate the re-partitioned table while the application is running
# nohup impdp APP_USR/pwd parfile=import.par &
directory=dumps
dumpfile=parent_table.dmp
parallel=8
logfile=import.log
remap_table=PARENT_TABLE:PARENT_TABLE_REPARTITIONED
table_exists_action=APPEND
5. Application shutdown (and backup the DB)
6. populate the delta
-- execute with nohup
declare
startDate DATE;
begin
select max(version_tsp) into startDate from PARENT_TABLE_REPARTITIONED;
insert into PARENT_TABLE_REPARTITIONED select /*+ parallel(8) */ s.* from PARENT_TABLE s where s.VERSION_TSP > startDate;
commit;
end;
7. switch tables
drop table parent_table cascade constraints;
rename parent_table_repartitioned to parent_table;
8. deploy the new version of the application
Is ok to export ALL from the table or should I export DATA_ONLY?
But most importantly, are those steps a valid approach?
Are you on 12.2 or higher? It could be as easy as:
SQL> create table t
2 partition by range ( created )
3 (
4 partition p1 values less than ( date '2020-01-01' ),
5 partition p2 values less than ( date '2021-01-01' ),
6 partition p3 values less than ( date '2022-01-01' ),
7 partition p4 values less than ( date '2023-01-01' )
8 )
9 as select * from dba_objects
10 where created is not null
11 and last_ddl_time is not null;
Table created.
SQL>
SQL> create index t_ix on t ( owner ) local;
Index created.
SQL>
SQL>
SQL> alter table t modify
2 partition by range ( last_ddl_time )
3 (
4 partition p1 values less than ( date '2020-01-01' ),
5 partition p2 values less than ( date '2021-01-01' ),
6 partition p3 values less than ( date '2022-01-01' ),
7 partition p4 values less than ( date '2023-01-01' )
8 ) online ;
Table altered.

FRM-40350 Query Caused No Records to be retrieved

I am new in Oracle forms. I have connected my forms to the database. Then created data block choosing data block wizard. I have choosen of my database table ORDERS. I have inserted values in this ORDERS table in sqlplus. After creating ORDERS data block I run my forms through browser, I click on execute query on menu but it's not fetching the data from my database table. It says,
FRM-40350 Query Caused No REcords to be retrieved . But in sqlplus I have checked that the row created successfully and column is fill in value and my application forms is connected to my database. Then why my execute query not working? How can I fix this?
If you're connected to the same user in both SQLPlus and Forms (you probably are, otherwise Forms wouldn't see that table and you wouldn't be able to base data block on it), you probably didn't COMMIT after inserting row(s) in SQLPlus.
Code you posted should be fixed (invalid datatype, missing column name, don't insert strings into DATE datatype column):
SQL> CREATE TABLE ORDERS(
2 order_id NUMBER(20),
3 customer_id NUMBER(20),
4 order_date DATE, -- NUMBER(20), --> date, not number
5 order_status VARCHAR2(20),
6 order_mode VARCHAR2 (200),
7 SALES_REP_ID NUMBER (20) );
Table created.
SQL> INSERT INTO orders (
2 order_id,
3 customer_id,
4 order_date,
5 order_status,
6 order_mode,
7 sales_rep_id
8 ) VALUES (
9 0001,
10 01,
11 date '2008-02-11', -- not '11-FEB-2008', it is a STRING
12 '5',
13 'direct',
14 158
15 );
1 row created.
This is what you're missing:
SQL> COMMIT;
Commit complete.
SQL>

ORA-00926: missing VALUES keyword in create table

Hi I have following script to create table with partition and sub partition with Range and list:
CREATE TABLE C##API_USER.METERENERGY
(
METERID NUMBER NOT NULL
, ENERGY_ACTIVE_EXPORT FLOAT(63)
, ENERGY_ACTIVE_IMPORT FLOAT(63)
, ENERGY_REACTIVE_EXPORT FLOAT(63)
, ENERGY_REACTIVE_IMPORT FLOAT(63)
, COL_G_DATE NUMBER(10)
)
LOGGING
TABLESPACE HEDC_TABLE_SPACE
PARTITION BY RANGE (COL_G_DATE)
SUBPARTITION BY LIST (METERID)
(
PARTITION C##API_USER.COL_G_DATE_1483228800 VALUES LESS THAN (1483228800‬) TABLESPACE HEDC_TABLE_SPACE
, PARTITION C##API_USER.COL_G_DATE_1485820800 VALUES LESS THAN (1485820800) TABLESPACE HEDC_TABLE_SPACE
, PARTITION C##API_USER.COL_G_DATE_1488412800 VALUES LESS THAN (1488412800) TABLESPACE HEDC_TABLE_SPACE
);
CREATE INDEX C##API_USER.METERENERGY_INDEX1 ON C##API_USER.METERENERGY (COL_G_DATE DESC, METERID ASC);
but oracle databse show error Report :
Error starting at line : 1 in command -
Error report -
ORA-00926: missing VALUES keyword
00926. 00000 - "missing VALUES keyword"
*Cause:
*Action:
so What I missing?(I'm using Oracle 19c with oracle sql Developer)
and if could you help me to find another answer Thanks:
(Oracle Database create automatic partition by range (long))
Subpartitions are missing.
This is just an example; you'd use smart values.
SQL> CREATE TABLE meterenergy
2 (
3 meterid NUMBER NOT NULL,
4 energy_active_export FLOAT (63),
5 energy_active_import FLOAT (63),
6 energy_reactive_export FLOAT (63),
7 energy_reactive_import FLOAT (63),
8 col_g_date NUMBER (10)
9 )
10 PARTITION BY RANGE (col_g_date)
11 SUBPARTITION BY LIST (meterid)
12 (PARTITION col_g_date_1483228800 VALUES LESS THAN (1483228800)
13 (SUBPARTITION sub1 VALUES (1),
14 SUBPARTITION sub2 VALUES (2)
15 ),
16 PARTITION col_g_date_1485820800 VALUES LESS THAN (1485820800)
17 (SUBPARTITION sub3 VALUES (3),
18 SUBPARTITION sub4 VALUES (4)
19 )
20 );
Table created.
SQL>

Oracle Partition - Error ORA14400 - inserted partition key does not map to any partition

I'm trying to insert information in a partition table, but I don't know what I'm doing wrong!
Show me this error: ORA-14400: inserted partition key does not map to any partition"
The table dba_tab_partitions shows this informations below:
1 PDIA_98_20091023 0
2 PDIA_98_20091022 0
3 PDIA_98_20091021 0
4 PDIA_98_20091020 0
5 PDIA_98_20091019 0
Please help me rs
select partition_name,column_name,high_value,partition_position
from ALL_TAB_PARTITIONS a , ALL_PART_KEY_COLUMNS b
where table_name='YOUR_TABLE' and a.table_name = b.name;
This query lists the column name used as key and the allowed values. make sure, you insert the allowed values(high_value). Else, if default partition is defined, it would go there.
EDIT:
I presume, your TABLE DDL would be like this.
CREATE TABLE HE0_DT_INF_INTERFAZ_MES
(
COD_PAIS NUMBER,
FEC_DATA NUMBER,
INTERFAZ VARCHAR2(100)
)
partition BY RANGE(COD_PAIS, FEC_DATA)
(
PARTITION PDIA_98_20091023 VALUES LESS THAN (98,20091024)
);
Which means I had created a partition with multiple columns which holds value less than the composite range (98,20091024);
That is first COD_PAIS <= 98 and Also FEC_DATA < 20091024
Combinations And Result:
98, 20091024 FAIL
98, 20091023 PASS
99, ******** FAIL
97, ******** PASS
< 98, ******** PASS
So the below INSERT fails with ORA-14400; because (98,20091024) in INSERT is EQUAL to the one in DDL but NOT less than it.
SQL> INSERT INTO HE0_DT_INF_INTERFAZ_MES(COD_PAIS, FEC_DATA, INTERFAZ)
VALUES(98, 20091024, 'CTA'); 2
INSERT INTO HE0_DT_INF_INTERFAZ_MES(COD_PAIS, FEC_DATA, INTERFAZ)
*
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
But, we I attempt (97,20091024), it goes through
SQL> INSERT INTO HE0_DT_INF_INTERFAZ_MES(COD_PAIS, FEC_DATA, INTERFAZ)
2 VALUES(97, 20091024, 'CTA');
1 row created.
For this issue need to add the partition for date column values, If last partition 20201231245959, then inserting the 20210110245959 values, this issue will occurs.
For that need to add the 2021 partition into that table
ALTER TABLE TABLE_NAME ADD PARTITION PARTITION_NAME VALUES LESS THAN (TO_DATE('2021-12-31 24:59:59', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) NOCOMPRESS

Obtaining an inserted recordid on Oracle db

I'm using Oracle on database server, from an XP client, using VB6 and ADO. In one transaction, I'm inserting one record into a parent table, which has a trigger and sequence to create a unique recordid, then that recordid is used for the relationship to a child table for a variable number of inserts to the child table. For performance, this is being sent in one execute command from my client app. For instance (simplified example):
declare Recordid int;
begin
insert into ParentTable (_field list_) Values (_data list_);
Select ParentTableSequence.currVal into Recordid from dual;
insert into ChildTable (RecordID, _field list_) Values (Recordid, _data list_);
insert into ChildTable (RecordID, _field list_) Values (Recordid, _data list_);
... multiple, variable number of additional ChildTable inserts
commit;
end;
This is working fine. My question is: I also need to return to the client the Recordid that was created for the inserts. On SQL Server, I can add something like a select to Scope_Identity() after the commit to return a recordset to the client with the unique id.
But how can I do something similar for Oracle (doesn't have to be a recordset, I just need that long integer value)? I've tried a number of things based on results from searching the 'net, but have failed in finding a solution.
These two lines can be compressed into a single statement:
-- insert into ParentTable (field list) Values (data list);
-- Select ParentTableSequence.currVal into Recordid from dual;
insert into ParentTable (field list) Values (data list)
returning ParentTable.ID into Recordid;
If you want to pass the ID back to the calling program you will need to define your program as a stored procedure or function, returning Recordid as an OUT parameter or a RETURN value respectively.
Edit
MarkL commented:
This is more of an Oracle PL/SQL
question than anything else, I
believe.
I confess that I no nothing about ADO, so I don't know whether the following example will work in your case. It involves building some infrastructure which allows us to pass an array of values into a procedure. The following example creates a new department, promotes an existing employee to manage it and assigns two new hires.
SQL> create or replace type new_emp_t as object
2 (ename varchar2(10)
3 , sal number (7,2)
4 , job varchar2(10));
5 /
Type created.
SQL>
SQL> create or replace type new_emp_nt as table of new_emp_t;
2 /
Type created.
SQL>
SQL> create or replace procedure pop_new_dept
2 (p_dname in dept.dname%type
3 , p_loc in dept.loc%type
4 , p_mgr in emp.empno%type
5 , p_staff in new_emp_nt
6 , p_deptno out dept.deptno%type)
7 is
8 l_deptno dept.deptno%type;
9 begin
10 insert into dept
11 (dname, loc)
12 values
13 (p_dname, p_loc)
14 returning deptno into l_deptno;
15 update emp
16 set deptno = l_deptno
17 , job = 'MANAGER'
18 , mgr = 7839
19 where empno = p_mgr;
20 forall i in p_staff.first()..p_staff.last()
21 insert into emp
22 (ename
23 , sal
24 , job
25 , hiredate
26 , mgr
27 , deptno)
28 values
29 (p_staff(i).ename
30 , p_staff(i).sal
31 , p_staff(i).job
32 , sysdate
33 , p_mgr
34 , l_deptno);
35 p_deptno := l_deptno;
36 end pop_new_dept;
37 /
Procedure created.
SQL>
SQL> set serveroutput on
SQL>
SQL> declare
2 dept_staff new_emp_nt;
3 new_dept dept.deptno%type;
4 begin
5 dept_staff := new_emp_nt(new_emp_t('MARKL', 4200, 'DEVELOPER')
6 , new_emp_t('APC', 2300, 'DEVELOPER'));
7 pop_new_dept('IT', 'BRNO', 7844, dept_staff, new_dept);
8 dbms_output.put_line('New DEPTNO = '||new_dept);
9 end;
10 /
New DEPTNO = 70
PL/SQL procedure successfully completed.
SQL>
The primary keys for both DEPT and EMP are assigned through triggers. The FORALL syntax is a very efficient way of inserting records (it also works for UPDATE and DELETE). This could be written as a FUNCTION to return the new DEPTNO instead, but it is generally considered better practice to use a PROCEDURE when inserting, updating or deleting.
That would be my preferred approach but I admit it's not to everybody's taste.
Edit 2
With regards to performance, bulk operations using FORALL will definitely perform better than a handful of individual inserts. In SQL, set operations are always preferable to record-by-record. However, if we are dealing with only a handful of records each time it can be hard to notice the difference.
Building a PL/SQL collection (what you think of as a temporary table in SQL Server) can be expensive in terms of memory. This is especially true if there are many users running the code, because it comes out of the session level allocation of memory, not the Shared Global Area. When we're dealing with a large number of records it is better to populate an array in chunks, perhaps using the BULK COLLECT syntax with a LIMIT clause.
The Oracle online documentation set is pretty good. The PL/SQL Developer's Guide has a whole chapter on Collections. Find out more.

Resources