how to remove correlated subquery - sql

I'd like to rewrite this query so as not to use the correlated subquery -- but do achieve the same output from the query.
CREATE TABLE "TABLE_1"
( "SITE_ID" NUMBER(*,0),
"USER_ID" NUMBER(*,0),
"REC_ID" NUMBER,
"REPORT_DATE" DATE
) ;
CREATE TABLE "TABLE_2"
( "SITE_ID" NUMBER,
"NOTE_DATE" DATE,
"NOTES" VARCHAR2(2000 BYTE),
"USER_ID" NUMBER,
"REC_ID" NUMBER
) ;
CREATE TABLE "TABLE_3"
( "SITE_ID" NUMBER,
"NOTE_DATE" DATE,
"HELP_NOTES" VARCHAR2(2000 BYTE),
"USER_ID" NUMBER,
"REC_ID" NUMBER
) ;
REM INSERTING into TABLE_1
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('17-APR-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('01-MAY-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('08-MAY-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('24-APR-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('15-MAY-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('05-JUN-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('22-MAY-10','DD-MON-RR'));
Insert into TABLE_1 (SITE_ID,USER_ID,REC_ID,REPORT_DATE) values (1,6165,121,to_date('29-MAY-10','DD-MON-RR'));
REM INSERTING into TABLE_2
Insert into TABLE_2 (SITE_ID,NOTE_DATE,NOTES,USER_ID,REC_ID) values (1,to_date('13-APR-10','DD-MON-RR'),'Notes - we need stuff.',6165,121);
REM INSERTING into TABLE_3
Insert into TABLE_3 (SITE_ID,NOTE_DATE,HELP_NOTES,USER_ID,REC_ID) values (1,to_date('17-MAY-10','DD-MON-RR'),'Entry #1',1932,121);
Insert into TABLE_3 (SITE_ID,NOTE_DATE,HELP_NOTES,USER_ID,REC_ID) values (1,to_date('12-MAY-10','DD-MON-RR'),'Entry #2',6005,121);
Insert into TABLE_3 (SITE_ID,NOTE_DATE,HELP_NOTES,USER_ID,REC_ID) values (1,to_date('25-MAY-10','DD-MON-RR'),'Entry #3',1932,121);
ALTER TABLE "TABLE_1" MODIFY ("REC_ID" NOT NULL ENABLE);
--------------------------------------------------------
-- Constraints for Table TABLE_2
--------------------------------------------------------
ALTER TABLE "TABLE_2" MODIFY ("SITE_ID" NOT NULL ENABLE);
ALTER TABLE "TABLE_2" MODIFY ("REC_ID" NOT NULL ENABLE);
--------------------------------------------------------
-- Constraints for Table TABLE_3
--------------------------------------------------------
ALTER TABLE "TABLE_3" MODIFY ("SITE_ID" NOT NULL ENABLE);
ALTER TABLE "TABLE_3" MODIFY ("REC_ID" NOT NULL ENABLE);
THe query is as follows:
SELECT
TABLE_1.REC_ID,
TO_CHAR(table_1.REPORT_DATE, 'DD-MON-YY HH:MI:SS') report_date,
(
SELECT
MAX(table_3.NOTE_DATE) AS MAX_DATE
FROM
table_3
WHERE
table_3.REC_ID = table_1.REC_ID
AND table_3.NOTE_DATE <= table_1.REPORT_DATE
)
notes_max_date
FROM
table_1
ORDER BY
To_date(table_1.REPORT_DATE, 'DD-MON-YY HH:MI:SS')
And the output should be as follows:
REC_ID REPORT_DATE NOTES_MAX_DATE
---------------------- ------------------ -------------------------
121 17-APR-10 12:30:00
121 24-APR-10 12:30:00
121 01-MAY-10 12:30:00
121 08-MAY-10 12:30:00
121 15-MAY-10 12:30:00 12-MAY-10
121 22-MAY-10 12:30:01 17-MAY-10
121 29-MAY-10 12:30:01 25-MAY-10
121 05-JUN-10 12:30:00 25-MAY-10
8 rows selected
The output needs to be 8 rows and include the nulls in the NOTES_MAX_DATE columm. Thanks!

You could rewrite it with a LEFT JOIN and GROUP BY like this:
SELECT t1.REC_ID
,to_char(t1.REPORT_DATE, 'DD-MON-YY HH:MI:SS') AS report_date
,max(t3.NOTE_DATE) AS notes_max_date
FROM table_1 AS t1
LEFT JOIN table_3 AS t3 ON t3.REC_ID = t1.REC_ID
AND t3.NOTE_DATE <= t1.REPORT_DATE
GROUP BY t1.REC_ID
,to_char(t1.REPORT_DATE, 'DD-MON-YY HH:MI:SS')
ORDER BY to_date(t1.REPORT_DATE, 'DD-MON-YY HH:MI:SS')

Related

How can I run the SQL like "Insert into table_A where columnA = temp_table_result"?

I have two temp_tables defined as:
with temp_table_1 as (
select id from table_A where fname="john" limit 1
),
with temp_table_2 as (
select id from table_B where lname="smith" limit 1
)
These tables returns 1 row with just the id. I want to Insert a row into table_C where the column col_1 is equal to the result of temp_table_1 and the column col_2 is equal to the result of temp_table_2, and the third column (created_at) set to now:
The following fails for me:
insert into table_c (col_1, col_2, created_at)
values (temp_table_1.id, temp_table_2.id, current_timestamp at time zone 'UTC');
BEGIN;
CREATE temp TABLE table_a (
id bigint,
fname text
);
CREATE temp TABLE table_b (
id bigint,
lname text
);
CREATE temp TABLE table_c (
col_1 bigint,
col_2 bigint,
created_at timestamp
);
INSERT INTO table_a
VALUES (1, 'john');
INSERT INTO table_a
VALUES (1, 'john');
INSERT INTO table_b
VALUES (2, 'smith');
INSERT INTO table_b
VALUES (2, 'smith');
COMMIT;
WITH temp_table_1 AS (
SELECT
a.id AS a_id,
b.id AS b_id
FROM
table_a a,
table_b b
WHERE
a.fname = 'john'
AND b.lname = 'smith'
LIMIT 1)
INSERT INTO table_c (col_1, col_2, created_at)
SELECT
temp_table_1.a_id,
temp_table_1.b_id,
CURRENT_TIMESTAMP at time zone 'UTC'
FROM
temp_table_1
RETURNING
*;
That should be fairly straightforward with a cross join:
WITH temp_table_1 AS (... LIMIT 1),
WITH temp_table_2 AS (... LIMIT 1)
INSERT INTO table_c (col_1, col_2, created_at)
SELECT temp_table_1.id,
temp_table_2.id,
current_timestamp AT TIME ZONE 'UTC'
FROM temp_table_1 CROSS JOIN temp_table_2;

How to write case expression inside merge statement to print the desired result

CREATE TABLE DELIGATE_DETAILS_MAIN
( E_ID NUMBER(10,0),
COMPLETED_DATE TIMESTAMP (6),
CONSTRAINT PK_DELIGATE_DETAILS_MAIN PRIMARY KEY (E_ID));
Insert into deligate_details_main (E_ID,COMPLETED_DATE) values (1,to_timestamp('13-12-21 6:05:23.991000000 PM','DD-MM-RR fmHH12:fmMI:SSXFF AM'));
Insert into deligate_details_main (E_ID,COMPLETED_DATE) values (2,to_timestamp('13-12-21 6:05:24.019000000 PM','DD-MM-RR fmHH12:fmMI:SSXFF AM'));
Insert into deligate_details_main (E_ID,COMPLETED_DATE) values (3,to_timestamp('13-12-21 6:05:24.029000000 PM','DD-MM-RR fmHH12:fmMI:SSXFF AM'));
Insert into deligate_details_main (E_ID,COMPLETED_DATE) values (4,to_timestamp('13-12-21 10:46:00.015000000 PM','DD-MM-RR fmHH12:fmMI:SSXFF AM'));
CREATE TABLE CONTROL_MAIN
( E_ID NUMBER(10,0),
E_SPEC VARCHAR2(30 BYTE),
CONSTRAINT PK_CONTROL_MAIN PRIMARY KEY (E_ID));
Insert into CONTROL_MAIN (E_ID,E_SPEC) values (1,'SAP1');
Insert into CONTROL_MAIN (E_ID,E_SPEC) values (2,'FSAP');
Insert into CONTROL_MAIN (E_ID,E_SPEC) values (3,'SAP2');
Insert into CONTROL_MAIN (E_ID,E_SPEC) values (4,'SAP1-480');
CREATE TABLE QUESTION
( E_ID NUMBER(10,0),
QUEST VARCHAR2(30 BYTE),
CONSTRAINT PK_QUESTION PRIMARY KEY (E_ID));
Insert into QUESTION (E_ID,QUEST) values (1,'Yes');
Insert into QUESTION (E_ID,QUEST) values (2,'No');
Insert into QUESTION (E_ID,QUEST) values (3,'Yes');
Insert into QUESTION (E_ID,QUEST) values (4,'Yes');
CREATE TABLE DELIGATE_DETAILS_TRANS
( D_ID NUMBER(10,0),
E_ID NUMBER(10,0),
COMPLETED_DATE_TRANS DATE,
OWNER_DETAIL VARCHAR2(30 BYTE),
CONSTRAINT PK_DELIGATE_DETAILS_TRANS PRIMARY KEY (D_ID),
CONSTRAINT FK_E_ID FOREIGN KEY (E_ID)
REFERENCES TAM.DELIGATE_DETAILS_MAIN (E_ID));
Attempt:
MERGE INTO deligate_details_trans t USING ( SELECT
ddm.e_id,
ddm.completed_date
FROM
deligate_details_main ddm
JOIN control_main cm ON ( cm.e_id = ddm.e_id AND cm.e_spec LIKE %'SAP'% )
JOIN question q ON ( q.e_id = ddm.e_id
AND q.quest = 'Yes' )
) s
on (t.e_id = s.e_id)
when not matched then
insert (d_id,e_id, completed_date_trans, owner_detail)
values (
deligate_details_trans_sq.nextval,
s.e_id,
CAST(s.completed_date AS DATE),
--Here need to insert owner detail from control main
--If it is SAP1 or SAP2 then it will insert SAP1 or SAP2
--If it is SAP1-480 then it should insert SAP3);
Expected output:
+------+---------+-----------------------+--------------+
| D_ID | E_ID | COMPLETED_DATE_TRANS | OWNER_DETAIL |
+------+---------+-----------------------+--------------+
| 1 | 1 | 13-12-21 | SAP1 |
| 2 | 3 | 13-12-21 | SAP2 |
| 3 | 4 | 13-12-21 | SAP3 |
+------+---------+-----------------------+--------------+
For e_id 1: Based on the join condition from control_main and question table. Data should get inserted into the deligate_details_trans table and owner detail should be SAP1.
For e_id 2: Based on the join condition from control_main and question table. Data is NOT matching so it should not get inserted into the trans table.
For e_id 3: Based on the join condition from control_main and question table. Data should get inserted into the deligate_details_trans table and owner detail should be SAP2.
For e_id 4 it should check in the control main table and if it is SAP1-480 then it should insert SAP3 and for others, corresponding owner details should be inserted from the control_main table
If you want the case expression in the insert's values clause then you need to expose the control table value in the using clause:
MERGE INTO deligate_details_trans t
USING (
SELECT
ddm.e_id,
ddm.completed_date,
cm.e_spec
FROM
deligate_details_main ddm
JOIN control_main cm ON ( cm.e_id = ddm.e_id AND cm.e_spec LIKE '%SAP%' )
JOIN question q ON ( q.e_id = ddm.e_id
AND q.quest = 'Yes' )
) s
ON (t.e_id = s.e_id)
WHEN NOT MATCHED THEN INSERT (
d_id,e_id, completed_date_trans, owner_detail
)
VALUES (
deligate_details_trans_sq.nextval,
s.e_id,
CAST(s.completed_date AS DATE),
CASE s.e_spec
WHEN 'SAP1' THEN 'SAP1'
WHEN 'SAP2' THEN 'SAP2'
WHEN 'SAP1-480' THEN 'SAP3'
END
);
Alternatively move the case expression into the using clause, give it an alias, and refer to that alias in the values clause:
MERGE INTO deligate_details_trans t
USING (
SELECT
ddm.e_id,
ddm.completed_date,
CASE cm.e_spec
WHEN 'SAP1' THEN 'SAP1'
WHEN 'SAP2' THEN 'SAP2'
WHEN 'SAP1-480' THEN 'SAP3'
END AS owner_detail
FROM
deligate_details_main ddm
JOIN control_main cm ON ( cm.e_id = ddm.e_id AND cm.e_spec LIKE '%SAP%' )
JOIN question q ON ( q.e_id = ddm.e_id
AND q.quest = 'Yes' )
) s
ON (t.e_id = s.e_id)
WHEN NOT MATCHED THEN INSERT (
d_id,e_id, completed_date_trans, owner_detail
)
VALUES (
deligate_details_trans_sq.nextval,
s.e_id,
CAST(s.completed_date AS DATE),
s.owner_detail
);
db<>fiddle showing both (with %'SAP'% changed to '%SAP%', and sequence creation added).
I'm not sure why you have a timestamp in one table and a date in the other, and you don't need an explicit cast (though it doesn't hurt). But if you're doing that because you're only interested in the date part you should be aware that an Oracle date still has a time component, even if it isn't being shown in your DD-MM-YY format or db<>fiddle's default DD-MON-YY format. If you want to 'lose' the time part you can truncate the value at the day (DD) component, shown in this db<>fiddle which changes the display format so you can see the difference. But you might want to keep the time - in which case, ignore this part...

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;

Row compare and insert into log table only changed data

I'm trying to compare a global temporary table to another table and want to insert into a log table but can not seem to find the best/most efficient way to accomplish this.
Log Table
CREATE TABLE LogTable
(
Date_Time DATETIME,
Name VARCHAR2(10 CHAR),
old VARCHAR2(20 CHAR),
new VARCHAR2(20 CHAR),
)
Object Type
CREATE OR REPLACE type dbo.P_REC AS OBJECT
(
ATTR1 VARCHAR2(10 CHAR),
ATTR2 VARCHAR2(20 CHAR),
ATTR3 VARCHAR2(20 CHAR),
ATTR4 VARCHAR2(20 CHAR)
);
Collection Type
CREATE OR REPLACE type dbo.P_REC_LIST IS TABLE OF P_REC;
Stored Procedure
PROCEDURE PASSPEOPLETOORACLE(tmpCollection IN P_REC_LIST , resultCursor out sys_refcursor)
IS
BEGIN
IF tmpCollection .count > 0 THEN
INSERT INTO tmpPTbl SELECT * FROM table1; <--tmpPTbl is a copy of table1 before the merge statement.
MERGE INTO table1 MKTP
USING (
WITH tmpTBL AS
(
SELECT ADCOLL.ATTR1,
ADCOLL.ATTR2,
MV.ATTR3,
MV.ATTR4
FROM TABLE(tmpCollection) ADCOLL
LEFT JOIN materializedView MV
ON ADCOLL.ATTR1 = MV.ATTR1
)
SELECT DISTINCT COALESCE(tmpTBL.ATTR1,MKtmpTBL.ATTR1) AS ATTR1,
tmpTBL.ATTR2,
tmpTBL.ATTR3,
tmpTBL.ATTR4,
CASE WHEN tmpTBL.ATTR1 IS NULL
THEN 'Y' ELSE 'N' END
match_flag FROM tmpTBL
FULL JOIN table1 MKtmpTBL
ON MKtmpTBL.ATTR1 = tmpTBL.ATTR1
) usingTBL
ON (MKTP.ATTR1 = usingTBL.ATTR1)
WHEN MATCHED THEN
UPDATE SET MKTP.ATTR2 = usingTBL.ATTR2,
MKTP.ATTR3 = usingTBL.ATTR3,
MKTP.ATTR4 = usingTBL.ATTR4,
DELETE WHERE match_flag = 'Y'
WHEN NOT MATCHED THEN
INSERT (ATTR1)
VALUES (usingTBL.ATTR1);
END IF;
END;
Id like a way to compare the newly update records in table1 to the prior records in tmpPTbl and where the old and new values differ, insert a new row into the log table.
2019-02-14 23:59:59,jdoe,abcd,efgh would be an example of a record inserted into the log table.
tmpPTbl & table1 both have 50 columns in them & about 16k rows on average.
The best solution for you would be to create a Trigger on table Table1. So that any operation occurs on Table1 it can be logged to Logtable. See below demo:
CREATE TABLE table1
(col1 VARCHAR2(10),
col2 VARCHAR2(10),
col3 VARCHAR2(10) );
/
--Trigger
CREATE OR REPLACE TRIGGER Log_Entry before
INSERT OR
UPDATE ON table1 FOR EACH row
BEGIN
IF INSERTING THEN
INSERT INTO LogTable VALUES
(sysdate, :new.col1, :new.col2, :new.col3
);
ELSIF UPDATING THEN
INSERT INTO LogTable VALUES
(sysdate, :old.col1, :old.col2, :old.col3
);
END IF;
END;
/
Execution:
SQL> Insert into table1 values ('A','B','C');
SQL>Update table1
set col1 ='Z'
where col1 = 'A';
SQL> Merge INTO table1 tb1 USING
(SELECT 'Z' col1 , 'D' col2, 'K' col3 FROM dual
) tb2 ON (tb1.col1 = tb2.col1)
WHEN matched THEN
UPDATE SET tb1.col2=tb2.col2 WHEN NOT matched THEN
INSERT VALUES
(tb2.col1,tb2.col2,tb2.col3
);
SQL>Commit;
SQL> Select * from logtable;
DATE_TIME NAME OLD NEW
--------- ---------- -------------------- --------------------
15-FEB-19 A B C
15-FEB-19 Z B C
15-FEB-19 Z B C
Note there is no need to copy data to tmpPTbl table as well.

want query to connect 1st table 1st row and 2nd table first two column

I want to connect query to connect 1st table 1st row and 2nd table first two column
means,
Table A,
ID Date Username Password
1 19/2/2016 XYZ ******
2 19/2/2016 ABC ******
Table B,
ID Date Username City
1 19/2/2016 XYZ NYC
2 19/2/2016 ABC LA
that when I insert some data in table A's 1st row then i want to check that data is available at table B's ID,DATE
Do you mean you want to enforce referential integrity between this two tables?
In this case you need a foreign key constraint
ALTER TABLE table_a
ADD CONSTRAINT reference_table_b_fk
FOREIGN KEY (id, date)
REFERENCES table_b (id, date);
If you want to just check before performing Insert option try this:
IF EXISTS (SELECT ID FROM TableB WHERE ID=1 AND Date='19/2/2016')
// Your either insert or not query
ELSE
// Your else logic will be here
so if you insert an entry into table1 only if an equivalent entry exists in table2, here's a little script to do that:
create table table1 (id number(2), date_t1 date, username varchar2(5), password varchar2(8));
create table table2 (id number(2), date_t1 date, username varchar2(5), city varchar2(8));
insert into table1 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'ABC123');
insert into table1 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'XYZ123');
insert into table2 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'NYC');
insert into table2 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'LA');
declare
n_id number(2);
d_date date;
v_username varchar2(5);
v_password varchar2(8);
n_check number(1);
begin
n_check := 0;
-- fill the variables with data which you want to insert:
n_id := 2;
d_date := to_date('16.02.2016', 'dd.mm.yyyy');
v_username := 'ABC';
v_password := 'CCCCC';
-- check whether an entry exists in table2:
begin
select count(1)
into n_check
from table2 t2
where t2.id = n_id
and trunc(t2.date_t1) = trunc(d_date);
exception when no_data_found then n_check := 0;
end;
-- if an entry exists in table2, then insert into table1:
if n_check <> 0 then
insert into table1 (id, date_t1, username, password)
values (n_id, d_date, v_username, v_password);
end if;
end;
/
select * from table1;
select * from table2;
delete from table1;
delete from table2;