Profit calculation according to the date of each month - sql
I am new in oracle , I have some question .I want my output table have to three columns named ref_dep_id , due_date,profit_amount ..please help me .
my code is :
--------------------------------------------------------
-- DDL for Table TBL_DEPOSIT_INTEREST_PAYMENT
--------------------------------------------------------
CREATE TABLE "IRAN"."TBL_DEPOSIT_INTEREST_PAYMENT"
( "REF_DEP_ID" NUMBER,
"DUE_DATE" DATE,
"PROFIT_AMOUNT" NUMBER
);
--------------------------------------------------------
-- DDL for Table TBL_DEPOSIT
--------------------------------------------------------
CREATE TABLE "IRAN"."TBL_DEPOSIT"
( "DEP_ID" NUMBER,
"REF_DEPOSIT_TYPE" NUMBER,
"REF_BRANCH" NUMBER,
"REF_CUSTOMER" NUMBER,
"DUE_DATE" DATE,
"BALANCE" NUMBER,
"OPENING_DATE" DATE,
"RATE" NUMBER,
"MODALITY_TYPE" NUMBER,
"REF_DEPOSIT_ACCOUNTING" NUMBER,
"REF_CURRENCY" NUMBER,
"REF_RATE" NUMBER,
"REGION_ID" NUMBER,
"REGION_NAME" VARCHAR2(4000 BYTE),
"REF_DEPOSIT_TYPE_SUB" NUMBER,
"STC_2" NUMBER(10,0),
"CUS_TYPE" NUMBER
) ;
--------------------------------------------------------
-- Constraints for Table TBL_DEPOSIT
--------------------------------------------------------
ALTER TABLE "IRAN"."TBL_DEPOSIT" MODIFY ("DEP_ID" NOT NULL ENABLE);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309166952002,40,60,6664833,to_date('18-JUN-09','DD-MON-RR'),6014,to_date('18-JUN-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309166736000,40,60,3884509,to_date('30-JUN-09','DD-MON-RR'),992180,to_date('30-JUN-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309165005000,40,60,63674543,to_date('23-OCT-09','DD-MON-RR'),32785,to_date('23-OCT-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309167087001,30,60,6670947,to_date('07-MAY-09','DD-MON-RR'),693239,to_date('07-MAY-08','DD-MON-RR'),10,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (201058140000,30,60,20915955,to_date('10-JUN-09','DD-MON-RR'),278768,to_date('10-JUN-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200252187009,30,60,54840160,to_date('05-AUG-09','DD-MON-RR'),160528,to_date('05-AUG-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200369929009,30,60,72046709,to_date('26-APR-09','DD-MON-RR'),792238,to_date('26-APR-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200370363003,30,60,12857116,to_date('26-APR-09','DD-MON-RR'),307572,to_date('26-APR-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200536619001,30,60,3335361,to_date('16-SEP-09','DD-MON-RR'),3252240,to_date('16-SEP-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200556682003,30,60,26903666,to_date('05-OCT-09','DD-MON-RR'),57499,to_date('05-OCT-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
profit_amount calculated with formula balance *rate /12 *100
and profit_amount insert into table for each month
function for each month is :
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309166952002,40,60,6664833,to_date('18-JUN-09','DD-MON-RR'),6014,to_date('18-JUN-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309166736000,40,60,3884509,to_date('30-JUN-09','DD-MON-RR'),992180,to_date('30-JUN-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309165005000,40,60,63674543,to_date('23-OCT-09','DD-MON-RR'),32785,to_date('23-OCT-08','DD-MON-RR'),13,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (309167087001,30,60,6670947,to_date('07-MAY-09','DD-MON-RR'),693239,to_date('07-MAY-08','DD-MON-RR'),10,3,920115120101,4,39915,10020,'?????',14011,14011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (201058140000,30,60,20915955,to_date('10-JUN-09','DD-MON-RR'),278768,to_date('10-JUN-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200252187009,30,60,54840160,to_date('05-AUG-09','DD-MON-RR'),160528,to_date('05-AUG-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200369929009,30,60,72046709,to_date('26-APR-09','DD-MON-RR'),792238,to_date('26-APR-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200370363003,30,60,12857116,to_date('26-APR-09','DD-MON-RR'),307572,to_date('26-APR-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200536619001,30,60,3335361,to_date('16-SEP-09','DD-MON-RR'),3252240,to_date('16-SEP-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
Insert into TBL_DEPOSIT (DEP_ID,REF_DEPOSIT_TYPE,REF_BRANCH,REF_CUSTOMER,DUE_DATE,BALANCE,OPENING_DATE,RATE,MODALITY_TYPE,REF_DEPOSIT_ACCOUNTING,REF_CURRENCY,REF_RATE,REGION_ID,REGION_NAME,REF_DEPOSIT_TYPE_SUB,STC_2,CUS_TYPE) values (200556682003,30,60,26903666,to_date('05-OCT-09','DD-MON-RR'),57499,to_date('05-OCT-08','DD-MON-RR'),10,3,92011532010120115320101,4,39916,10020,'?????',13011,13011,1);
I use this function
create or replace function fnc_get_next_n_month(IN_DATE IN int,duration in int)
RETURN varchar
as
out_string varchar(2000);
BEGIN
SELECT LISTAGG(result,',') WITHIN GROUP (ORDER BY result)result into out_string FROM (
select
to_char(
substr(IN_DATE, 1, 4)+
case when
(substr(IN_DATE, 5, 2) + ( b.n ))/12 = 0 then 0
else
ceil( (substr(IN_DATE, 5, 2) + ( b.n ))/12 ) -1 end
)
||
CASE
WHEN mod(substr(IN_DATE, 5, 2) + ( b.n ),12) = 0 then '12'
when mod(substr(IN_DATE, 5, 2) + ( b.n ),12) between 1 and 9 then '0'||to_char(mod(substr(IN_DATE, 5, 2) + ( b.n ),12))
else to_char(mod(substr(IN_DATE, 5, 2) + ( b.n ),12))
end
|| substr(IN_DATE, 7, 2) result
FROM dual,(select rownum n from dual connect by level <= duration order by n desc)b ) /*where rownum = 1*/ ;
return out_string;
end ;
I want the output table have to 3 columns :
profit_amount
due_date
ref_dep_id
Try it like this:
INSERT INTO TBL_DEPOSIT_INTEREST_PAYMENT (REF_DEP_ID, DUE_DATE, PROFIT_AMOUNT)
SELECT DEP_ID "REF_DEP_ID",
DUE_DATE "DUE_DATE",
Round(BALANCE * RATE / 12 * 100, 2) "PROFIT_AMOUNT"
FROM TBL_DEPOSIT;
Your formula from question looks a bit unclear and I left it as in the question. It woud be better if you put some brackets to direct the calculation. Anyway, you would make it right yourself...
R e s u l t :
REF_DEP_ID DUE_DATE PROFIT_AMOUNT
---------------------------------------- --------- -------------
309166952002 18-JUN-09 651516.67
309166736000 30-JUN-09 107486167
309165005000 23-OCT-09 3551708.33
309167087001 07-MAY-09 57769916.7
201058140000 10-JUN-09 23230666.7
200252187009 05-AUG-09 13377333.3
200369929009 26-APR-09 66019833.3
200370363003 26-APR-09 25631000
200536619001 16-SEP-09 271020000
200556682003 05-OCT-09 4791583.33
ADDITION
After the comments - I'm still not sure what are you trying to do. Here is a sample (Just for first row DEP_ID=309166952002) that generates monthly rows (12 of them) along with your db data and transforms Gregorian dates to Julian values (if Julian means solar - I don't know). Anyway here is the code that could be used to insert values into your result table (TBL_DEPOSIT_INTEREST_PAYMENT):
SELECT To_Char(d.DEP_ID) "REF_DEP_ID",
d.OPENING_DATE "OPENING_DATE",
d.DUE_DATE "DUE_DATE",
Round(d.BALANCE * d.RATE / 12 * 100, 2) "PROFIT_AMOUNT",
--
'<--' "DB_DATA", '-->' "SQL_DATES",
CASE WHEN ROW_NUMBER() OVER(Partition By d.DEP_ID Order By m.MONTHS) = 1 THEN d.OPENING_DATE END "START_DATE",
ADD_MONTHS(d.OPENING_DATE, m.MONTHS) "NEXT_MONTH",
CASE WHEN ROW_NUMBER() OVER(Partition By d.DEP_ID Order By m.MONTHS) = 12 THEN d.DUE_DATE END "END_DATE",
--
'-->' "JULIAN_SQL_DATES",
To_Number( To_Char( CASE WHEN ROW_NUMBER() OVER(Partition By d.DEP_ID Order By m.MONTHS) = 1 THEN d.OPENING_DATE END, 'J') ) "J_START_DATE",
To_Number( To_Char( ADD_MONTHS(d.OPENING_DATE, m.MONTHS), 'J') ) "J_NEXT_MONTH",
To_Number( To_Char( CASE WHEN ROW_NUMBER() OVER(Partition By d.DEP_ID Order By m.MONTHS) = 12 THEN d.DUE_DATE END, 'J') ) "J_END_DATE"
FROM
TBL_DEPOSIT d
INNER JOIN (SELECT LEVEL "MONTHS" FROM Dual Connect By LEVEl <= 12) m ON(1 = 1)
WHERE
DEP_ID = 309166952002
ORDER BY m.MONTHS
REF_DEP_ID OPENING_DATE DUE_DATE PROFIT_AMOUNT DB_DATA SQL_DATES START_DATE NEXT_MONTH END_DATE JULIAN_SQL_DATES J_START_DATE J_NEXT_MONTH J_END_DATE
---------------------------------------- ------------ --------- ------------- ------- --------- ---------- ---------- ------------- ---------------- ------------ ------------ ---------------
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-JUN-08 18-JUL-08 --> 2454636 2454666
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-AUG-08 --> 2454697
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-SEP-08 --> 2454728
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-OCT-08 --> 2454758
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-NOV-08 --> 2454789
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-DEC-08 --> 2454819
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-JAN-09 --> 2454850
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-FEB-09 --> 2454881
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-MAR-09 --> 2454909
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-APR-09 --> 2454940
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-MAY-09 --> 2454970
309166952002 18-JUN-08 18-JUN-09 651516.67 <-- --> 18-JUN-09 18-JUN-09 --> 2455001 2455001
Related
How to Insert Select from last table auto increment Id
I have two tables as shown here: I need to insert some data by a stored procedure as below code: ALTER PROCEDURE [dbo].[DeviceInvoiceInsert] #dt AS DeviceInvoiceArray READONLY AS DECLARE #customerDeviceId BIGINT DECLARE #customerId BIGINT DECLARE #filterChangeDate DATE BEGIN SET #customerId = (SELECT TOP 1 CustomerId FROM #dt WHERE CustomerId IS NOT NULL) SET #filterChangeDate = (SELECT TOP 1 filterChangeDate FROM #dt) INSERT INTO CustomerDevice (customerId, deviceId, deviceBuyDate, devicePrice) SELECT customerId, deviceId, deviceBuyDate, devicePrice FROM #dt WHERE CustomerId IS NOT NULL SET #customerDeviceId = SCOPE_IDENTITY() INSERT INTO FilterChange (customerId, filterId, customerDeviceId, filterChangeDate) SELECT #customerId, dt.filterId, #customerDeviceId, #filterChangeDate FROM #dt AS dt END The problem is that when the procedure wants to insert data into the FilterChange table, the #customerDeviceId always has the last IDENTITY Id. How can I figure out this problem? Update Thanks for #T N answer but his solution is just to insert one filter per device, so in my case, there can be many filters per device
As mentioned above, using the OUTPUT clause is the best way to capture inserted IDENTITY or other implicitly assigned values. However, you also need to correlate this data with other values from your source table. As far as I know, this cannot be done using a regular INSERT statement, which only allows you to capture data from the target table via the INSERTED pseudo-table. I am assuming that none of the explicitly inserted values in the first target table can be used to reliably uniquely identify the source record. A workaround is to use the MERGE statement to perform the insert. The OUTPUT clause may then be used to capture a combination of source and inserted target data. ALTER PROCEDURE [dbo].[DeviceInvoiceInsert] #dt AS DeviceInvoiceArray READONLY AS BEGIN -- Temp table to receive captured data from output clause DECLARE #FilterChangeData TABLE ( customerId INT, filterId INT, customerDeviceId INT, filterChangeDate DATETIME2 ) -- Merge is used instead of a plain INSERT so that we can capture -- a combination of source and inserted data MERGE CustomerDevice AS TGT USING (SELECT * FROM #dt WHERE CustomerId IS NOT NULL) AS SRC ON 1 = 0 -- Never match WHEN NOT MATCHED THEN INSERT (customerId, deviceId, deviceBuyDate, devicePrice) VALUES (SRC.customerId, SRC.deviceId, SRC.deviceBuyDate, SRC.devicePrice) OUTPUT SRC.customerId, SRC.filterId, INSERTED.customerDeviceId, SRC.filterChangeDate INTO #FilterChangeData ; INSERT INTO FilterChange (customerId, filterId, customerDeviceId, filterChangeDate) SELECT customerId, filterId, customerDeviceId, filterChangeDate FROM #FilterChangeData END Given the following #dt source data: customerId deviceId deviceBuyDate devicePrice filterId filterChangeDate 11 111 2023-01-01 111.1100 1111 2023-02-01 22 222 2023-01-02 222.2200 2222 2023-02-02 33 333 2023-01-03 333.3300 3333 2023-02-03 11 222 2023-01-04 333.3300 1111 2023-02-04 The following is inserted into CustomerDevice: customerDeviceId customerId deviceId deviceBuyDate devicePrice 1 11 111 2023-01-01 111.1100 2 22 222 2023-01-02 222.2200 3 33 333 2023-01-03 333.3300 4 11 222 2023-01-04 333.3300 The following is inserted into FilterChange: customerId filterId customerDeviceId filterChangeDate 11 1111 1 2023-02-01 22 2222 2 2023-02-02 33 3333 3 2023-02-03 11 1111 4 2023-02-04 See this db<>fiddle.
Thanks to #TN, his solution is just to insert one filter per device, so in my case, there can be many filters per device, So I just manipulate the last part to solve the problem. Also the corrected #dt value is like this: As you can see, there are many filters per device, and All customers are the same because per invoice belongs to one customer so I had to mark the rest repetitive devices with null to group filters per device. Here is the corrected code, Thanks by #tn: -- Example showing MERGE (instead of INSERT) to capture a combination of -- source and inserted data in an OUTPUT clause. CREATE TABLE CustomerDevice ( customerDeviceId INT IDENTITY(1,1), customerId INT, deviceId INT, deviceBuyDate DATE, devicePrice NUMERIC(19,4) ) CREATE TABLE FilterChange ( customerId INT, filterId INT, customerDeviceId INT, filterChangeDate DATE ) DECLARE #dt TABLE ( customerId INT, deviceId INT, deviceBuyDate DATE, devicePrice NUMERIC(19,4), filterId INT, filterChangeDate DATE ) INSERT #dt VALUES (3, 1, '2023-01-01', 111.11, 1, '2023-02-01'), (NULL, 1, '2023-01-02', 222.22, 2, '2023-02-02'), (NULL, 1, '2023-01-03', 333.33, 3, '2023-02-03'), (NULL, 1, '2023-01-03', 333.33, 4, '2023-02-03'), (3, 2, '2023-01-04', 333.33, 1, '2023-02-04'), (NULL, 2, '2023-01-04', 333.33, 2, '2023-02-04'), (NULL, 2, '2023-01-04', 333.33, 3, '2023-02-04'), (NULL, 2, '2023-01-04', 333.33, 4, '2023-02-04') -- Procedure body DECLARE #customerId BIGINT SET #customerId = (SELECT TOP 1 CustomerId FROM #dt WHERE CustomerId IS NOT NULL) DECLARE #FilterChangeData TABLE ( customerId INT, deviceId INT, filterId INT, customerDeviceId INT, filterChangeDate DATETIME ) MERGE CustomerDevice AS TGT USING (SELECT * FROM #dt WHERE CustomerId IS NOT NULL) AS SRC ON 1 = 0 -- Never match WHEN NOT MATCHED THEN INSERT (customerId, deviceId, deviceBuyDate, devicePrice) VALUES (SRC.customerId, SRC.deviceId, SRC.deviceBuyDate, SRC.devicePrice) OUTPUT SRC.customerId,SRC.deviceId, SRC.filterId, INSERTED.customerDeviceId, SRC.filterChangeDate INTO #FilterChangeData; INSERT INTO FilterChange (customerId, filterId, customerDeviceId, filterChangeDate) SELECT #customerId, dt.filterId, fcd.customerDeviceId, dt.filterChangeDate FROM #dt AS dt INNER JOIN #FilterChangeData AS fcd ON fcd.deviceId = dt.deviceId -- End procedure body SELECT * FROM #dt SELECT * FROM CustomerDevice SELECT * FROM FilterChange Result show in, [https://dbfiddle.uk/yf7z_wqr][2]
Using Oracle merge statement to merge specific rows from a group
Using Oracle database 19c, I'm trying to figure out how to merge only the rows with the most recent date in table1 into table2. If a new row with a matching tab1_id is later added to table1, it should update the row with the matching tab1_id the next time the merge statement is run. CREATE TABLE table1 ( tab1_id NUMBER(2) , log_date DATE , group_name VARCHAR2(10) , text2 VARCHAR2(10) , text3 VARCHAR2(10) , CONSTRAINT pk_tab1 PRIMARY KEY (tab1_id) ); INSERT INTO table1 VALUES (1, '10-FEB-21', 'group1', 'textrow1', 'textrow1'); INSERT INTO table1 VALUES (2, '09-JAN-21', 'group1', 'textrow2', 'textrow2'); INSERT INTO table1 VALUES (3, '09-MAR-21', 'group1', 'textrow3', 'textrow3'); INSERT INTO table1 VALUES (4, '08-MAR-21', 'group2', 'textrow4', 'textrow4'); INSERT INTO table1 VALUES (5, '08-JAN-21', 'group2', 'textrow5', 'textrow5'); INSERT INTO table1 VALUES (6, '10-FEB-21', 'group2', 'textrow6', 'textrow6'); INSERT INTO table1 VALUES (7, '10-MAR-21', 'group3', 'textrow7', 'textrow7'); INSERT INTO table1 VALUES (8, '05-JAN-21', 'group3', 'textrow8', 'textrow8'); INSERT INTO table1 VALUES (9, '05-FEB-21', 'group3', 'textrow9', 'textrow9'); COMMIT; CREATE TABLE table2 ( tab2_id NUMBER(2) , tab1_id NUMBER(2) UNIQUE NOT NULL , log_date DATE , group_name VARCHAR2(10) , text2 VARCHAR2(10) , text3 VARCHAR2(10) , CONSTRAINT pk_tab2 PRIMARY KEY (tab2_id) ); CREATE SEQUENCE seq_table2_id MINVALUE 1 MAXVALUE 99 INCREMENT BY 1 START WITH 1 NOCACHE ORDER NOCYCLE; These rows should merge into TABLE2 because they contain the most recent date in the log_date column. edit-(Results should be something like this, ignoring the tab2_id column generated from a sequence.) TAB2_ID TAB1_ID LOG_DATE GROUP_NAME TEXT2 TEXT3 ---------- ---------- --------- ---------- ---------- ---------- 1 3 09-MAR-21 group1 textrow3 textrow3 2 4 08-MAR-21 group2 textrow4 textrow4 3 7 10-MAR-21 group3 textrow7 textrow7 Update: based on Caius' comments, I got this to work: MERGE INTO table2 t2 USING( WITH T1_TO_MERGE AS ( SELECT row_number() OVER( PARTITION BY group_name ORDER BY log_date DESC ) row_num, tab1_id, log_date, group_name, text2, text3 FROM table1 ) SELECT * FROM T1_TO_MERGE WHERE row_num = 1 ) t1 ON(t2.group_name = t1.group_name) WHEN MATCHED THEN UPDATE SET t2.tab1_id = t1.tab1_id, t2.log_date = t1.log_date, t2.text2 = t1.text2, t2.text3 = t1.text3 WHEN NOT MATCHED THEN INSERT( t2.tab2_id, t2.tab1_id, t2.log_date, t2.group_name, t2.text2, t2.text3) VALUES (seq_table2_id.NEXTVAL, t1.tab1_id, t1.log_date, t1.group_name, t1.text2, t1.text3); COMMIT;
How to create an Oracle audit trigger?
CREATE OR REPLACE TRIGGER EVALUATION BEFORE INSERT OR UPDATE OR DELETE ON BOOKING FOR EACH ROW DECLARE BEGIN SELECT BOOKING_EVALUATION FROM BOOKING WHERE BOOKING_EVALUATION > 2; SELECT BOOKING_EVALUATION INTO EVALUATIONAUDIT FROM BOOKING; IF INSERTING THEN INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION) VALUES(:NEW.VOYAGES_ID,:NEW.CUSTOMER_NAME,:NEW.START_DATE,:NEW.SHIP_NAME,:NEW.BOOKING_EVALUATION); END IF; IF UPDATING THEN INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION) VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION); END IF; IF DELETING THEN INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION) VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION); END IF; END; This is my audit table: desc evaluationaudit; Name Null? Type ----------------------------------------- -------- ----------------------- AUDITT_ID NOT NULL NUMBER(10) VOYAGES_ID NOT NULL NUMBER(10) CUSTOMER_NAME NOT NULL VARCHAR2(20) START_DATE NOT NULL DATE SHIP_NAME NOT NULL VARCHAR2(20) BOOKING_EVALUATION NOT NULL NUMBER(20) and this is my show error output: SQL> SHOW ERRORS; Errors for TRIGGER EVALUATION: LINE/COL ERROR -------- ------------------------------------------------------------- 12/24 PLS-00049: bad bind variable 'NEW.CUSTOMER_NAME' 12/43 PLS-00049: bad bind variable 'NEW.START_DATE' 12/59 PLS-00049: bad bind variable 'NEW.SHIP_NAME' 18/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME' 18/43 PLS-00049: bad bind variable 'OLD.START_DATE' 18/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME' 24/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME' 24/43 PLS-00049: bad bind variable 'OLD.START_DATE' 24/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME' I wanted to add to the audit table. If a customer gives a poor evaluation of 2 or less, the details of their voyage (customer_name, name and date of the cruise, ship name and evaluation) but I'm getting error Warning: Trigger created with compilation errors. And the audit table is empty, showing no rows selected. I can't seem to find the problem where I am going wrong.
Maybe the following code snippets will help you. Suppose we have 2 tables: BOOKING, and EVALUATION_AUDIT (everything written in uppercase letters is taken from the code in your question). Tables create table booking ( VOYAGES_ID number primary key , CUSTOMER_NAME VARCHAR2(20) NOT NULL , START_DATE DATE NOT NULL , SHIP_NAME VARCHAR2(20) NOT NULL , BOOKING_EVALUATION NUMBER(20) NOT NULL ) ; create table evaluationaudit ( AUDITT_ID number generated always as identity start with 5000 primary key , trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!' , VOYAGES_ID NUMBER(10) NOT NULL , CUSTOMER_NAME VARCHAR2(20) NOT NULL , START_DATE DATE NOT NULL , SHIP_NAME VARCHAR2(20) NOT NULL , BOOKING_EVALUATION NUMBER(20) NOT NULL ) ; Trigger -- "updating" and "deleting" code omitted for clarity CREATE OR REPLACE TRIGGER EVALUATION_trigger BEFORE INSERT OR UPDATE OR DELETE ON BOOKING FOR EACH ROW BEGIN case when INSERTING then if :new.booking_evaluation <= 2 then INSERT INTO EVALUATIONAUDIT ( trg_cond_pred, VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION ) VALUES ( 'INSERTING' , :NEW.VOYAGES_ID , :NEW.CUSTOMER_NAME , :NEW.START_DATE , :NEW.SHIP_NAME , :NEW.BOOKING_EVALUATION ); end if ; end case ; END ; / Testing One of your requirements (in your question) is: I wanted to add to the audit table. If a customer gives a poor evaluation of 2 or less, the details of their voyage (customer_name, name and date of the cruise, ship name and evaluation) delete from evaluationaudit ; delete from booking ; -- booking_evaluation greater than 2 -> no entry in audit table insert into booking ( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION ) values ( 1111, 'customer1', date '2018-05-24', 'ship1', 9999 ) ; select * from evaluationaudit ; -- no rows selected -- booking_evalution = 2 -> insert a row into the audit table insert into booking ( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION ) values ( 1112, 'customer1', date '2018-05-24', 'ship1', 2 ) ; select * from evaluationaudit ; AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME START_DATE SHIP_NAME BOOKING_EVALUATION 5000 INSERTING 1112 customer1 24-MAY-18 ship1 2 __Update__ If - as you wrote in your comment - you need to pull in some more data from other tables, maybe you want to try the following approach: keep the trigger code rather brief, and use it to call a procedure for the more complicated stuff eg Tables create table evaluationaudit ( AUDITT_ID number generated always as identity start with 7000 primary key , trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!' , VOYAGES_ID NUMBER NOT NULL , CUSTOMER_NAME VARCHAR2(20) NOT NULL , SHIP_NAME VARCHAR2(20) NOT NULL , BOOKING_EVALUATION NUMBER(20) NOT NULL ) ; create table ships ( name varchar2( 64 ), id number unique ) ; create table customers ( name varchar2( 64 ), id number unique ) ; insert into ships ( name, id ) values ( 'ship1', 501 ); insert into ships ( name, id ) values ( 'ship2', 502 ); insert into ships ( name, id ) values ( 'ship3', 503 ); insert into customers ( name, id ) values ( 'customer1', 771 ) ; insert into customers ( name, id ) values ( 'customer2', 772 ) ; insert into customers ( name, id ) values ( 'customer3', 773 ) ; create table bookings ( id number generated always as identity start with 5000 primary key , voyagesid number , shipid number , customerid number , evaluation number , bookingdate date ); Trigger create or replace trigger bookingeval before insert on bookings for each row when ( new.evaluation <= 2 ) -- Use NEW without colon here! ( see documentation ) begin auditproc( :new.voyagesid, :new.customerid, :new.shipid, :new.evaluation ) ; end ; / Procedure create or replace procedure auditproc ( voyagesid_ number , customerid_ number , shipid_ number , evaluation_ number ) as customername varchar2( 64 ) := '' ; shipname varchar2( 64 ) := '' ; begin -- need to find the customername and shipname before INSERT select name into customername from customers where id = customerid_ ; select name into shipname from ships where id = shipid_ ; insert into evaluationaudit ( trg_cond_pred, voyages_id, customer_name, ship_name, booking_evaluation ) values ( 'INSERTING' , voyagesid_ , customername , shipname , evaluation_ ); end ; / Testing -- evaluation > 2 -> no INSERT into evaluationaudit insert into bookings ( voyagesid, customerid, shipid, evaluation, bookingdate ) values ( 1111, 771, 501, 9999, sysdate ) ; select * from evaluationaudit ; -- no rows selected -- evaluation = 2 -- -> trigger calling audit procedure -> inserts into evaluationaudit insert into bookings ( voyagesid, customerid, shipid, evaluation, bookingdate ) values ( 1112, 772, 502, 2, sysdate ) ; select * from evaluationaudit ; AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME SHIP_NAME BOOKING_EVALUATION 7000 INSERTING 1112 customer2 ship2 2
Update previous record version during Oracle insert trigger
This is my insert trigger on Table_A where I store parameters to my system. When I do insert to the table, I want to change end_date of last record in order to keep record versioning. create or replace trigger parameter_version before insert on parameters for each row declare v_is_exist number := 0; v_rowid rowid; begin select count(*) into v_is_exist from parameters where name = :new.name; -- check if parameter exist select rowid into v_rowid from parameters where name = :new.name and end_date is null; -- record rowid, which sholud be changed if v_is_exist <> 0 then set end_date = :new.start_date - 1 end if; end; Situation in table before insert is: | id | name | value | start_date | end_date | ----------------------------------------------- | 1 |Par_A | 10 | 2016-09-01 | 2016-10-01 | ----------------------------------------------- | 2 |Par_A | 20 | 2016-10-02 | 2016-10-03 | ----------------------------------------------- | 3 |Par_A | 30 | 2016-10-05 | <null> | ----------------------------------------------- Record with id=3 should set end_date on :new_start_date - 1 (close version) and in inserting record I have a next param version with start_date = sysdate. I've got an ORA-04091 error 'table name is mutating, trigger/function may not see it'. I know that this case is hard and probably impossible but maybe someone know the solution? Or maybe exists another solution that case?
You can handle this with an After Statement trigger with the LEAD Analytic Function: DROP TABLE demo; CREATE TABLE demo( id NUMBER , name VARCHAR2( 30 ) , VALUE NUMBER , start_date DATE , end_date DATE ); INSERT INTO demo( id, name, VALUE, start_date, end_date ) VALUES ( 1, 'Par_A', 10, TO_DATE( '2016-09-01', 'YYYY-MM-DD' ), TO_DATE( '2016-10-01', 'YYYY-MM-DD' ) ); INSERT INTO demo( id, name, VALUE, start_date, end_date ) VALUES ( 2, 'Par_A', 20, TO_DATE( '2016-10-02', 'YYYY-MM-DD' ), TO_DATE( '2016-10-04', 'YYYY-MM-DD' ) ); INSERT INTO demo( id, name, VALUE, start_date ) VALUES ( 3, 'Par_A', 30, TO_DATE( '2016-10-05', 'YYYY-MM-DD' ) ); INSERT INTO demo( id, name, VALUE, start_date ) VALUES ( 4, 'Par_A', 40, TO_DATE( '2016-10-07', 'YYYY-MM-DD' ) ); INSERT INTO demo( id, name, VALUE, start_date ) VALUES ( 5, 'Par_A', 50, TO_DATE( '2016-10-11', 'YYYY-MM-DD' ) ); COMMIT; SELECT id , name , start_date , end_date , LEAD( start_date ) OVER( PARTITION BY name ORDER BY start_date ) - 1 AS new_date FROM demo WHERE end_date IS NULL ORDER BY id; CREATE OR REPLACE TRIGGER demo_aius AFTER INSERT OR UPDATE ON demo REFERENCING NEW AS new OLD AS old DECLARE CURSOR c_todo IS SELECT id, new_date FROM (SELECT id , name , start_date , end_date , LEAD( start_date ) OVER( PARTITION BY name ORDER BY start_date ) - 1 AS new_date FROM demo WHERE end_date IS NULL) WHERE new_date IS NOT NULL; BEGIN FOR rec IN c_todo LOOP UPDATE demo SET end_date = rec.new_date WHERE id = rec.id; END LOOP; END demo_aius; / INSERT INTO demo( id, name, VALUE, start_date ) VALUES ( 6, 'Par_A', 60, TO_DATE( '2016-10-15', 'YYYY-MM-DD' ) ); COMMIT; SELECT id , name , start_date , end_date FROM demo ORDER BY id; Like the Script shows, such an Update can even handle multiple missing end dates, in case the trigger was accidentally disabled. The "PARTITION BY name" part makes sure that it also functions after complex insert statements. BtW I agree that Autonomous Transactions in triggers are a last resort. I try to avoid triggers in general by controlling the User Interface and putting all such functionality in packages.
Try something like this: create or replace trigger parameter_version before insert on parameters for each row begin /*Don't care if there's 0 rows updated */ update parameters set end_date = :new.start_date - 1 where name = :new.name and end_date is null; :new.end_date := null; end;
sql compute difference between 2 rows
I'm looking for a methodology to compare the difference between 2 rows in the same table. From what I found here (How to get difference between two rows for a column field?) it's almost what I wanted. I have done the following code: create table #tmpTest ( id_fund int null, id_ShareType int null, ValueDate datetime null, VarNAV float null, FundPerf float null, ) insert into #tmpTest(id_fund, id_ShareType, ValueDate, VarNAV) values(1,1,'20140101',100) insert into #tmpTest(id_fund, id_ShareType, ValueDate, VarNAV) values(1,1,'20140102',20) update #tmpTest set hrc.FundPerf = (isnull(hrn.VarNAV, 0) - hrc.VarNAV)/hrc.VarNAV from #tmpTest hrc left join #tmpTest hrn on hrn.ValueDate = (select min(ValueDate) from #tmpTest where ValueDate > hrc.ValueDate) and hrc.id_fund = hrn.id_fund and hrc.id_ShareType = hrn.id_ShareType My issue is that the result I'm computing starts on line 1 instead of line 2. Hereunder the result I'm obtaining: id_fund id_ShareType ValueDate VarNAV FundPerf ------- ------------ ------------------- ------- ----------------------------- 1 1 2014-01-01 00:00:00 100 -0.8 1 1 2014-01-02 00:00:00 20 -1 whereas I'd like it to be that way: id_fund id_ShareType ValueDate VarNAV FundPerf ------- ------------ ------------------- ------- ----------------------------- 1 1 2014-01-01 00:00:00 100 -1 1 1 2014-01-02 00:00:00 20 -0.8 What's wrong with my approach?
You are not restricting the minimum to the same fund and share type. update #tmpTest set hrc.FundPerf = (isnull(hrn.VarNAV, 0) - hrc.VarNAV)/hrc.VarNAV from #tmpTest hrc left join #tmpTest hrn on hrn.ValueDate = (select min(ValueDate) from #tmpTest tt where tt.ValueDate > hrc.ValueDate and hrc.id_fund = tt.id_fund and hrc.id_ShareType = tt.id_ShareType ) and hrc.id_fund = hrn.id_fund and hrc.id_ShareType = hrn.id_ShareType ;
Try this: update hrn set FundPerf = (isnull(hrn.VarNAV, 0) - hrc.VarNAV)/hrc.VarNAV from #tmpTest hrc left join #tmpTest hrn on hrn.ValueDate = (select min(ValueDate) from #tmpTest where ValueDate > hrc.ValueDate) and hrc.id_fund = hrn.id_fund and hrc.id_ShareType = hrn.id_ShareType
Hi you can achieve this using by CTE (Common Table Expression) create table #tmpTest ( id_fund int null, id_ShareType int null, ValueDate datetime null, VarNAV float null, FundPerf float null, ) insert into #tmpTest(id_fund, id_ShareType, ValueDate, VarNAV) values(1,1,'20140101',100) insert into #tmpTest(id_fund, id_ShareType, ValueDate, VarNAV) values(1,1,'20140102',20) ;With tbl as ( Select Row_Number() OVER (Order by T.ValueDate) as RowNumber,* From #tmpTest T )SELECT Cur.*,(ISNULL(Cur.VarNAV,0) - ISNULL(Prv.VarNAV,0))/Prv.VarNAV as [Col Name] FROM tbl Cur LEFT OUTER JOIN tbl Prv ON Cur.RowNumber = Prv.RowNumber+1 ORDER BY Cur.ValueDate