ORACLE HOW TO APPLY CTAS WITH SUBPARTITION? - sql

I am trying to build subpartitioned table with CTAS from non-partitioned table but I got errors. How can I do that.Here is similiar example for my data ID IS PRIMARY KEY:
TASK
ID START_DATE
1 22/09/21
2 21/09/21
3 20/09/21
And Here is my code :
CREATE TABLE EMPLOYEE AS
(SELECT * FROM TASK)
PARTITION BY RANGE ("START_DATE")
SUBPARTITION BY LIST("ID")
PARTITION P1 VALUES LESS THAN TO_DATE('20210920','YYYYYMMDD') (
SUBPARTITION S1 VALUES (1),
SUBPARTITION S2 VALUES (2),
SUBPARTITION S3 VALUES (3)
),
PARTITION P2 VALUES LESS THAN TO_DATE('20210921','YYYYYMMDD') (
SUBPARTITION S4 VALUES (1),
SUBPARTITION S5 VALUES (2),
SUBPARTITION S6 VALUES (3)
),
PARTITION P3 VALUES LESS THAN TO_DATE('20210922','YYYYYMMDD') (
SUBPARTITION S7 VALUES (1),
SUBPARTITION S8 VALUES (2),
SUBPARTITION S9 VALUES (3)
),
PARTITION P4 VALUES LESS THAN MAXVALUE (
SUBPARTITION S10 VALUES (1),
SUBPARTITION S11 VALUES (2),
SUBPARTITION S12 VALUES (3)
)
How can I do that it is important for me I am getting this error and I think I will get another errors when I correct it:
00933. 00000 - "SQL command not properly ended"

An example
SQL> create table task ( id number , start_date date ) ;
Table created.
SQL> CREATE TABLE t2
2 PARTITION BY RANGE (START_DATE)
3 SUBPARTITION BY LIST (ID)
4 (
5 PARTITION P1 VALUES LESS THAN (TO_DATE('2021-07-20','YYYY-MM-DD'))
6 (
7 SUBPARTITION S1 VALUES (1),
8 SUBPARTITION S2 VALUES (2),
9 SUBPARTITION S3 VALUES (3)
10 ),
11 PARTITION P2 VALUES LESS THAN (TO_DATE('2021-08-20','YYYY-MM-DD'))
12 (
13 SUBPARTITION S4 VALUES (1),
14 SUBPARTITION S5 VALUES (2),
15 SUBPARTITION S6 VALUES (3)
16 )
17 )
18* as select * from task
SQL> /
Table created.
Considerations
Use the expression as select * from xxx at the end of the construction.
Keep in mind the parenthesis for each partition and subpartition construction.
Although I did not put it on my example, try to use a subpartition template.

I would recommend an INTERVAL partition and subpartition template. Would be this one:
CREATE TABLE EMPLOYEE (
...
)
PARTITION BY RANGE (START_DATE) INTERVAL (INTERVAL '1' DAY)
SUBPARTITION BY LIST (ID)
SUBPARTITION TEMPLATE (
SUBPARTITION S1 VALUES (1),
SUBPARTITION S2 VALUES (2),
SUBPARTITION S3 VALUES (3)
)
( PARTITION P_INITIAL VALUES LESS THAN (TIMESTAMP '2021-09-01 00:00:00') SEGMENT CREATION DEFERRED );

Related

Looping in SQL Server from 1 to 60

I have a table T1 as below
I need to copy the data from T1 to another table called T2. T2 has an additional column called 'Month' and each record from T1 needs to be copied to T2 60 times, with Month value ranging from 1 to 60.
I have been trying something like this and need the MONTH value to be taken dynamically , like a loop from 1 to 60. Could someone help please? Thank you
INSERT INTO T2
SELECT PRODUCT, CUSTOMER, 1 as MONTH
FROM T1
We can use a cross join approach:
WITH months AS (
SELECT n = v2.n * 10 + v1.n
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) v1(n)
CROSS JOIN (VALUES (0), (1), (2), (3), (4), (5), (6)) v2(n)
)
INSERT INTO T2 (Product, Customer, Month)
SELECT t1.Product, t1.Customer, m.n
FROM table1 t1
CROSS JOIN months m
WHERE m.n BETWEEN 1 AND 60;
CROSS JOIN to a tally, with the values 1 to 60. -- Thanks #Larnu for the answer.
I used stored procedures in MySQL:
DELIMITER $$
CREATE PROCEDURE auto_insert()
BEGIN
DECLARE i1 INT DEFAULT 1;
WHILE i1 <= 60 DO
INSERT INTO T2 SELECT *, i1 FROM T1;
SET i1 = i1 + 1;
END WHILE;
END $$
DELIMITER ;
CALL auto_insert;

Split maxvalue partition in Oracle with new subpartition template

How can I split maxvalue partition in Oracle with new subpartition template.
Or
How can I split maxvalue partition in Oracle and add also add new subpartitions parallely
You can use SPLIT PARTITION as following:
Oracle setup:
SQL> CREATE TABLE PART_EXAMPLE (
2 ID NUMBER,
3 CREATED_DATE DATE
4 )
5 PARTITION BY RANGE (
6 CREATED_DATE
7 )
8 ( PARTITION T1_2019
9 VALUES LESS THAN ( MAXVALUE )
10 );
Table created.
Checking the partitions:
SQL> SELECT
2 TABLE_NAME,
3 PARTITION_NAME
4 FROM
5 USER_TAB_PARTITIONS
6 WHERE TABLE_NAME = 'PART_EXAMPLE';
TABLE_NAME PARTITION_
--------------- ----------
PART_EXAMPLE T1_2019
Splitting the partition into two partitions:
SQL> ALTER TABLE PART_EXAMPLE
2 SPLIT PARTITION T1_2019 AT (TO_DATE('31-DEC-2017 23:59:59', 'DD-MON-YYYY HH24:MI:SS'))
3 INTO (PARTITION T1_2017, PARTITION T1_2019)
4 ONLINE;
Table altered.
Now, Checking the partitions:
SQL> SELECT
2 TABLE_NAME,
3 PARTITION_NAME
4 FROM
5 USER_TAB_PARTITIONS
6 WHERE TABLE_NAME = 'PART_EXAMPLE';
TABLE_NAME PARTITION_
--------------- ----------
PART_EXAMPLE T1_2017
PART_EXAMPLE T1_2019
SQL>
I hope it is clear.
Cheers!!
You can set the subpartitions template for a table. But this only affects new partitions. It has no impact on existing partitions:
create table t (
c1 int, c2 int
) partition by range ( c1 )
subpartition by hash ( c2 )
subpartition template 2
(
partition p0 values less than ( 1 ),
partition pmax values less than ( maxvalue )
);
select partition_name, subpartition_name
from user_tab_subpartitions
where table_name = 'T';
PARTITION_NAME SUBPARTITION_NAME
P0 SYS_SUBP2922
P0 SYS_SUBP2923
PMAX SYS_SUBP2924
PMAX SYS_SUBP2925
alter table t
set subpartition template 1;
select partition_name, subpartition_name
from user_tab_subpartitions
where table_name = 'T';
PARTITION_NAME SUBPARTITION_NAME
P0 SYS_SUBP2922
P0 SYS_SUBP2923
PMAX SYS_SUBP2924
PMAX SYS_SUBP2925
You can also set the template while splitting. But again, this doesn't affect the existing partitions:
alter table t
split partition pmax at ( 2 )
into (
partition p1 set subpartition template 1,
partition pmax set subpartition template 1
);
select partition_name, subpartition_name
from user_tab_subpartitions
where table_name = 'T';
PARTITION_NAME SUBPARTITION_NAME
P0 SYS_SUBP2922
P0 SYS_SUBP2923
P1 SYS_SUBP2926
P1 SYS_SUBP2927
PMAX SYS_SUBP2924
PMAX SYS_SUBP2925
If you want to change the subpartitions while splitting a partition, define them in the subpartitions clause:
alter table t
split partition pmax at ( 3 )
into (
partition p2 subpartitions 1,
partition pmax subpartitions 4
);
select partition_name, subpartition_name
from user_tab_subpartitions
where table_name = 'T';
PARTITION_NAME SUBPARTITION_NAME
P0 SYS_SUBP2936
P0 SYS_SUBP2937
P1 SYS_SUBP2940
P1 SYS_SUBP2941
P2 SYS_SUBP2942
PMAX SYS_SUBP2943
PMAX SYS_SUBP2944
PMAX SYS_SUBP2945
PMAX SYS_SUBP2946

Hive: how to select from specfic sub-partiton

For example, create a table in hive.
CREATE TABLE t_data_daily(
imp_date BIGINT,
sp STRING,
datax STRING
)
PARTITION BY LIST( imp_date )
SUBPARTITION BY LIST( sp )(
SUBPARTITION sp_1 VALUES IN ( 'sp_1' ),
SUBPARTITION sp_2 VALUES IN ( 'sp_2' ),
SUBPARTITION sp_3 VALUES IN ( 'sp_3' )
)
(
PARTITION p_20191030 VALUES IN ( 20191030 ),
PARTITION p_20191101 VALUES IN ( 20191101 ),
PARTITION p_20191122 VALUES IN ( 20191122 )
)
select data with specific partition:
select * from t_data_daily partition (p_20191030) x limit 100
How to select data from specific partition and sub-partition?
Except the following:
select * from t_data_daily partition (p_20191030) x where sp = 'sp_1' limit 100

SQL sectioning/averaging based on different timetag/timestamps and user-chosen input (T-SQL)

I've got the following problem that I would like to cope with - I have a SQL dataset that I would like to section (e.g. like this one):
OldTimetag OldValue
2012-05-03 12:47:00 5
2012-05-03 13:00:00 1.3
2012-05-03 13:21:00 7
2012-05-03 14:56:00 5
2012-05-03 14:57:00 0.3
.... ....
Now, I want to section (and/or average) the data based on a user-chosen interval - into new timetags, e.g. every 15 minutes with the first timetag as starting point, i.e.:
NewTimetag NewValue
2012-05-03 12:47:00 4.507
2012-05-03 13:02:00 1.3
.... ....
The main constraint is that the value next to the timetag is always valid, until the next timetag appears. So the value of 5 at timetag 2012-05-03 12:47:00 is valid for the next 13 minutes until 13:00:00. The value for the first 15 minutes from 12:47:00 would be (13*5+2*1.3)/15 = 4.507. In the next 15 minutes, at 13:02:00 the value is simply equal to 1.3... (and so on)
I've come so long, that it is a good idea to make an "artificial table" first, to later join it with the old table. I'm generating that table by:
DECLARE #intStart datetime, #intEnd datetime
SELECT #intStart =min(OldTimetag), #intEnd = MAX(OldTimetag)
FROM OldTable
where OldTimetag between '2012-05-03 12:47:00' and '2012-05-03 14:57:00'
Declare #ArtificalTable table (NewTimeTag datetime, NewValue Float)
Declare #MinuteSlicer Int
Set #MinuteSlicer = 15
Insert #Hallo Select #intStart, null
While ( #intStart < #intEnd ) BEGIN
Insert #ArtificalTable
Select DATEADD(mi,#MinuteSlicer, #intStart), Null
Set #intStart = DATEADD(mi,#MinuteSlicer,#intStart)
If #intEnd <= DATEADD(mi,#MinuteSlicer,#intStart)
Break
End
This gives me an output like:
NewTimetag NewValue
2012-05-03 12:47:00 Null
2012-05-03 13:02:00 Null
.... ....
However, I'm having problems with the next step, how to join the tables correctly - can anyone give me a hint?
Here is one way of doing it.
Sample Data:
declare #data table(OldTimetag datetime2, OldValue numeric(5,2));
Insert into #data(OldTimetag, OldValue) Values
('2012-05-03 12:47:00', 5)
, ('2012-05-03 13:00:00', 1.3)
, ('2012-05-03 13:21:00', 7)
, ('2012-05-03 14:56:00', 5)
, ('2012-05-03 14:57:00', 0.3);
Your custom range size in minutes:
declare #mins int = 15;
List is used to quickly compute an ordered list of number from 0 to n Where n <= to the number of minutes between the first and the last OldTimetag.
With list(n) as (
Select top(Select 1+DATEDIFF(minute, min(OldTimetag), max(OldTimetag)) From #data)
ROW_NUMBER() over(order by (select 1))-1
From (
Select 1 From (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x1(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x2(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x3(n)
) as x(n)
)
Select NewTimetag = DATEADD(minute, #mins*(l.n/#mins), MIN(r.startTime)), NewValue = AVG(d.oldValue)
From list l
Cross Join (Select startTime = min(OldTimetag) From #data) as r
Cross Apply (Select maxTimetag = MAX(OldTimetag) From #data Where OldTimetag <= DATEADD(minute, n, startTime)) as mx
Inner Join #data d on d.OldTimetag = mx.maxTimetag
Group By l.n/#mins
Cross Join is used to mix each number from the ordered list with the first OldTimetag from your data.
Cross Apply is used to get the nearest OldTimetag before each minute created with the Cross Join.
Inner Join then matches the nearest OldTimetag with your data in order to retrieved oldValue.
Select only have to calculate the average for each range on #mins minutes and its NewTimetag.
It works well for a range of up to 1000 minutes between the min and max OldTimetag. If you need to go beyond this limit, you can add a a 4th line in the list CTE:
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x4(n) => up to 10.000
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x5(n) => up to 100.000
...
One way is to determine the intervals (an interval is generated if it contains at least one timestamp), augment the time table with the next timestamp and then calculate the averages for each such interval by intersecting the intervals with the time table.
IF OBJECT_ID('tempdb..#values') IS NOT NULL DROP TABLE #values
CREATE TABLE #values (pk int identity, time datetime, value numeric(10,4))
INSERT INTO #values VALUES ('2012-05-03 12:47:00', 5)
INSERT INTO #values VALUES ('2012-05-03 13:00:00', 1.3)
INSERT INTO #values VALUES ('2012-05-03 13:21:00', 7)
INSERT INTO #values VALUES ('2012-05-03 14:56:00', 5)
INSERT INTO #values VALUES ('2012-05-03 14:57:00', 0.3)
DECLARE #timeSpanMinutes int SET #timeSpanMinutes=15
DECLARE #startTime datetime, #endTtime datetime
SELECT #startTime=MIN(time) FROM #values
SELECT #endTtime =DATEADD(MINUTE,(DATEDIFF(MINUTE,#startTime,MAX(time))
/#timeSpanMinutes+1)*#timeSpanMinutes, #startTime) FROM #values -- MAX(time) multiple
SELECT intervals.start
, SUM(value*(DATEDIFF(MINUTE -- minutes in intersection of [start,end] and [time,next]
, CASE WHEN time<start THEN start ELSE time END -- Maximum(time,start)
, CASE WHEN next<DATEADD(MINUTE,#timeSpanMinutes,intervals.start) THEN next
ELSE DATEADD(MINUTE,#timeSpanMinutes,intervals.start) END -- Minimum(next,end)
)*1.0/#timeSpanMinutes)) as average
FROM
(SELECT DISTINCT DATEADD(MINUTE, (DATEDIFF(MINUTE,#startTime,time)
/#timeSpanMinutes)*#timeSpanMinutes, #startTime) AS start
FROM #values -- round start to multiple of #timeSpanMinutes
UNION SELECT DISTINCT DATEADD(MINUTE,#timeSpanMinutes+(DATEDIFF(MINUTE,#startTime,time)
/#timeSpanMinutes)*#timeSpanMinutes, #startTime)
FROM #values -- union distinct with same as above but shifted with #timeSpanMinutes
) intervals -- intervals start time (end is calculated as start + #timeSpanMinutes)
INNER JOIN
(SELECT v.*,ISNULL((SELECT MIN(time) FROM #values WHERE time>v.time),#endTtime) as next
FROM #values v -- add next column to #values
) vals
ON vals.next>=intervals.start and vals.time<=DATEADD(MINUTE,#timeSpanMinutes,start)
WHERE intervals.start<>#endTtime
GROUP BY intervals.start
ORDER BY intervals.start

Range partation

I have a table with following structure,
CREATE TABLE test_range_partition (
table_name VARCHAR2(30),
order_date DATE,
num_rows NUMBER
) PARTITION BY RANGE(num_rows) (
PARTITION num_rows1 VALUES LESS THAN (100) TABLESPACE part1,
PARTITION num_rows2 VALUES LESS THAN (1000) TABLESPACE part2,
PARTITION num_rows3 VALUES LESS THAN (10000) TABLESPACE part3,
PARTITION num_rows4 VALUES LESS THAN (MAXVALUE) TABLESPACE part4
);
If I want to select data
select * from test_range_partition where num_rows =100
which partition is selected?
num_rows1 contain 1 to 99
num_rows2 contain 100 to 999
num_rows1 contain 1000 to 9999 and so on
So your answer is num_rows2