Oracle: Composite unique key with Date column - oracle11g

I have created a table with composite unique key as below--
create table test11
(
aa number,
bb varchar2(10),
cc DATE,
dd number,
ee NUMBER
);
CREATE UNIQUE INDEX TEST11_IDX ON TEST11 (AA,BB,CC);
Now, whenever I try to insert data, I get this error:
ORA-00001: unique constraint (CDUREFDB.TEST11_IDX) violated
INSERT INTO TEST11 VALUES (1, 'AA', SYSDATE, 1, 1);
commit;
INSERT INTO TEST11 VALUES (1, 'AA', SYSDATE, 1, 1);
commit;
Is that because of DATE column is considering Date value till seconds?
Because I could see below query is returning is result--
select to_char(CC,'DD-Mon-YY HH:Mi:SS AM') from test11;
TO_CHAR(CC,'DD-MON-YYHH:MI:SSAM')
---------------------------------
17-Mar-16 04:28:37 PM
17-Mar-16 04:28:43 PM
So, what can be done in order to only consider Date value (not hours, mins, secs precision) as unique key member.
Also, DATE column above(CC) has partition on it.
UPDATE::
In this table, we have RANGE partition on DATE column(CC).
And we are planning to remove partitions periodically (i.e. after some days interval).
So if I Don't use direct CC in unique index ( instead of making trunc as Justin suggested) then i am getting error as ORA-01502: index 'CDUREFDB.TEST111_IDX' or partition of such index is in unusable state if I try to insert data after some old partition got removed .
UPDATE_1
As per #Justin suggestion below, this issue is resolved creating virtual column like below:
CREATE TABLE TEST11
(
AA NUMBER,
BB VARCHAR2(10),
CC DATE,
DD NUMBER ,
EE NUMBER,
FF DATE generated always AS (TRUNC(CC)) virtual
)
PARTITION BY RANGE
(
FF
)
INTERVAL
(
NUMTODSINTERVAL(1,'DAY')
)
(
PARTITION partition_test_1 VALUES LESS THAN (TO_DATE('01-APR-2006','dd-MON-yyyy'))
);
CREATE UNIQUE INDEX TEST111_IDX ON TEST11 (AA,BB,FF) LOCAL; -- creating unique local index

A date always has a time component so your two rows have different cc values. You could create a function-based index based on the trunc(cc) value which will set the time component to midnight.
CREATE UNIQUE INDEX TEST11_IDX
ON TEST11 (AA,BB,trunc(CC));
Of course, that means that if you want a query to use the index, you'd want to ensure that your predicate is on trunc(cc) rather than cc.

Related

Count returns null with Insert Into, but it works when I just use select in Netezza

I'm trying to insert data into a table and one of my columns is coming back null. The columns I sum are working fine, but the one I count is returning null with the insert into command followed by select. When I eliminate insert into, and just run my select statement I get the result I need. What am I doing wrong?
`
INSERT INTO DAILY_TOTALS(TIME_FRAME,
VIDEO,
DATA,
VOICE,
PREV_VIDEO,
PREV_DATA,
PREV_VOICE,
VIDEO_REVENUE,
INTERNET_REVENUE,
PHONE_REVENUE,
OCC_REVENUE,
TOTAL_REVENUE,
ACCOUNTS)
SELECT
D.TIME_FRAME,
D.VIDEO,
D.DATA,
CASE WHEN D.VOICE ='N' THEN 'N' ELSE 'Y' END AS VOICE,
D.PREV_VIDEO,
D.PREV_DATA,
CASE WHEN D.PREV_VOICE = 'N' THEN 'N' ELSE 'Y' END AS PREV_VOICE,
SUM(D.VIDEO_REVENUE) AS VIDEO_REVENUE,
SUM(D.INTERNET_REVENUE) AS INTERNET_REVENUE,
SUM(D.OCC_REVENUE) AS OCC_REVENUE,
SUM(D.TOTAL_REVENUE) AS TOTAL_REVENUE,
COUNT(D.ACCOUNT_NUMBER) AS ACCOUNTS
FROM DAILY D
WHERE D.TIME_FRAME = CURRENT_DATE -1
GROUP BY
D.TIME_FRAME,
D.VIDEO,
D.DATA,
D.VOICE,
D.PREV_VIDEO,
D.PREV_DATA,
D.PREV_VOICE`
Aren't you missing 1 column?
The INSERT statement mentions 13 columns ...but the SELECT only selects 12 columns.
So "COUNT(D.ACCOUNT_NUMBER) AS ACCOUNTS" is being inserted into the TOTAL_REVENUE column. And the ACCOUNTS column in your table is NULL because you haven't specified anything to go in there.

SQLite order results by smallest difference

In many ways this question follows on from my previous one. I have a table that is pretty much identical
CREATE TABLE IF NOT EXISTS test
(
id INTEGER PRIMARY KEY,
a INTEGER NOT NULL,
b INTEGER NOT NULL,
c INTEGER NOT NULL,
d INTEGER NOT NULL,
weather INTEGER NOT NULL);
in which I would typically have entries such as
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30100306);
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30140306);
INSERT INTO test (a,b,c,d) VALUES(1,2,5,5,10100306);
INSERT INTO test (a,b,c,d) VALUES(1,5,5,5,11100306);
INSERT INTO test (a,b,c,d) VALUES(5,5,5,5,21101306);
Typically this table would have multiple rows with the some/all of b, c and d values being identical but with different a and weather values. As per the answer to my other question I can certainly issue
WITH cte AS (SELECT *, DENSE_RANK() OVER (ORDER BY (b=2) + (c=3) + (d=4) DESC) rn FROM test where a = 1) SELECT * FROM cte WHERE rn < 3;
No issues thus far. However, I have one further requirement which arises as a result of the weather column. Although this value is an integer it is in fact a composite where each digit represents a "banded" weather condition. Take for example weather = 20100306. Here 2 represents the wind direction divided up into 45 degree bands on the compass, 0 represents a wind speed range, 1 indicates precipitation as snow etc. What I need to do now while obtaining my ordered results is to allow for weather differences. Take for example the first two rows
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30100306);
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30140306);
Though otherwise similar they represent rather different weather conditions - the fourth number is four as opposed to 0 indicating a higher precipitation intensity brand. The WITH cte... above would rank the first two rows at the top which is fine. But what if I would rather have the row that differs the least from an incoming "weather condition" of 30130306? I would clearly like to have the second row appearing at the top. Once again, I can live with the "raw" result returned by WITH cte... and then drill down to the right row based on my current "weather condition" in Java. However, once again I find myself thinking that there is perhaps a rather neat way of doing this in SQL that is outwith my skill set. I'd be most obliged to anyone who might be able to tell me how/whether this can be done using just SQL.
You can sort the results 1st by DENSE_RANK() and 2nd by the absolute difference of weather and the incoming "weather condition":
WITH cte AS (
SELECT *,
DENSE_RANK() OVER (ORDER BY (b=2) + (c=3) + (d=4) DESC) rn
FROM test
WHERE a = 1
)
SELECT a,b,c,d,weather
FROM cte
WHERE rn < 3
ORDER BY rn, ABS(weather - ?);
Replace ? with the value of that incoming "weather condition".

Optimize JOIN for a large dataset in SQLite

I need to create a table with the running total (aka cumulative sum) per client per day from a large dataset (200 GB). However, my current code is too slow: it has been running for days and still has not finished.
My data came from two tables. The table Orders informs the order id and client id. The table Transactions informs the order id, date and status (status=5 means order approved, status=7 means order rejected).
CREATE TABLE RunningTotal AS
SELECT DISTINCT Orders.ClientId,
Transactions.Date,
SUM(CASE WHEN Transactions.Status = 5 THEN 1 ELSE -1 END) OVER (PARTITION BY Orders.ClientId ORDER BY Transactions.Date) AS RunTotal
FROM Transactions
LEFT JOIN Orders ON Transactions.Order_Id= Orders.Id
WHERE Transactions.Status IN (5,7)
I used a toy table to test and my current code provides the expected results: the net running total of approved order per client per day. The problem seems to be how to optimize it to use with a large dataset. I might be wrong, but I believe my bottleneck is the JOIN. If I run a similar query with just one table, it takes minutes.
The command EXPLAIN QUERY PLAN gives​ me the following:
CO-ROUTINE 3
SEARCH TABLE Transactions USING INDEX idx_trans_status (Status=?)
SEARCH TABLE Orders USING INDEX idx_orders_id (Id=?)
USE TEMP B-TREE FOR ORDER BY
SCAN SUBQUERY 3
USE TEMP B-TREE FOR DISTINCT
USE TEMP B-TREE FOR ORDER BY
EDIT:
The output table would look like:
CREATE TABLE Transactions (
ClientId INTEGER,
Date DATE,
RunTotal INTEGER
PRIMARY KEY (ClientId, Date)
)
ClientId Date RunTotal
-------- ----------- --------
1 2018-12-28 110
1 2018-12-30 125
3 2018-10-15 87
3 2018-11-22 93
3 2018-11-24 99
'Orders' table looks like:
CREATE TABLE Orders (
Id INTEGER,
ClientId INTEGER
Class INTEGER,
Recall INTEGER,
Family INTEGER,
CreationDate DATE,
DeletionDate DATE,
Level INTEGER,
Notes TEXT,
Tags TEXT,
Operator_ID INTEGER,
Node INTEGER,
PRIMARY KEY (Id)
)
'Transactions' table looks like:
CREATE TABLE Transactions (
Id INTEGER,
Order_Id INTEGER,
Date DATE,
Status INTEGER,
Amount INTEGER,
Operator_ID INTEGER,
Notes TEXT,
PRIMARY KEY (Id)
)

How to cast a column into decimal of varying significant digits in Oracle

I have a column that is stored in ###0.0000000000 format. In a report I'm generating I need it to only show a few significant digits. Problem is the number needed changes based on the product with a default of 2. There's a column in another table that provides the required digits per each product.
I've tried a few things so far but it seems to not like it and throws a syntax error.
Cast(A.Price as Numeric(10,coalesce(B.Sig_Digits,2)))
That threw an error so I tried making the coalesce part a column and aliasing it in case the coalesce broke it, and that didn't work either. Round will take a column as an argument but I don't want it to round. Other than an ugly
case when Sig_digits = 1 then to_char(price,'###0.0') when Sig_digits = 2...
etc. what other options are there? This is a very large report, with 100+ columns and a few million rows so I'd prefer to not do the case when.
Use TO_CHAR with RPAD to add 0s to the end of the format model to the correct number of decimal places:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( value, sig ) AS
SELECT 123.456789, 2 FROM DUAL UNION ALL
SELECT 123456789.123456789, 7 FROM DUAL;
Query 1:
SELECT TO_CHAR( value, RPAD( 'FM999999999990.', sig + 15, '0' ) )
FROM table_name
Results:
| TO_CHAR(VALUE,RPAD('FM999999999990.',SIG+15,'0')) |
|---------------------------------------------------|
| 123.46 |
| 123456789.1234568 |

how to query by row number in oracle

Is there any way to query directly by row number in a table in Oracle? In other words, to achieve the same effect of ordinary lookup in an array in some basic language like C or Java. I've not yet tried virtual columns.
For instance, the following is an example of an efficient query, but it wastes disk space:
create table ary (row_position_id number(10) NOT NULL,
datum binary_float NOT NULL);
declare i pls_integer;
begin
for i in 0..10000000
loop
insert into ary values (i, dbms_random.normal());
end loop;
commit;
end;
create unique index ary_rp on ary(row_position_id);
now, i'm going to create a set of query values to store in another "parameter" table:
create table query_values (qval number(10) NOT NULL);
declare i pls_integer;
begin
for i in 0..10000
loop
insert into query_values (abs(dbms_random.random() % 10000000));
end loop;
commit;
end;
now, having these query values, i'm going to query the original table
select d.* from ary d where exists (select 0 from query_values v
where d.row_position_id = v.qval);
Now, this query would be fine -- it would use INDEX UNIQUE SCAN and TABLE access by ROWID. The problem I have is that the row_position_id takes up as much space in the table blocks as the actual data (the DATUM column).
I am aware of Index-organized tables and also Virtual Columns (which cannot be used with IOTs). And, of course, things like ROWNUM and ROW_NUMBER are irrelevant here (unless I'm misunderstanding something).
Also worth pointing out, this table is static data -- once loaded, it will never change. I would likely do an ALTER TABLE ARY READ ONLY;
What I would really like is:
create table ary (datum binary_float not null);
-- load rows in a specific order
-- efficiently query this table by implicit row position
Thanks very much!
Henry
I think you're going to want to keep the extra column. Here's why:
As you said, ROWNUM and ROW_NUMBER are not applicable here because they are generated as rows are returned in the query; they will not tell you anything about insert order.
What about ROWID? ROWID is just where a row is stored - again, from the docs:
The data object number of the object
The data block in the data file in which the row resides
The position of the row in the data block (first row is 0)
The data file in which the row resides (first file is 1). The file number is relative to the tablespace.
The "position in the data block" sounds interesting, but you would have no idea what order of the data blocks that were inserted (Oracle could use whatever datablocks it can quickly make use of) so this would not be a reliable option, and even so, you'd be having to parse ROWIDs which are not human readable (e.g. in 12g they look like this: *BAGAASMCwQL+ )
Another option is ORA_ROWSCN which is interesting in that it does give you some idea of order, in terms of the system change number. However, it doesn't come for free. Just to start, you have to create your table with the ROWDEPENDENCIES option and as per docs:
ROWDEPENDENCIES Specify ROWDEPENDENCIES if you want to enable
row-level dependency tracking. This setting is useful primarily to
allow for parallel propagation in replication environments. It
increases the size of each row by 6 bytes.
The other catch with this is that you would have to have to follow each row inserted with a commit so each row would get a different SCN.
If you're willing to go this far, you'll still have to convert the rows to have indexes (starting with, say, 0 or 1) that you can use to join to other tables.
Here's a quick sample of what it would involve:
DROP TABLE temp;
CREATE TABLE temp
( a number(10)
, b varchar2(10)
)
ROWDEPENDENCIES
;
-- one commit after all rows
INSERT INTO temp VALUES (1, 'A');
INSERT INTO temp VALUES (2, 'B');
INSERT INTO temp VALUES (3, 'C');
INSERT INTO temp VALUES (4, 'D');
INSERT INTO temp VALUES (5, 'E');
INSERT INTO temp VALUES (6, 'F');
COMMIT;
SELECT X.*, ROWNUM
FROM (SELECT T.*
, ORA_ROWSCN
FROM TEMP T
ORDER BY ORA_ROWSCN
) x
;
A B ORA_ROWSCN ROWNUM
1 A 2272340 1
2 B 2272340 2
6 F 2272340 3
4 D 2272340 4
5 E 2272340 5
3 C 2272340 6
Whoops. Those rows are definitely not in the order they came in.
Now using one commit per row:
TRUNCATE TABLE temp;
INSERT INTO temp VALUES (1, 'A');
COMMIT;
INSERT INTO TEMP VALUES (2, 'B');
COMMIT;
INSERT INTO temp VALUES (3, 'C');
COMMIT;
INSERT INTO temp VALUES (4, 'D');
COMMIT;
INSERT INTO temp VALUES (5, 'E');
COMMIT;
INSERT INTO temp VALUES (6, 'F');
COMMIT;
SELECT X.*, ROWNUM
FROM (SELECT T.*
, ORA_ROWSCN
FROM TEMP T
ORDER BY ORA_ROWSCN
) x
;
A B ORA_ROWSCN ROWNUM
1 A 2272697 1
2 B 2272699 2
3 C 2272701 3
4 D 2272703 4
5 E 2272705 5
6 F 2272707 6
Better. But if you've got a significant number of rows it's not going to go in fast. (I think this is what you would do if you intentionally wanted to slow down your inserts. ;) )
I think that's about as good as you'll get trying to get around using your own column, BUT there is still hope to economize storage: you can do away with the table + index and just go with an index-organized table. It's basically an index that you query directly.
It's just this easy:
CREATE TABLE TEMP2
( A NUMBER(10)
, B VARCHAR2(10)
, CONSTRAINT PK_CONSTRAINT PRIMARY KEY (A)
)
ORGANIZATION INDEX
;
There are other parameters you'll want to consider for this as well, but for more info check out... the docs.

Resources