Temporal tables in Teradata: different behaviour as compared to the documentation - sql

I just started exploring Teradata's temporal table implementation and it seems that my version of Teradata (16.20) is not behaving in the way as it is supposed to:
Example is taken from Teradata documentation
Create temporal table:
CREATE MULTISET TABLE Policy(
Policy_ID INTEGER,
Customer_ID INTEGER,
Policy_Type CHAR(2) NOT NULL,
Policy_Details CHAR(40),
Validity PERIOD(DATE) NOT NULL AS VALIDTIME
)
PRIMARY INDEX(Policy_ID);
And insert row data with valid time period'(2009-10-01,2010-10-01)'
INSERT INTO Policy
(Policy_ID, Customer_ID, Policy_Type, Policy_Details, Validity)
VALUES (541008, 246824626, 'AU', 'STD-CH-345-NXY-00',
PERIOD(DATE '2009-10-01', 2010-10-01));
INSERT INTO Policy
(Policy_ID, Customer_ID, Policy_Type, Policy_Details, Validity)
VALUES (99999, 11111, 'AU', 'STD-CH-345-NXY-00',
PERIOD(DATE '2009-10-01', until_changed));
If I now query (corresponding to the doc)
SELECT * FROM Policy;
I should actually only get the results which are still valid (intersection not null with today '2023-02-01'--'9999-12-31') and without the validity column
so
Policy_ID Customer_ID Policy_Type Policy_Details
--------- ----------- ----------- ---------------
99999 11111 'AU' 'STD-CH-345-NXY-00'
but I get the whole table (and complete history):
Policy_ID Customer_ID Policy_Type Policy_Details Validity
--------- ----------- ----------- --------------- -------
99999 11111 'AU' 'STD-CH-345-NXY-00' (2009-10-01,9999-12-31)
541008 246824626 'AU' 'STD-CH-345-NXY-00' (2009-10-01,2010-10-01)
which is exactly the same result as when queried via the nonsequenced query
NONSEQUENCED VALIDTIME SELECT * FROM Policy;
Why is this the case, am I missing something? Is it possible, that my "select" somehow defaults to a different behaviour? How can I make sure that the temporal versioning is actually working the way it should?

Related

How to select max price using multiple query

i have create tow table, table and data inserting value are given below :
create table product (
pid number(10) primary key,
pname varchar2(30)
);
INSERT INTO product values(100,'Apple');
INSERT INTO product values(101,'Banana');
INSERT INTO product values(102,'Pinaple');
INSERT INTO product values(103,'Orange');
create table purchase(
invid number(10) primary key,
pid number(10),
pprice number(10)
);
alter table purchase add(constraint pid_fk FOREIGN KEY (pid) references product(pid));
INSERT INTO purchase values(10,101,30);
INSERT INTO purchase values(11,103,35);
INSERT INTO purchase values(12,103,9);
INSERT INTO purchase values(13,103,22);
INSERT INTO purchase values(14,101,12);
now i have select table purchase show give data
invid pid pprice
10 101 30
11 103 35
12 103 9
13 103 22
14 101 12
now i want to be last insert price pid=103
now need show pprice=22
i have all ready done the task using two query such as:
select max(invid) from purchase where pid=103;
result is 22 and running next statement
select max(pprice) from purchase where invid=13;
show result =22
i want to show result 22 using running one statment,
Probably the most direct way is to use a subquery:
SELECT MAX(pprice)
FROM purchase
WHERE invid = (SELECT MAX(invid) FROM purchase WHERE pid = 103)
Side note, you don't need to MAX() pprice based on your sample data, but I left it that way in case you need it for your real data set.
The aggregate FIRST/LAST function is made exactly for this kind of task. Unfortunately it seems the vast majority of developers don't use it - either because they are not aware of its existence, or because they don't understand how it works, or for who knows what other reason.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions056.htm
The function allows for the case when the max(invid) (in your model) is not unique; in which case you must decide WHICH pprice to select. You can select the max, or min, or average pprice (or the sum for that matter), from all those that have pid = 103 and the max value in the invid column. Of course, if the max(invid) is unique (as it is in your problem), then all these functions will return just the pprice associated to that invid; but you must still use one of these aggregate functions, since that uniqueness is not known at parsing time, it is only known at execution time. min is conventionally used in this manner. So:
select min(pprice) keep (dense_rank last order by invid) as pprice
from ........
where pid = 103
;

Row returned by a query with ROWNUM and PRIMARY KEY column

I'm reading about 1 of the oracle pseudocolumns i.e
ROWNUM: which return number indicating the order in which oracle return the row.
I have encountered some behavior here,
Example used
1. Create Script:
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
2 Insert Script:
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (2, 'Max', 22, 'India', 4500.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (1, 'Maths', 22, 'US', 4500.00 );
select * from CUSTOMERS; executed in the same order as inserted above,
ID NAME AGE ADDRESS SALARY
------ ---- --- ------- ------
2 Max 22 India 4500.00
1 Math 22 US 4500.00
Here if I run the select query,
select
rownum,
customers.ID
from customers;
I get below output:
ROWNUW ID
------ --
1 1
2 2
Here ID = 2 is inserted first but oracle returns in the 2nd row,
But if you include any other column from a table with ROWNUM and PK like
select
rownum,
customers.ID,customers.Name
from customers;
I get correct output (Correct inserted order) :
ROWNUW ID NAME
------ -- ----
1 2 Max
2 1 Math
If run query without Name column i.e only ROWNUW (pseudocolumns) and PK (table column)
We get this,
ROWNUW ID
------ --
1 1
2 2
My Question is Why is that ID=2 is not first returned row.
if I query any table column with ROWNUM, I get the result back based on insertion order.
Example below
select
rownum,
(NAME / AGE / ADDRESS SALARY) any one of these columns
From CUSTOMERS
But if use ROWNUM with ID (Primary Key column) insertion order is not working. why is this behavior with the only Primary Key column?
ROWNUM values depends on how oracle access the resultset. Once resultset is fetched, rownum is assigned to the rows.
As there is no guarantee in what order the data is returned, there is no guarantee in what order rownum is assigned.
Maybe if you try running both of your queries without rownum, you might get the rows swapped as you showed, or they might not.
Rownum will always be in order of 1,2,3,4 .. no matter what you are getting in result set. It is always calculated after result set is returned from query.
Now, you have ID column as PRIMARY KEY which makes it eligible for default clustered index creation in database. So when you select only PRIMARY column, it will be sorted as it was stored in DB that way. Index created as a result of PRIMARY key creation is sorted in ASC which is default.
Here if you select only PRIMARY KEY column i.e. "ID", Database is returning it in sorted order and ROWNUM is writing 1 & 2 to it.
But if you are selecting some extra non-PRIMARY KEY as well then it is totally upto oracle, whichever way it finds data faster. Sometimes it will return data from cache memory. If not it will read disk and fetch data. While reading datafile, there are multiple things which will differ machine to machine i.e is your table partitioned? is your table sharing tablespace ? what organization you are using in table ? blah blah....
Bottom line is, don't trust ROWNUM. It gives 1,2,3.. after you have applied your brain to the query.

How to delete row with past date and add this row to an other table?

Please help. I have 2 tables. First one is called Reservation and second one is called LastOrders.
There are the columns DATE_FROM in Date type and DATE:)_TO in date type too. And I want if the date in table Reservation in column DATE_TO = SYSDATE then delete this row and insert new row to table LastOrders I think a trigger can do that, but I dont know how. Any idea? Thank you very much and have a nice Christmas :)
TABLE RESERVATION
Name Null Type
-------------------------- -------- -------------
ID NOT NULL VARCHAR2(25)
DESCRIPTION NOT NULL VARCHAR2(100)
DATE_FROM NOT NULL DATE
DATE_TO NOT NULL DATE
TABLE LASTORDERS
Name Null Type
-------------------------- -------- -------------
ID NOT NULL VARCHAR2(25)
DESCRIPTION NOT NULL VARCHAR2(100)
DATE_FROM NOT NULL DATE
DATE_TO NOT NULL DATE
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE RESERVATION (
ID VARCHAR2(25) CONSTRAINT RESERVATION__ID__NN NOT NULL,
DESCRIPTION VARCHAR2(100) CONSTRAINT RESERVATION__DE__NN NOT NULL,
DATE_FROM DATE CONSTRAINT RESERVATION__DF__NN NOT NULL,
DATE_TO DATE CONSTRAINT RESERVATION__DT__NN NOT NULL
)
/
CREATE TABLE LASTORDERS (
ID VARCHAR2(25) CONSTRAINT LASTORDERS__ID__NN NOT NULL,
DESCRIPTION VARCHAR2(100) CONSTRAINT LASTORDERS__DE__NN NOT NULL,
DATE_FROM DATE CONSTRAINT LASTORDERS__DF__NN NOT NULL,
DATE_TO DATE CONSTRAINT LASTORDERS__DT__NN NOT NULL
)
/
CREATE PROCEDURE fulfilReservation(
I_ID IN RESERVATION.ID%TYPE,
O_SUCCESS OUT NUMBER
)
AS
r_reservation RESERVATION%ROWTYPE;
BEGIN
DELETE FROM RESERVATION
WHERE ID = I_ID
AND SYSDATE BETWEEN DATE_FROM AND DATE_TO
RETURNING ID, DESCRIPTION, DATE_FROM, DATE_TO INTO r_reservation;
INSERT INTO lastorders VALUES r_reservation;
o_success := 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
o_success := 0;
END;
/
INSERT INTO RESERVATION VALUES ( 1, 'Test', SYSDATE - 1, SYSDATE + 1 )
/
DECLARE
success NUMBER(1,0);
BEGIN
fulfilReservation( 1, success );
END;
/
Query 1:
SELECT * FROM RESERVATION
Results:
No Results
Query 2:
SELECT * FROM LASTORDERS
Results:
| ID | DESCRIPTION | DATE_FROM | DATE_TO |
|----|-------------|----------------------------|----------------------------|
| 1 | Test | December, 24 2015 18:59:07 | December, 26 2015 18:59:07 |
Trigger can do such job but it will fire only on some changes on table data. If you need to move data between tables in real-time you have to use jobs.
begin
dbms_scheduler.create_job(
job_name => 'my_job',
job_type => 'PLSQL_BLOCK',
enabled => true,
repeat_interval => 'FREQ=DAILY; INTERVAL=1',
start_date => round(sysdate,'DD'),
job_action => 'begin
insert into LastOrders
select *
from Reservation
where DATE_TO = round(sysdate,''DD'');
delete Reservation where DATE_TO = round(sysdate,''DD'');
commit;
end;');
end;
This job will move data from Reservation to LastOrders every day at 00:00.
Ooo, tricky one!
The main question is how "realtime" you need this to be. You need to consider that data changes on both sides of the equation - meaning, there is new data that enters the table and should be compared to SYSDATE (that's the easy part). The other side is SYSDATE, changing every second. So if you need your data to be as accurate as possible you basically need to check the entire table every second to see if any of the DATE_DO has "arrived" to SYSDATE. This is where the job #Artem suggested comes in handy.
BUT
Running the job once a day means that rows would be moved only once a day, and running it every second would mean querying the table every second (potential performance overhead).
SO, you need to be smart about it -
Add an index on DATE_TO
Make sure to use bind variables in repeating
queries you're making on the table. i.e, change the query in the
job.
Build the job in a smart way - Let's say you run it every 5
min (because you don't want your potentially heavy query to run
every sec), and you check for the lines that will reach SYSDATE in
the next 5 min. You can then move to a temporary table that holds
only these lines, and have another job run on that every second.
Sounds a bit complex, but would save you lot's of trouble if this data is planned to scale.
Good luck, Let me know if you have any questions.

pull pairs from table with multiple requirements

I have a database of multiple employees and how many hours they work. Please see below.
So, every Time.Type that says Wellsite needs two rows. One for the Attribute: Reg Work Day and the next row showing the Attribute: Job ID. My code is showing all rows that have Job ID which is what I need to get rid of unless they have the Reg Work Day above it that meets my condition (see below). Also, I have a row of On Office Time Type that is fine and I'm not having any issues with.
I've been assisted to do this, but for one employee. Now I need my data to work through this process for multiple employees.
The condition I need to be true is when a row has a Time.Type of "Wellsite/Job/Vessel" and an Attribute that says "Regular Work Day" the "Hourly Value" needs to be >15. Also, I need the row right below it which has an Attribute of "Job ID" with a job number under the "value" tab too. From what I have learned is that the Date column can be used to keep the rows together, but i just need it to work for the certain employees/days rather than everything.
The table below shows what I have and what I want it to do is below this table..
CREATE TABLE mytable(
Complete_Name VARCHAR(9) NOT NULL PRIMARY KEY
,Day_of_Week VARCHAR(7) NOT NULL
,Date VARCHAR(11) NOT NULL
,Time_Type VARCHAR(19) NOT NULL
,Attribute VARCHAR(16) NOT NULL
,Value VARCHAR(28) NOT NULL
,Hourly_value_in_decimals NUMERIC(5,2)
);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeA','Tuesday','20-Oct-2015','Wellsite/Job/Vessel','Regular Work Day','RGWD - Regular Work Day (BR)',16.75);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeA','Tuesday','20-Oct-2015','Wellsite/Job/Vessel','Job ID','2213840',NULL);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeB','Tuesday','20-Oct-15','Wellsite/Job/Vessel','Regular Work Day','RGWD - Regular Work Day (BR)',2.25);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeB','Tuesday','20-Oct-15','Wellsite/Job/Vessel','Job ID','2213840',NULL);
Table:
Complete_Name Day_of_Week Date Time_Type Attribute Value Hourly_value_in_decimals
------------- ----------- ----------- ------------------- ---------------- ---------------------------- ---------------------------------------
EmployeeA Tuesday 20-Oct-2015 Wellsite/Job/Vessel Regular Work Day RGWD - Regular Work Day (BR) 16.75
EmployeeA Tuesday 20-Oct-2015 Wellsite/Job/Vessel Job ID 2213840 NULL
EmployeeB Tuesday 20-Oct-15 Wellsite/Job/Vessel Regular Work Day RGWD - Regular Work Day (BR) 2.25
EmployeeB Tuesday 20-Oct-15 Wellsite/Job/Vessel Job ID 2213840 NULL
What I want to see is...
CREATE TABLE mytable(
Complete_Name VARCHAR(9) NOT NULL PRIMARY KEY
,Day_of_Week VARCHAR(7) NOT NULL
,Date VARCHAR(11) NOT NULL
,Time_Type VARCHAR(19) NOT NULL
,Attribute VARCHAR(16) NOT NULL
,Value VARCHAR(28) NOT NULL
,Hourly_value_in_decimals NUMERIC(5,2)
);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeA','Tuesday','20-Oct-2015','Wellsite/Job/Vessel','Regular Work Day','RGWD - Regular Work Day (BR)',16.75);
INSERT INTO mytable(Complete_Name,Day_of_Week,Date,Time_Type,Attribute,Value,Hourly_value_in_decimals) VALUES ('EmployeeA','Tuesday','20-Oct-2015','Wellsite/Job/Vessel','Job ID','2213840',NULL);
Table:
Complete_Name Day_of_Week Date Time_Type Attribute Value Hourly_value_in_decimals
------------- ----------- ----------- ------------------- ---------------- ---------------------------- ---------------------------------------
EmployeeA Tuesday 20-Oct-2015 Wellsite/Job/Vessel Regular Work Day RGWD - Regular Work Day (BR) 16.75
EmployeeA Tuesday 20-Oct-2015 Wellsite/Job/Vessel Job ID 2213840 NULL
So building on my prior answer you need to pull for different employees based on different criteria.
For example employee name Hogan might want days > 15 while employee named NotHogan might want days > 24.
To do this you put the criteria in a table (in the example below I build the table dynamically) and then join to that table to get the dates and names required.
Then I join to this result to get the final data.
SELECT *
FROM ROSTER
JOIN (
-- A list of dates and users we want to select
SELECT `Date`, Name
FROM ROSTER
JOIN (
-- Innermost table, our criteria, this can be an actual table in your db
SELECT 'Hogan' as N, 15 as C
UNION ALL
SELECT 'NotHogan' as N, 24 as C
) Criteria ON
Criteria.N = ROSTER.Name AND
ROSTER.`Hourly Value` > Criteria.C AND
Attribute = 'Reg Work Day'
) sub ON ROSTER.`Date` = sub.`Date` and ROSTER.Name = Sub.Name

how to fix the Violation of PRIMARY KEY constraint?

I have a few tables, I wrote a sql to join those tables and insert the result to a final table. The final table is tb(account, date), account is a primary key. The problem came from the result of the query, which would look like following:
account date
0001 09/01/2012
0001 09/02/2012
0002 09/01/2012
0003 09/03/2012
when I insert above those result records, I got Violation of PRIMARY KEY constraint error. How do I fix the query, I could get the result records like following
account date
0001 09/02/2012 (get the latest date)
0002 09/01/2012
0003 09/03/2012
Thanks.
How about
INSERT INTO SomeOtherTable (account, DATE)
SELECT account,
MAX(date)
FROM YourTable
GROUP BY account
It would make a lot more sense if you were to show us the table schemas, original query, and soem example data.
You could add DISTINCT or GROUP BY to the query to prevent duplicates. If the final table already has some data prior to this population, you may also want to add a correlated NOT EXISTS. For example:
INSERT dbo.FinalTable(account, [date])
SELECT account, [date] FROM
(
SELECT account, MAX([date])
FROM --... your join here
GROUP BY account
) AS src (account, [date])
WHERE NOT EXISTS
(
SELECT 1
FROM dbo.FinalTable
WHERE account = src.account
AND [date] = src.[date]
);
Also consider using a more meaningful (and non-reserved) word for your date column(s).
edit sorry, missed the (get the latest date) bit.
I think You need to ORDER BY date DESC GROUP BY account when you insert. However this alone will not work if you already have 0001 in table.
You'll need to insert non-existing records and then update existing ones (if that's what you want)