Oracle Update takes long time while updating primary key column - sql

There is the table EFORMDYNAMICFIELDINSTANCE with primary key (Enterpriseid, Ownertype, Ownerid, Itemtype, Itemid).
In order to change a primary key to single column i.e EDFI_ID we want to update this EDFI_ID starting with 7000000 as increment value.
This table with hardly 50000 records takes 10 hrs to update.
This is my table defination:
ENTERPRISEID NOT NULL NUMBER(10),
OWNERTYPE NOT NULL VARCHAR2(60),
OWNERID NOT NULL NUMBER(10),
ITEMTYPE NOT NULL VARCHAR2(60),
ITEMID NOT NULL NUMBER(10),
EDFI_ID NOT NULL NUMBER(10),
FIELD1 VARCHAR2(2000),
FIELD2 VARCHAR2(2000),
...
FIELD199 VARCHAR2(2000),
FIELD200 VARCHAR2(2000)
Earlier we had (ENTERPRISEID, OWNERTYPE, OWNERID, ITEMTYPE, ITEMID) as the primary key.
Now EDFI_ID is my primary key column and we want to update this primary key with (rownumber + 7000000). This table has approximately 50000 records and EDFI_ID should update as 7000000, 7000001, 7000002....7050000.
Please suggest an UPDATE statement which will take less time. As of now my above UPDATE is taking 10 hours.

My guess is that your problem is the repeated execution of your subqueries. I'm not even sure the second one (inside the WHERE EXISTS) is doing anything useful.
Strip out the WHERE EXISTS - that will eliminate half the effort. If that doesn't help then...
create a temporary mapping table that holds all your existing primary key values, along with your new PK value. Create a unique index on your five PK columns. Then use the mapping table in the subquery
Just to elaborate on my second suggestion :
CREATE TABLE temp_pk_mapping AS
(SELECT
enterpriseid
,ownertype
,ownerid
,itemtype
,itemid
,rownum + 70000 new_pk
FROM
(SELECT DISTINCT
enterpriseid
,ownertype
,ownerid
,itemtype
,itemid
FROM
eformdynamicfieldinstance
)
)
;
CREATE UNIQUE INDEX temp_pk_mapping_u1 ON temp_pk_mapping
( enterpriseid
,ownertype
,ownerid
,itemtype
,itemid
)
;
UPDATE eformdynamicfieldinstance edfi
SET edfi.edfi_id =
(SELECT new_pk
FROM temp_pk_mapping map
WHERE edfi.enterpriseid = map.enterpriseid
AND edfi.ownertype = map.ownertype
AND edfi.ownerid = map.ownerid
AND edfi.itemtype = map.itemtype
AND edfi.itemid = map.itemid
)
;

Related

Create combination sql table

I'm trying to create a sql table in data base in VS that has room and userid column, but the sql will only accept your input if the userid exists in users table and room exists in rooms tables
Allows:
Users table:
Userid
1
2
3
RoomUsers table:
Room ----- User
1 1
2. 1
1. 2
1. 3
2. 3
Won't allow:
Users table:
Userid
1
2
RoomUsers table:
Room ----- User
1 4
Normal foreign key wont work because it only allows one of each index and not multiple, how can I allow what I need to occur,to happen?
(This would be a mess in comments)
Probably we are having an XY problem here. The thing you describe is simply solved with a foreign key. ie:
CREATE TABLE users (id INT IDENTITY NOT NULL PRIMARY KEY, ad VARCHAR(100));
CREATE TABLE rooms (id INT IDENTITY NOT NULL PRIMARY KEY, ad VARCHAR(100));
CREATE TABLE room_user
(
RoomId INT NOT NULL
, UserId INT NOT NULL
, CONSTRAINT PK_roomuser
PRIMARY KEY(RoomId, UserId)
, CONSTRAINT fk_room
FOREIGN KEY(RoomId)
REFERENCES dbo.rooms(id)
, CONSTRAINT fk_user
FOREIGN KEY(UserId)
REFERENCES dbo.users(id)
);
INSERT INTO dbo.users(ad)
OUTPUT
Inserted.id, Inserted.ad
VALUES('RayBoy')
, ('John')
, ('Frank');
INSERT INTO dbo.rooms(ad)
OUTPUT
Inserted.id, Inserted.ad
VALUES('Room1')
, ('Room2')
, ('Room3');
INSERT INTO dbo.room_user(RoomId, UserId)VALUES(1, 1), (1, 2), (2, 3);
-- won't allow
INSERT INTO dbo.room_user(RoomId, UserId)VALUES(999, 888);

How select from two different tables with multiple foreign keys

First explain what I need using an image:
Second creation tables:
CREATE SEQUENCE SEQ_tab7 MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 2206 NOCACHE ORDER NOCYCLE NOKEEP NOSCALE GLOBAL ;
CREATE SEQUENCE SEQ_tab8 MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 2206 NOCACHE ORDER NOCYCLE NOKEEP NOSCALE GLOBAL ;
CREATE TABLE TABLE_7
(
ID NUMBER(19,0) DEFAULT SEQ_tab7.nextval NOT NULL
,DESCRIPTION VARCHAR2(256) NOT NULL
,CONSTRAINT TAB_7_PK PRIMARY KEY (ID) ENABLE
);
CREATE TABLE TABLE_8
(
ID NUMBER(19,0) DEFAULT SEQ_tab8.nextval NOT NULL
,FIELD_1 NUMBER(19,0) NOT NULL
,FIELD_2 NUMBER(19,0) NOT NULL
,FIELD_3 NUMBER(19,0) NOT NULL
,FIELD_4 VARCHAR2(256)
,CONSTRAINT TAB_8_PK PRIMARY KEY (ID) ENABLE
,CONSTRAINT tab_8_FIELD_1_FK FOREIGN KEY (FIELD_1) REFERENCES TABLE_7(ID)
,CONSTRAINT tab_8_FIELD_2_FK FOREIGN KEY (FIELD_2) REFERENCES TABLE_7(ID)
,CONSTRAINT tab_8_FIELD_3_FK FOREIGN KEY (FIELD_3) REFERENCES TABLE_7(ID)
);
Third - inserts:
Insert into TABLE_7 (ID,DESCRIPTION) values ('1','desc_1');
Insert into TABLE_7 (ID,DESCRIPTION) values ('2','desc_2');
Insert into TABLE_7 (ID,DESCRIPTION) values ('3','desc_3');
Insert into TABLE_8 (ID,FIELD_1,FIELD_2,FIELD_3,FIELD_4) values ('23','1','2','1','lorem_1');
Insert into TABLE_8 (ID,FIELD_1,FIELD_2,FIELD_3,FIELD_4) values ('43','1','3','3','lorem_2');
Insert into TABLE_8 (ID,FIELD_1,FIELD_2,FIELD_3,FIELD_4) values ('54','3','3','3','lorem_3');
How could I get the desired results?
Best regards
Use multiple joins
Try this:
SELECT Base.ID, FirstJoin.Desc, SecondJoin.Desc, ThirdJoin.Desc, Base.field_4
FROM table_2 Base
JOIN table_1 FirstJoin ON FirstJoin.ID = Base.Field_1
JOIN table_1 SecondJoin ON SecondJoin.ID = Base.Field_2
JOIN table_1 ThirdJoin ON ThirdJoin.ID = Base.Field_3
Here is an example which use subqueries for each field_? it will search in table_1 for the description and use it as an attribute. I added alias for the field to make the output more clear.
SELECT
table_2.id,
(SELECT table_1.desc FROM table_1 WHERE table_1.id=table_2.field_1) as "FIELD_1",
(SELECT table_1.desc FROM table_1 WHERE table_1.id=table_2.field_2) as "FIELD_2",
(SELECT table_1.desc FROM table_1 WHERE table_1.id=table_2.field_3) as "FIELD_3",
table_2.field_4
FROM table_2;
For example the first row of table_2 is (id=23, field_1=1, field_2=2, field_3=1, field_4="lorem_1") The second attribute after performing the suggested query will be the result of the query SELECT table_1.desc FROM table_1 WHERE table_1.id=table_2.field_1 which is "desc_1" and the same process for the field_2 and field_3
in that case the result for the first row will be:
ID FIELD_1 FIELD_2 FIELD_3 FIELD_4
23 desc_1 desc_2 desc_1 lorem_1

SQL violation of Primary Key constraint on update

--EDITED--
I think I figured out the problem. The original row is being taken to update instead of the latest row therefore the CreationDate is the same and since ID + CreationDate is the primary key it returns a violation. Is there any way to select the latest row instead of the original row when updating records?
Thanks :D
----------------
I got the error violation of primary key constraint but I don't know why because my primary key values are unique.
I can add the record for 'Darren' and update it once. After that I get the error. My trigger works such that when I update an existing record, both the original and edited record are inserted into the table ProcessList so that I am able to see all the changes made to all records.
Tables:
CREATE TABLE dbo.ProcessList
(
TransactionID integer IDENTITY,
IsEdited bit DEFAULT 'FALSE',
ID integer NOT NULL,
Name varchar(30) NOT NULL,
Amount smallmoney NOT NULL,
CreationDate datetime DEFAULT GETDATE(),
ModificationDate datetime,
PRIMARY KEY (ID, CreationDate)
)
CREATE TABLE dbo.ProcessListHist
(
TransactionID integer IDENTITY,
IsEdited bit DEFAULT 'FALSE',
ID integer NOT NULL,
Name varchar(30) NOT NULL,
Amount smallmoney NOT NULL,
CreationDate datetime DEFAULT GETDATE(),
ModificationDate datetime,
PRIMARY KEY (ID, CreationDate)
)
Trigger:
CREATE TRIGGER CloneAfterUpdate ON ProcessList
AFTER UPDATE
AS
IF (UPDATE (Amount) OR UPDATE (NAME))
BEGIN
INSERT INTO ProcessListHist (ID, Name, Amount, CreationDate, ModificationDate, IsEdited)
SELECT
ID, Name, Amount, CreationDate, GETDATE(), 'True'
FROM deleted
UPDATE PL
SET PL.CreationDate = PLH.ModificationDate
FROM ProcessList PL
INNER JOIN deleted ON PL.ID = deleted.ID
AND PL.CreationDate = deleted.CreationDate
INNER JOIN ProcessListHist PLH ON PL.ID = PLH.ID
AND PLH.CreationDate = deleted.CreationDate
INSERT INTO ProcessList (ID, Name, Amount, CreationDate, ModificationDate, IsEdited)
SELECT
ID, Name, Amount, CreationDate, ModificationDate, IsEdited
FROM ProcessListHist
END
Insert/Update statements:
INSERT INTO ProcessList (ID, Name, Amount) VALUES ('1020', 'Darren', '300')
UPDATE ProcessList SET Amount = 1000 WHERE Name = 'Darren'
You logging a transaction on your history table right ? , therefore you need to remove the ID of history table from being unique. Make it TransactionID INT only, not an identity, your problem is because every time you perform an UPDATE , the record is inserted into history table, so if the record already exist meaning to say, if the ID is already existing in the history table , you are not allowed to insert the same ID, that's why you will receiving that error.

How to make a db2 trigger updates only the inserted row without going through all the records

I'm using IBM DB2 and I have the following trigger that updates a column in the Sales table after inserting in the same table
The PriceSize table:
create table PriceSize
(
P_size varchar(20) primary key not null,
P_Price decimal(5,2)
);
The CombosAndPromotions table:
Create table CombosAndPromotions
(
CP_ID char(4) primary key not null,
CP_Price decimal(5,2),
CP_Type varchar (15),
CP_Description long varchar
);
The sales table
create table sales(
FID char(3) not null,
CID int not null,
PID char(3),
P_size varchar(20),
CP_ID char(4),
Quantity int,
Price Decimal(5,2) with default 0,
FOREIGN key (FID) references Franchise,
FOREIGN key (CID) references Customer,
FOREIGN key (PID) references Product,
FOREIGN key (P_size) references PriceSize,
FOREIGN key (CP_ID) references CombosAndPromotions
);
The trigger
create trigger calculate_Price
after insert on sales
referencing new as n
for each row mode db2sql
begin atomic
if n.CP_ID is null then
update Sales s
set Price = (select Pricesize.P_price * s.Quantity
from Pricesize
where s.P_size = Pricesize.P_size);
else
update Sales s
set price = (select CombosAndPromotions.CP_price
from CombosAndPromotions
where s.CP_ID = CombosAndPromotions.CP_ID);
end if;
end#
but the issue is that this trigger updates all the rows not only the one inserted.
I would like to get some help on how to make it affect only the row inserted. Thank you
Untested, but something like:
create trigger calculate_Price
before insert on sales
referencing new as n
for each row
mode db2sql
set price = case when n.CP_ID is null then
( select ps.P_price * n.Quantity
from Pricesize ps
where n.P_size = ps.P_size )
else
( select cp.CP_price
from CombosAndPromotions cp
where n.CP_ID = cp.CP_ID )
end #
In a before trigger you affect only the row that you are about to insert. Some test data:
insert into PriceSize (p_size, p_price) values ('a',10.0);
insert into CombosAndPromotions (CP_ID, cp_price) values ('b',20.0);
insert into sales (FID, CID, P_size, cp_id, quantity, price) values ('x',1,'a',null,5,100);
insert into sales (FID, CID, P_size, cp_id, quantity, price) values ('y',2,null,'b',5,100);
select * from sales;
FID CID PID P_SIZE CP_ID QUANTITY PRICE
--- ----------- --- -------------------- ----- ----------- -------
x 1 - a - 5 50.00
y 2 - - b 5 20.00
2 record(s) selected.
for FID=X cp_id is null so the price = 10 * 5 = 50
for FID=y cp_id is not null so the price = 20
correct?

Update table using query result

I am attempting to add the result of my query into the column of an existing table.
Thus far, the query below calculates the CAR_PRICE and displays the value. However, I want to add this value to the CAR_PAYMENT_TBL in the car_price column.
The create table commands below show the relevant table and the relationships between them. Is it possible to update the CAR_PRICE value in the CAR_PAYMENT_TBL?
SELECT C.TICKET_NO,
C.REG_ID,
C.BOOKING_ID,
(R.END_DATE-R.START_DATE) AS DAYS_STAYED,
(R.END_DATE-R.START_DATE)*5 AS CAR_PRICE
FROM CAR_TBL C
LEFT JOIN
ROOM_TBL R
ON C.BOOKING_ID = R.BOOKING_ID;
TABLE SCHEMA:
CREATE TABLE CAR_PAYMENT_TBL
(
TICKET_NO INT NOT NULL PRIMARY KEY,
CAR_PRICE NUMERIC(5,2)
);
CREATE TABLE CAR_TBL
(
REG_ID VARCHAR2(7) NOT NULL PRIMARY KEY,
TICKET_NO INT NOT NULL references CAR_PAYMENT_TBL(TICKET_NO),
BOOKING_ID INT NOT NULL references BOOKING_TBL(BOOKING_ID)
);
CREATE TABLE ROOM_TBL
(
STAY_NO INT NOT NULL PRIMARY KEY,
ROOM_NO VARCHAR2(4) NOT NULL references ROOM_DETAILS_TBL(ROOM_NO),
START_DATE DATE NOT NULL,
END_DATE DATE NOT NULL,
BOOKING_ID INT NOT NULL references BOOKING_TBL(BOOKING_ID)
);
You cannot reference other tables in an UPDATE statement in Oracle - use a subquery or a MERGE statement:
UPDATE CAR_PAYMENT_TBL
SET CAR_PRICE =
(select (ROOM_TBL.END_DATE - ROOM_TBL.START_DATE)*5 from room_tbl where ... )
WHERE CAR_PAYMENT_TBL.TICKET_NO = &TICKET_NO;
You'll also have to provide a sensible WHERE clause for the subquery (assuing &TICKET_NO is really a bind variable and not your join condition for the two tables).
If you want to update all the records of CAR_PAYMENT_TBL based on the values of ROOM_TBL
then
UPDATE CAR_PAYMENT_TBL
SET CAR_PRICE = (select (ROOM_TBL.END_DATE – ROOM_TBL.START_DATE)*5
FROM ROOM_TBL WHERE CAR_PAYMENT_TBL.TICKET_NO = ROOM_TBL.TICKET_NO)
If you want to update only specific record of CAR_PAYMENT_TBL then
UPDATE CAR_PAYMENT_TBL
SET CAR_PRICE = (select (ROOM_TBL.END_DATE – ROOM_TBL.START_DATE)*5
FROM ROOM_TBL WHERE CAR_PAYMENT_TBL.TICKET_NO = ROOM_TBL.TICKET_NO)
where CAR_PAYMENT_TBL = &ticket_num;