Creating and Populating the Time dimension - sql

My shift_date column of my shift table has all the dates of the shift requests recieved by the agency.It contains other columns which shows whether the shift was filled or cancelled. I want to use this to create a time dimension and link to the fact table to find out how many shifts were filled each month,cancelled in a quarter,etc. i have created the time table as below. I am doing it in SQL developer and to understand dimensional modelling which I am having trouble getting my head around.
my shift table:
shift_date |shift_status|request_id|
-----------------------------------------
09-01-2011|Filled |8899
21-01-2011 |Cancelled |6677
and so on.
I created a time dimension table as below:
CREATE TABLE "DIM_TIME"
( "TIME_KEY" NUMBER(10,0),
"FULL_DATE" DATE,
"DAY_NAME" VARCHAR2(9 BYTE),
"DAY_OF_WEEK" NUMBER(5,0),
"DAY_NUMBER_IN_MONTH" NUMBER(2,0),
"DAY_NAME_ABBREVATED" VARCHAR2(5 BYTE),
"WEEKDAY_FLAG" VARCHAR2(2 BYTE),
"WEEK_OF_THE_YEAR" NUMBER(5,0),
"WEEK_BEGIN_DATE" DATE,
"MONTH_NUMBER" NUMBER(3,0),
"MONTH_NAME" VARCHAR2(20 BYTE),
"MONTH_ABBREVATED" VARCHAR2(5 BYTE),
"QUARTER" NUMBER(5,0),
"YEAR" NUMBER(5,0),
"LAST_DAY_IN_MONTH_FLAG" VARCHAR2(2 BYTE)
);
how can i extract the dates from the shift_date column to populate this time_dimension table? I am not expecting the whole code but if anyone can point me in the right direction it will be helpful as i am still learning.Thank you

This will create the rows in the DIM_TIME table:
create sequence s_time_key START WITH 1;
insert into dim_time (
time_key,
full_date,
day_name,
day_of_week,
day_number_in_month,
day_name_abbrevated,
weekday_flag,
week_of_the_year,
week_begin_date,
month_number,
month_name,
month_abbrevated,
quarter,
year,
last_day_in_month_flag
)
select
s_time_key.nextval,
d,
to_char(d,'Day'), --Monday
to_char(d,'D'), --1-7, monday=2 in some countries, 1 in others (NLS)
to_char(d,'DD'), --1-31
to_char(d,'Dy'), --Su, Mo, ...
decode(to_char(d,'Dy'),'Sa','N','Su','N','Y'),
to_char(d,'IW'), --week num ISO standard
--to_char(d,'WW'), --week num other
d+1-to_char(d,'D'), --first day in week, depending on NLS
to_number(to_char(d,'MM')),
to_char(d,'Month'),
to_char(d,'MON'),
to_char(d,'YYYYQ'),
to_char(d,'YYYY'),
decode(to_char(d+1,'DD'),'01','Y','N')
from (select distinct shift_date d from shift);
In Oracle 12c, instead of a sequence to get the time_key column, you could use create table dim_time ( time_key number(10) generated by default on null as identity, ... to autoincrement the key.
The select above will only populate table DIM_TIME with the dates that actually exists in table SHIFT. But often one will want all dates in a certain period, for instance all the days Jan 1st 2014 trough 2018 like this: replace the last line in the sql above with this:
.
from (
select d from (
select to_date('1970','YYYY')+level d from dual connect by level<=366*100
)
where to_char(d,'YYYY')
between 2014 and 2018
);
It's important to learn the to_char and to_date functions to handle dates in Oracle. https://www.techonthenet.com/oracle/functions/to_char.php

Related

ORA-14300: partitioning key maps to a partition outside maximum permitted number of partitions

I have created below table with partitioning :
CREATE TABLE FRD_TBL_H (DAT NUMBER(8), DATST NUMBER(8), MEST VARCHAR2(1 CHAR), MEST1 VARCHAR2(1 CHAR), BELD CHAR(11 CHAR),VALID_FROM DATE, VALID_TO DATE)
PARTITION BY RANGE(valid_to)
INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
( PARTITION p0 VALUES LESS THAN (TO_DATE('01-01-1999', 'DD-MM-YYYY')))
ENABLE ROW MOVEMENT;
I am trying to insert data into this table by keeping valid_to value to null and also i tried valid_to value to '01-01-1999' but getting below error:
ORA-14300: partitioning key maps to a partition outside maximum permitted number of partitions
I did partioning by month on valid_to column because the table has 70 million rows and i am writing below mere query for historization logic:
MERGE INTO FRD_TBL_H e
USING FRD_TBL h
ON (e.beld = h.beld)
WHEN MATCHED THEN
UPDATE SET e.valid_from = sysdate
where e.valid_to = 01.01.1999
The maximum number of partitions in Oracle is 1024K - 1 = 1048575
What are you probably doing is the following. You created the table with day partitioning. (Not month as you posted).
CREATE TABLE FRD_TBL_H (DAT NUMBER(8), DATST NUMBER(8), MEST VARCHAR2(1 CHAR), MEST1 VARCHAR2(1 CHAR), BELD CHAR(11 CHAR),VALID_FROM DATE, VALID_TO DATE)
PARTITION BY RANGE(valid_to)
INTERVAL(NUMTODSINTERVAL(1, 'DAY'))
( PARTITION p0 VALUES LESS THAN (TO_DATE('01-01-1999', 'DD-MM-YYYY')))
ENABLE ROW MOVEMENT;
And used a sufficiently high valid_to date e.g. DATE'4999-01-01'
insert into FRD_TBL_H( VALID_TO) values (DATE'4999-01-01');
ORA-14300: partitioning key maps to a partition outside maximum permitted number of partitions
A simple calculation shows, that the partition limit is indeed broken
select 1024*1024-1 max_part, DATE'4999-01-01' - TO_DATE('01-01-1999', 'DD-MM-YYYY') day_diff from dual;
MAX_PART DAY_DIFF
---------- ----------
1048575 1095728
So this is expected bahavior.
The real question is, why do you use valid_to as the partition key and not valid_from.
With valid_from you will
have not the problem with the null or dummy value
the column will not change, so no need for row movement
most queries use the predicate :1 >= valid_from so the pruning will work fine
To get an efficient acces to the open (last) version you may use composite partitioning with validfrom date range partitioning and list subpartitioning for open / closed versions.

Range partition on CHAR/VARCHAR2 data type for every month

We have a table where we are trying to create a RANGE partition on CHAR data type for each month as mentioned below. But we are getting errors.The ACC_DATE value will be like '202010'. Can this be done in ORACLE? because the same can be done in DB2. But we have to implement it in ORACLE.
Error we are getting:
ORA-14751: Invalid data type for partitioning column of an interval partitioned table
CREATE TABLE T_ACCOUNT_PARTITION
(
V_ACCOUNT_NUM VARCHAR2(100 CHAR),
V_ACCOUNT_NAME VARCHAR2(200 CHAR),
ACC_DATE CHAR(6)
)
PARTITION BY RANGE (ACC_DATE) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(PARTITION P_MAY2021 VALUES LESS THAN (202105));
As already mentioned you should never store date or time values as strings, use always proper DATE or TIMESTAMP data type.
As a workaround you can use VIRTUAL column like this:
CREATE TABLE T_ACCOUNT_PARTITION
(
V_ACCOUNT_NUM VARCHAR2(100 CHAR),
V_ACCOUNT_NAME VARCHAR2(200 CHAR),
ACC_DATE CHAR(6),
PARTITION_KEY TIMESTAMP(0) GENERATED ALWAYS AS (TO_TIMESTAMP(ACC_DATE, 'YYYYMM')),
)
PARTITION BY RANGE (PARTITION_KEY) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(PARTITION P_MAY2021 VALUES LESS THAN (TIMESTAMP '2021-05-01 00:00:00'));

how to create date

how to create date format yyyy-mm with postgresql11
CREATE TABLE public."ASSOL"
(
id integer NOT NULL,
"ind" character(50) ,
"s_R" character(50) ,
"R" character(50) ,
"th" character(50),
"C_O" character(50) ,
"ASSOL" numeric(11,3),
date date,
CONSTRAINT "ASSOL_pkey" PRIMARY KEY (id)
This is a variation of Kaushik's answer.
You should just use the date data type. There is no need to create another type for this. However, I would implement this use a check constraint:
CREATE TABLE public.ASSOL (
id serial primary key,
ind varchar(50) ,
s_R varchar(50) ,
R varchar(50) ,
th varchar(50),
C_O varchar(50) ,
ASSOL numeric(11,3),
yyyymm date,
constraint chk_assol_date check (date = date_trunc('month', date))
);
This only allows you to insert values that are the first day of the month. Other inserts will fail.
Additional notes:
Don't use double quotes when creating tables. You then have to refer to the columns/tables using double quotes, which just clutters queries. Your identifiers should be case-insensitive.
An integer primary key would normally be a serial column.
NOT NULL is redundant for a PRIMARY KEY column.
Use reasonable names for columns. If you want a column to represent a month, then yyyymm is more informative than date.
Postgres stores varchar() and char() in the same way, but for most databases, varchar() is preferred because trailing spaces actually occupy bytes on the data pages.
for year and month you can try like below
SELECT to_char(now(),'YYYY-MM') as year_month
year_month
2019-05
You cannot create a date datatype that stores only the year and month component. There's no such option available at the data type level.
If you want to to truncate the day component to default it to start of month, you may do it. This is as good as having only the month and year component as all the dates will have day = 1 and only the month and year would change as per the time of running insert.
For Eg:
create table t ( id int, col1 text,
"date" date default date_trunc('month',current_date) );
insert into t(id,col1) values ( 1, 'TEXT1');
select * from t
d col1 date
1 TEXT1 2019-05-01
If you do not want to store a default date, simply use the date_trunc('month,date) expression wherever needed, it could either be in group by or in a select query.

ORA-00984 Column not allowed here with date in oracle SQL

Im inserting values into this table
CREATE TABLE Flight (
FlightNumber char(7) primary key,
ArrivalAirportCode char(6) references Airport (Airport_code),
DepartureAirportCode char(6) references Airport (Airport_code),
AircraftNumber varchar2(25) references Aircraft (AircraftNumber),
ArrivalDate date,
ArrivalTime Varchar2(5),
DepartureDate date,
DepartureTime varchar2(5)
);
and here are the values Im inserting into it
INSERT INTO FLIGHT values
('CA3048',
'LHR',
'EDI',
'N859E',
'14-NOV-2014',
'22:15',
'14-NOV-2014',
'20:15');
And I get the column not allowed here error for the 2nd date I insert, but not the first one. I've tried putting quotes around the date but I just get another error.
'14-NOV-2014'
Why are you inserting a string in a DATE column? '14-NOV-2014' is a STRING and NOT a DATE. You should not depend on implicit data type conversion.
Always, convert the string into a DATE explicitly using TO_DATE and proper format mask.
For example,
TO_DATE('14-NOV-2014','DD-MON-YYYY')
One more thing,
DepartureTime varchar2(5)
Makes no sense. You already have a DATE column, a DATE would have the time element too.
No need of a separate time column. A DATE has both date and time elements stored in 7 bytes.
Oracle stores DATE in total of 7 bytes. Each byte in it stores values for an element of the DATE as follows:
Byte Description
---- ------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
All you need to do is just have 2 DATE columns:
CREATE TABLE Flight (
FlightNumber char(7) primary key,
ArrivalAirportCode char(6) references Airport (Airport_code),
DepartureAirportCode char(6) references Airport (Airport_code),
AircraftNumber varchar2(25) references Aircraft (AircraftNumber),
ArrivalDate date,
DepartureDate date
);
And then insert the values as:
INSERT INTO FLIGHT values
('CA3048',
'LHR',
'EDI',
'N859E',
TO_DATE('14-NOV-2014 22:15:00','DD-MON-YYYY HH24:MI:SS'),
TO_DATE('14-NOV-2014 20:15:00','DD-MON-YYYY HH24:MI:SS')
);
Update
As mentioned in the comments by #GriffeyDog and #a_horse_with_no_name.
Alternatively, you could also the ANSI literal instead, for example:
timestamp '2014-11-14 22:15'

SQL server: retrieve grouped data from a table

Write SQL query based on info below Table TRACK_BY_WCN
CREATE TABLE CONTRACTOPS.TRACK_BY_WCN(
CLAIM_TYPE CHAR(1 BYTE)
,LOBID CHAR(3 BYTE)
,WCN VARCHAR2(10 BYTE)
,RECEIVED_DATE DATE
,FOUND_DATE DATE
,CLAIM_NUMBER VARCHAR2(10 BYTE) DEFAULT NULL
,HOLD_FLAG CHAR(1 BYTE) DEFAULT NULL
,LOCK_FLAG VARCHAR2(3 BYTE) DEFAULT NULL
,BILLED NUMBER(16,2))
There are data in first 3 columns, CLAIM_TYPE, LOBID, WCN, and there is program doing match by WCN. When found, the program will update all other fields. Please use one SQL to get count on how many WCN and how many matched claims, by claim type.
It's hard to tell what you are asking exactly. This might be what you want:
Count rows per WCN:
SELECT WCN, count(*) AS row_count
FROM CONTRACTOPS.TRACK_BY_WCN
GROUP BY WCN;
Count rows per CLAIM_TYPE
SELECT CLAIM_TYPE, count(*) AS row_count
FROM CONTRACTOPS.TRACK_BY_WCN
GROUP BY CLAIM_TYPE;
Count claims per WCN and CLAIM_TYPE
SELECT WCN, CLAIM_TYPE, count(*) AS row_count
FROM CONTRACTOPS.TRACK_BY_WCN
GROUP BY WCN, CLAIM_TYPE;