Skip to Main Content
  • Questions
  • Range Partition on Virtual column - CTAS.. syntax is not working

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question.

Asked: February 06, 2018 - 8:25 pm UTC

Last updated: February 08, 2018 - 1:57 am UTC

Version: 12c

Viewed 1000+ times

You Asked

Hi Tom,

I have a requirement to create a range partition for an existing table.

Lets say the table name is Orders.

The table has Order_Date column of timestamp datatype and the data is in UTC timezone.

[We get the data in UTC format in a file to load into this table and Business want to keep it as it is so we did not populate in EST timezone when we were loading the data for previous days]

Business want query this table in EST timezone in reports going forward. And we have to create partition for this table. so our proposal is to create a virtual column which should have EST time zone and the data should be in timestamp format(should be same as the actual column ) and then create range partition on this virtual column.

I am trying to create a partitoin table using CTAS... syntax as below

Create table order_part
(Order_id,
Order_date,
Order_desc,
Order_zip,
Order_date_est as (cast(from_tz(Order_date, 'UTC') at time zone 'America/New_York' as timestamp))
)
partition by range (order_date_est) interval (numtodsinterval(1,'day'))
( partition p_before_2000 values less than (to_date('01-jan-2000','dd-mon-yyyy')) tablespace ts1 pctfree 0 )
tablespace ts1 nologging pctfree 0
AS SELECT /*+ PARALLEL(8) */ * FROM orders;

But the above code giving me error "SQL Error: ORA-01773: may not specify column datatypes in this CREATE TABLE"

I know when we use CTAS syntax we should not specify the column datatypes. But not sure how to create the virtual column while creating the partition table.

I can create a partition table with all column names and datatypes and then run a DML to insert all the data as below,

insert into order_part as select * from orders;
But in this case we have 80 millions records in the original table so not sure how much time it will take to do a insert. So please suggest me what is the best way to accomplish my requirement.

Thanks in Advance.

and Connor said...

You've got problems here no matter what you do....because timezone functions are not pure

SQL> create table order_part
  2  ( order_id int,
  3    Order_ts timestamp,
  4    Order_date_est timestamp as (cast(from_tz(Order_ts, 'UTC') at time zone 'America/New_York' as timestamp))
  5  );
  Order_date_est timestamp as (cast(from_tz(Order_ts, 'UTC') at time zone 'America/New_York' as timestamp))
                                                             *
ERROR at line 4:
ORA-54002: only pure functions can be specified in a virtual column expression


But that said, the performance cost of CTAS is about the same as using a direct mode insert

- create table
- insert /*+ APPEND */ into


Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Hum

Moris, February 07, 2018 - 9:04 am UTC

Business want query this table in EST timezone..

Adapt your queries in order to add/ retrieve the diff in predicates.
Alter session set time zone.


A reader, February 07, 2018 - 3:12 pm UTC

So Could you please suggest me any other approach how can you achieve this.

A reader, February 07, 2018 - 3:15 pm UTC

Thank for your answer.
But Could you please suggest me how can I achieve this. The table should be partitioned and should able to provide in EST time zone. If I create partition on the column in UTC timezone and when I query this table but by converting the column to EST timezone it may degrade the performance as partition will be not be effective in this case. This is just as per my knowledge. So could you please share your thoughts.
Connor McDonald
February 08, 2018 - 1:57 am UTC

Options you could explore
=========================

1) If *everything* is in EST timezone, then perhaps set the database timezone to that and partition ORDER_DATE storing it as TIMESTAMP WITH LOCAL TIMEZONE.

2) Store everything as UTC, and partition on that. It is possible to get *some* performance degradation for queries right at the partition boundary, but you could minimize that by partitioning with an hour offset as well. For example,

SQL> select timestamp '2020-01-01 05:00:00 UTC' d1,
  2         timestamp '2020-01-01 05:00:00 UTC' at time zone 'EST' d2
  3  from dual;

D1                                                 D2
-------------------------------------------------- -------------------------------------
01-JAN-20 05.00.00.000000000 AM UTC                01-JAN-20 12.00.00.000000000 AM EST


So I could use "values less than ( timestamp '2020-01-01 05:00:00')" and store all my timestamps as UTC. Queries from midnight to midnight in EST will not span boundaries.


I generally prefer all my timestamps to be stored in UTC - it's the one thing that won't change, unlike other zones where daylight saving switchovers etc all make things more complicated.

More to Explore

Design

New to good database design? Check out Chris Saxon's full fundamentals class.