(Yet another) "Missing Right Parenthesis" - oracle

I am using 10g, and am trying to do some simple calculation and then save the result in a column. The actual tables have many more columns, but here are the what I am using in my query:
CREATE TABLE "VACCINE_LOT"
(
"VACCINE_LOT_ID" NUMBER(10,0) NOT NULL ENABLE,
"DOSE" NUMBER(6,3),
"QUANTITY_ON_HAND" NUMBER(12,2) NOT NULL ENABLE
)
CREATE TABLE "IMMUNIZATION"
(
"VACCINE_LOT_ID" NUMBER(10,0),
"DOSE_MAGNITUDE" NUMBER(4,2)
)
CREATE TABLE "VACCINE_LOT_TRANSACTION"
(
"VACCINE_LOT_ID" NUMBER(10,0) NOT NULL ENABLE,
"QUANTITY" NUMBER(12,2) NOT NULL ENABLE
)
INSERT INTO vaccine_lot VALUES (100, 0.2, 120);
INSERT INTO immunization VALUES (100, 0.2);
INSERT INTO immunization VALUES (100, 0.3);
INSERT INTO vaccine_lot_transaction VALUES (100, 150);
Immunization shots are taken from a vaccine lot. 'Dose_magnitude' is how much a particular immunization shot uses from a lot. The 'Dose' column in vaccine_lot tells how much to use for a standard immunization shot. So a standard shot may be 0.1cc. But one immunization shot may actually use 0.2cc or even 0.05cc. The 'Quantity' column in vaccine_lot_transaction records originally how many standard immunization shots a vaccine lot contains.
What I am trying to do here is to calculate the correct 'Quantity_on_hand' for vaccine lots (that is, how many standard immunization shots are still left for vaccine lots).
Here is an example using the data we just inserted. We have a vaccine lot (lot ID is '100'), and it starts off with 150 standard shots (that is, it contains 150 0.2cc shots). There are two immunization shots from this lot already, one 0.2cc, the other 0.3cc). And the current quantity of 120 is obviously wrong, and we need to recalculate and update it.
Here is my query:
UPDATE vaccine_lot V SET quantity_on_hand =
(
(
(SELECT T.quantity * V.dose FROM vaccine_lot_transaction T WHERE V.vaccine_lot_id = T.vaccine_lot_id) -
(SELECT SUM(I.dose_magnitude) FROM immunization I WHERE I.vaccine_lot_id = V.vaccine_lot_id)
) / dose
);
And sure enough, Oracle starts to complain about "missing right parenthesis". Looks like it thinks there is something syntactically wrong.
Can anyone help take a look at this query and see what's wrong with it? Thanks!
This is what I get when I run it through SQL*PLUS:
SQL> run
1 UPDATE vaccine_lot V SET quantity_on_hand =
2 (
3 (
4 (SELECT T.quantity * V.dose FROM vaccine_lot_transaction T
5 WHERE V.vaccine_lot_id = T.vaccine_lot_id) -
6 (SELECT SUM(I.dose_magnitude) FROM immunization I
7 WHERE I.vaccine_lot_id = V.vaccine_lot_id)
8 ) / dose
9* );
WHERE V.vaccine_lot_id = T.vaccine_lot_id) -
*
ERROR at line 5:
ORA-00907: missing right parenthesis
By the way, I am using version 10.2.0.1.0 of SQL*Plus. I get the same result when using SQL Developer (version 3.0.04).
Can anyone help take a look at this issue? Thanks!

I cut and pasted your code and it seems to work for me (I believe the end result of 147.5 is correct). Are you sure that you didn't accidentally simplify the problem too far?
SQL> CREATE TABLE "VACCINE_LOT"
2 (
3 "VACCINE_LOT_ID" NUMBER(10,0) NOT NULL ENABLE,
4 "DOSE" NUMBER(6,3),
5 "QUANTITY_ON_HAND" NUMBER(12,2) NOT NULL ENABLE
6 );
Table created.
SQL> CREATE TABLE "IMMUNIZATION"
2 (
3 "VACCINE_LOT_ID" NUMBER(10,0),
4 "DOSE_MAGNITUDE" NUMBER(4,2)
5 );
Table created.
SQL> CREATE TABLE "VACCINE_LOT_TRANSACTION"
2 (
3 "VACCINE_LOT_ID" NUMBER(10,0) NOT NULL ENABLE,
4 "QUANTITY" NUMBER(12,2) NOT NULL ENABLE
5 );
Table created.
SQL> INSERT INTO vaccine_lot VALUES (100, 0.2, 120);
1 row created.
SQL> INSERT INTO immunization VALUES (100, 0.2);
1 row created.
SQL> INSERT INTO immunization VALUES (100, 0.3);
1 row created.
SQL> INSERT INTO vaccine_lot_transaction VALUES (100, 150);
1 row created.
SQL> commit;
Commit complete.
SQL> UPDATE vaccine_lot V SET quantity_on_hand =
2 (
3 (
4 (SELECT T.quantity * V.dose FROM vaccine_lot_transaction T WHERE V.vacci
ne_lot_id = T.vaccine_lot_id) -
5 (SELECT SUM(I.dose_magnitude) FROM immunization I WHERE I.vaccine_lot_id
= V.vaccine_lot_id)
6 ) / dose
7 );
1 row updated.
SQL> select * from vaccine_lot;
VACCINE_LOT_ID DOSE QUANTITY_ON_HAND
-------------- ---------- ----------------
100 .2 147.5

Related

Oracle convert table to interval day(3) to second

I have the following table definition, with data that creates fine but I like to convert it to a more generic format but I'm having issues. Can someone point me in the right direction
CREATE TABLE partition_retention
(
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
TABLE_NAME VARCHAR2(30),
DAYS NUMBER(6),
CONSTRAINT
partition_retention_pk primary key (table_name));
/
INSERT into partition_retention(TABLE_NAME, DAYS)
WITH data as (
select 'T1', 0
from dual union all
select 'T3', 15
from dual union all
select 'T4', 10
from dual union all
select 'T5', 5
from dual)
SELECT * from data;
/
-- having problem creating
CREATE TABLE PARTITION_RETENTION AS (
TABLE_NAME AS VARCHAR2(30)
RETENTION DAY AS INTERVAL DAY(3) TO SECOND(0)
);
The three AS keywords are invalid (create table .. as select .. is valid, but you aren't doing that); you are missing a comma; and you have an unquoted column name with a space.
Correcting those things, this works:
CREATE TABLE PARTITION_RETENTION (
TABLE_NAME VARCHAR2(30),
RETENTION_DAY INTERVAL DAY(3) TO SECOND(0)
);
Your inserts will then have to insert interval values, not simple numbers, obviously.
db<>fiddle
how can I add a CONSTRAINT on the table to ensure the day>0 and the time is always 0
You can add separate constraints to check both things:
CREATE TABLE PARTITION_RETENTION (
TABLE_NAME VARCHAR2(30),
RETENTION_DAY INTERVAL DAY(3) TO SECOND(0),
CONSTRAINT CHK_NON_ZERO_DAYS CHECK (
RETENTION_DAY > INTERVAL '0' DAY
),
CONSTRAINT CHK_WHOLE_DAYS CHECK (
EXTRACT(HOUR FROM RETENTION_DAY) = 0
AND EXTRACT(MINUTE FROM RETENTION_DAY) = 0
AND EXTRACT(SECOND FROM RETENTION_DAY) = 0
)
);
to give slightly different errors (via the constraint names) - db<>fiddle - or combine them into one.
I'm not sure this is really any clearer or easier than having a number column constrained to integers between 1 and 999.

Oracle unique constraint or index

If I had the following table in an Oracle database:
CREATE TABLE TESTTABLE
(
"MYUNIQUEIDCOL" NUMBER(9,0) NOT NULL,
"VAN" NUMBER(9,0) NOT NULL,
"STATUS" VARCHAR2(30 BYTE) NOT NULL
)
I would like a van (of which there could be many) to only ever have one row with a status of "Active" - what kind of constraint would I need to ensure the illustration below does not happen (ie. rows 2 & 5 should not have been allowed)
MYUNIQUEIDCOL | VAN | STATUS
1 100 Active
2 100 Active
3 100 Cancelled
4 200 Active
5 200 Active
Thank you.
You can create unique index on mentioned column with the condition as following:
CREATE UNIQUE INDEX UQ_VAN_STATUS
ON TESTTABLE (VAN, CASE WHEN STATUS = 'ACTIVE' THEN STATUS END);
Example:
SQL> INSERT INTO TESTTABLE VALUES (1, 100, 'ACTIVE');
1 row created.
SQL> INSERT INTO TESTTABLE VALUES (1, 100, 'INACTIVE');
1 row created.
SQL> INSERT INTO TESTTABLE VALUES (1, 100, 'ACTIVE');
INSERT INTO TESTTABLE VALUES (1, 100, 'ACTIVE')
*
ERROR at line 1:
ORA-00001: unique constraint (TEJASH.UQ_VAN_STATUS) violated
SQL>
Cheers!!

insert all and inner join in oracle

I would like to insert data in to two tables. Will be one-to-many connection. For this, I have to use Foreign Key, of course.
I think, table1 - ID column is an ideal for this a Primary Key. But I generate it always with a trigger, automatically, every line. SO,
How can I put Table1.ID (auto generated, Primary Key) column in to table2.Fkey column in the same insert query?
INSERT ALL INTO table1 ( --here (before this) generated the table1.id column automatically with a trigger.
table1.food,
table1.drink,
table1.shoe
) VALUES (
'apple',
'water',
'slippers'
)
INTO table2 (
fkey,
color
) VALUES (
table1.id, -- I would like table2.fkey == table1.id this gave me error
'blue'
) SELECT
*
FROM
table1
INNER JOIN table2 ON table1.id = table2.fkey;
The error message:
"00904. 00000 - "%s: invalid identifier""
As suggested by #OldProgrammer, use sequence
INSERT ALL INTO table1 ( --here (before this) generated the table1.id column automatically with a trigger.
table1_id,
table1.food,
table1.drink,
table1.shoe
) VALUES (
<sequecename_table1>.nextval,
'apple',
'water',
'slippers'
)
INTO table2 (
fkey,
color
) VALUES (
<sequecename_table2>.nextval,
<sequecename_table1>.currval, -- returns the current value of a sequence.
'blue'
) SELECT
*
FROM
table1
INNER JOIN table2 ON table1.id = table2.fkey;
Since you're using Oracle DB's 12c version, then might use Identity Column Property. Then easily return the value of first table's (table1) to a local variable by charging of returning clause just after an insert statement for table1, and use inside the next insert statement which is for table2 as stated below :
SQL> create table table1(
2 ID integer generated always as identity primary key,
3 food varchar2(50), drink varchar2(50), shoe varchar2(50)
4 );
SQL> create table table2(
2 fkey integer references table1(ID),
3 color varchar2(50)
4 );
SQL> declare
2 cl_tab table1.id%type;
3 begin
4 insert into table1(food,drink,shoe) values('apple','water','slippers' )
5 returning id into cl_tab;
6 insert into table2 values(cl_tab,'blue');
7 end;
8 /
SQL> select * from table1;
ID FOOD DRINK SHOE
-- ------- ------- -------
1 apple water slippers
SQL> select * from table2;
FKEY COLOR
---- --------------------------------------------------
1 blue
Anytime you issue the above statement for insertions between begin and end, both table1.ID and table2.fkey columns will be populated by the same integer values. By the way do not forget to commit the changes by insertions, if you need these values throughout the DB(i.e.from other sessions also).

How to select partition name of specific data?

I want to select a data and wanna see in which partition.
partition column is : code (varchar column)
Select .... -- I want to find partition name
from
partition_table
where to_number(code) = 55;
why I need to this:
I have a data which code is '55' but in that table when I use partition column I do not select it. But there is data which value is '55'
So I want to that data in which partition.
And the data is not in PDEFAULT partition. I ve already check it.
edit
data is in another partition. I think there is a problem with exchange partition process
thanks in advance
There are a couple of ways.
1) The rowid will point to the partition object
SQL> create table t ( x int, y int )
2 partition by range (x )
3 ( partition p1 values less than ( 100 ),
4 partition p2 values less than ( 200 )
5 );
Table created.
SQL>
SQL> insert into t values (34,34);
1 row created.
SQL>
SQL> select rowid from t;
ROWID
------------------
AAA0cqAAHAAAAQ6AAA
1 row selected.
SQL>
SQL> select dbms_rowid.rowid_object(rowid) from t;
DBMS_ROWID.ROWID_OBJECT(ROWID)
------------------------------
214826
1 row selected.
SQL>
SQL> select subobject_name
2 from user_objects
3 where data_Object_id =
4 ( select dbms_rowid.rowid_object(rowid) from t );
SUBOBJECT_NAME
------------------------------------------------------------
P1
2) You can data mine the dictionary to probe the HIGH_VALUE column in USER_TAB_PARTITIONS. I did a video on how to do that here
https://www.youtube.com/watch?v=yKHQQXKdfOM

DDL date data type in check constraint manipulation in oracle

i'm creating a table that should check various date constraint, but oracle is kind ticky on this argument. My goal is to reserve a certain room for let's say a lecture, so i tought of something like this :
....
prenotation_date date NOT NULL,
starting_date date NOT NULL,
ending_date date NOT NULL,
check (/strarting_date > prenotation_date+3gg/ ) -> you can book a room only 3 days after the time you're asking the reservation
check (/* starting_date = ending_date */ ) in regards of the date you're booking, meaning you can book a room for a period of one day
check (/* starting_date < ending_date */) in regards of the time you're considering, meaning that the time in witch you start to use the room must be earlier of the time you intend of leaving the room (let's say you want to reserve it from 8:00 a.m. to 10:00 a.m.
the problem is that i don't know how consider just a portion of the data, but i guess there's a way to accomplish that, otherwise you should create 4 columns instead of 2 and use for a couple a dummy date and for the other a dummmy time, wich seems cluncky to me. So i guess the question is, how do i consider the date type in its single components ? Is that even possible ? thanks for the attention and sorry for the long post.
You could use a CHECK constraint with following conditions:
starting_date > prenotation_date + 3
starting_date < ending_date
TRUNC(starting_date) = TRUNC(ending_date)
starting_date > prenotation_date + 3 will make sure that the booking is allowed after 3 days of the reservation.
starting_date < ending_date will make sure that the time at which a booking is made is always before to the leaving time.
TRUNC(starting_date) = TRUNC(ending_date) will make sure that the booking is done for the same day. That is the booking window is only for one single day.
Test case:
CREATE TABLE
SQL> CREATE TABLE t(
2 prenotation_date DATE NOT NULL, starting_date DATE NOT NULL, ending_date DATE NOT NULL);
Table created.
SQL>
ADD CHECK CONSTRAINT
SQL> ALTER TABLE t ADD CONSTRAINT t_chk CHECK(
2 (starting_date > prenotation_date + 3)
3 AND (starting_date < ending_date)
4 AND (TRUNC(starting_date) = TRUNC(ending_date))
5 );
Table altered.
SQL>
INSERT : check starting_date > prenotation_date + 3. Below insert should fail.
SQL> INSERT
2 INTO t VALUES
3 (
4 to_date('03/01/2015 00:00:00','mm/dd/yyyy hh24:mi:ss'),
5 to_date('03/02/2015 08:00:00','mm/dd/yyyy hh24:mi:ss'),
6 to_date('03/02/2015 10:00:00','mm/dd/yyyy hh24:mi:ss')
7 );
INSERT
*
ERROR at line 1:
ORA-02290: check constraint (LALIT.T_CHK) violated
SQL>
INSERT : check starting_date < ending_date. Below insert should fail.
SQL> INSERT
2 INTO t VALUES
3 (
4 to_date('02/01/2015 00:00:00','mm/dd/yyyy hh24:mi:ss'),
5 to_date('03/02/2015 10:00:00','mm/dd/yyyy hh24:mi:ss'),
6 to_date('03/02/2015 08:00:00','mm/dd/yyyy hh24:mi:ss')
7 );
INSERT
*
ERROR at line 1:
ORA-02290: check constraint (LALIT.T_CHK) violated
SQL>
INSERT : check booking is done for the same day. Below insert should fail. Note the time 08:00:00 and 10:00:00 for start and end time respectively.
SQL> INSERT
2 INTO t VALUES
3 (
4 to_date('02/01/2015 00:00:00','mm/dd/yyyy hh24:mi:ss'),
5 to_date('03/02/2015 08:00:00','mm/dd/yyyy hh24:mi:ss'),
6 to_date('04/02/2015 10:00:00','mm/dd/yyyy hh24:mi:ss')
7 );
INSERT
*
ERROR at line 1:
ORA-02290: check constraint (LALIT.T_CHK) violated
SQL>
INSERT : All values satisfy the requirement, below insert should pass.
SQL> INSERT
2 INTO t VALUES
3 (
4 to_date('02/01/2015 00:00:00','mm/dd/yyyy hh24:mi:ss'),
5 to_date('03/02/2015 08:00:00','mm/dd/yyyy hh24:mi:ss'),
6 to_date('03/02/2015 10:00:00','mm/dd/yyyy hh24:mi:ss')
7 );
1 row created.
SQL>
Generally you should just to learn how to work with dates in Oracle and SQL :) There are several ways to do this. For example:
-- Check whether room was booked timely
alter table Reservations add constraint RC1
check (trunc(starting_date, 'DD') >= trunc(prenotation_date, 'DD') + 3);
-- Disable booking ranges around midnight
alter table Reservations add constraint RC2
check (trunc(starting_date, 'DD') = trunc(ending_date, 'DD'));
-- Check whether booked range exists
alter table Reservations add constraint RC3
check (starting_date < ending_date);

Resources