简体   繁体   中英

Partitioning a Oracle table using a function over a column

I would like to partition a table using a function over a column, due to I don't want to create a new column. My table Example has a column DATE_VARCHAR (format 'YYYY-MM-DD') and I would like to partition it given the month of the date, but I have some problems.

  1. I can't ALTER my table to add the partitioning (Oracle requirements), so I create a backup table, to backup the data.
  2. I can't add a column in the new structure (client requirements), so if I want to do the partitioning using substring function over DATE_VARCHAR, I get a syntax error:

     CREATE TABLE PRUEBA( DATE_VARCHAR VARCHAR2(10), SOME_COLUMNS VARCHAR(50) ) PARTITION BY LIST (SUBSTR(DATE_VARCHAR,6,2)) (PARTITION p1 VALUES ('01','05','09'), PARTITION p2 VALUES ('02','06','10'), PARTITION p3 VALUES ('03','07','11'), PARTITION p4 VALUES ('04','08','12'));

    Due to it expects an identifier column (column name), in spite of I get the following error: ORA-00907 missing right parenthesis .

  3. If I create a column with the month value:

     CREATE TABLE Example( DATE_VARCHAR VARCHAR2(10), SOME_COLUMNS VARCHAR(50), MONTH VARCHAR2(2) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL ) PARTITION BY LIST (MONTH) (PARTITION p1 VALUES ('01','05','09'), PARTITION p2 VALUES ('02','06','10'), PARTITION p3 VALUES ('03','07','11'), PARTITION p4 VALUES ('04','08','12'));

I get the following error: ORA-12899 value too large for column %s (actual: %s, maximum: %s) , due to the new column is only length 2 (and the source column is length 10); in spite of I'm doing a substring of length 2.

The only way to do this, is the 3? Exists another way to solve this?

Edit: If I do the next, it works for me:

CREATE TABLE Example(
DATE_VARCHAR VARCHAR2(10),
SOME_COLUMNS VARCHAR(50),
MONTH VARCHAR2(**10**) GENERATED ALWAYS AS
    (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL
)

PARTITION BY LIST (MONTH)
(PARTITION p1 VALUES ('01','05','09'),
PARTITION p2 VALUES ('02','06','10'),
PARTITION p3 VALUES ('03','07','11'),
PARTITION p4 VALUES ('04','08','12'));

But I don't understand why column needs have length equal 10, and not 2, having in count the substring.

Edit: Oracle version在此处输入图片说明

Error at script execution: 在此处输入图片说明

Edit: Execution trough sqlplus:

SQL> CREATE TABLE tabx(
  2      DATE_VARCHAR VARCHAR2(10),
    SOME_COLUMNS VARCHAR(50),
    MONTH VARCHAR2(2) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL
)
PARTITION BY LIST (MONTH)
(PARTITION p1 VALUES ('01','05','09'),
PARTITION p2 VALUES ('02','06','10'),
PARTITION p3 VALUES ('03','07','11'),
PARTITION p4 VALUES ('04','08','12'),
-- need default in case of bad data format
partition others values (default)
);

-- NOTE: this WON'T work now after adding VIRTUAL column
--insert into tabx values ('2015-12-01', 'ABC');

-- but this will (must specify columns)
  3    4    5    6    7    8    9   10   11   12   13  insert into tabx(date_varchar,some_columns) values ('2016-01-01', 'XYZ');
    MONTH VARCHAR2(2) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL
    *
ERROR at line 4:
ORA-12899: value too large for column "MONTH" (actual: 2, maximum: 8)


SQL> SQL> SQL> SQL> SQL> SQL> insert into tabx(date_varchar,some_columns) values ('2016-01-01', 'XYZ')
            *
ERROR at line 1:
ORA-00942: table or view does not exist

Here is what worked for me. Its case 3 (virtual column). It techncally violates your client requirement that no new columns be created, although virtual, its still a column. Anyway:

CREATE TABLE tabx(
    DATE_VARCHAR VARCHAR2(10),
    SOME_COLUMNS VARCHAR(50),
    MONTH VARCHAR2(2) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL
)
PARTITION BY LIST (MONTH)
(PARTITION p1 VALUES ('01','05','09'),
PARTITION p2 VALUES ('02','06','10'),
PARTITION p3 VALUES ('03','07','11'),
PARTITION p4 VALUES ('04','08','12'),
-- need default in case of bad data format
partition others values (default)
);

-- NOTE: this WON'T work now after adding VIRTUAL column
--insert into tabx values ('2015-12-01', 'ABC');

-- but this will (must specify columns)
insert into tabx(date_varchar,some_columns) values ('2016-01-01', 'XYZ');

commit;

Note that the default partition is created as well.

ADDING SCRIPT OUTPUT HERE :

Running the above on 11.2 instance (with additional select from table) gives:

SQL> set lines 500
SQL> drop table tabx
Table dropped.
SQL> CREATE TABLE tabx(
    DATE_VARCHAR VARCHAR2(10),
    SOME_COLUMNS VARCHAR(50),
    MONTH VARCHAR2(2) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL
)
PARTITION BY LIST (MONTH)
(PARTITION p1 VALUES ('01','05','09'),
PARTITION p2 VALUES ('02','06','10'),
PARTITION p3 VALUES ('03','07','11'),
PARTITION p4 VALUES ('04','08','12'),
-- need default in case of bad data format
partition others values (default)
)
Table created.
SQL> -- NOTE: this WON'T work now after adding VIRTUAL column
SQL> --insert into tabx values ('2015-12-01', 'ABC');
SQL> -- but this will (must specify columns)
SQL> insert into tabx(date_varchar,some_columns) values ('2016-01-01', 'XYZ')
1 row created.
SQL> commit
Commit complete.
SQL> select * from tabx partition(p1)

DATE_VARCHAR SOME_COLUMNS                                       MONTH
------------ -------------------------------------------------- -----
2016-01-01   XYZ                                                01   
1 row selected.

EDIT :

Just a guess, but if the above doesn't work for you, perhaps the issue is with multi-byte encoding (which is why I was asking about your NLS_LANG settings on client and server). Anyway, what happens if you specify the MONTH column as:

MONTH VARCHAR2(2 CHAR) GENERATED ALWAYS AS (SUBSTR(DATE_VARCHAR,6,2)) VIRTUAL

Here I'm specifying 2 characters instead of 2 bytes. Again, just a guess, but easy to check.

Not sure if you have gone to 12c yet on your database. But, if you have then you can use a virtual invisible column to achieve what you are trying to do without affecting the database.

CREATE TABLE example
(
    date_varchar    VARCHAR2(10)
  , some_columns    VARCHAR(50)
  , month           NUMBER(2, 0) INVISIBLE GENERATED ALWAYS AS(EXTRACT(MONTH FROM TO_DATE(date_varchar, 'YYYY-MM-DD'))) VIRTUAL
)
PARTITION BY LIST(month)
(
    PARTITION p1 VALUES (1, 5, 9)
  , PARTITION p2 VALUES (2, 6, 10)
  , PARTITION p3 VALUES (3, 7, 11)
  , PARTITION p4 VALUES (4, 8, 12)
);
No rows affected (0.023 seconds)

Because the column is invisible it isn't required for an insert.

INSERT INTO example VALUES ('2015-12-01', 'ABC');
1 row affected (0.056 seconds)

And the row gets put into the right partition.

SELECT * FROM example PARTITION(p4);
+--------------+--------------+
| DATE_VARCHAR | SOME_COLUMNS |
+--------------+--------------+
| 2015-12-01   | ABC          |
+--------------+--------------+
1 row selected (0.016 seconds)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM