oracle sql to retrieve followup dates from a preceding date - oracle

CREATE TABLE tst_tbl
(
id NUMBER,
last_name VARCHAR2 (50),
first_name VARCHAR2 (50),
dob DATE,
register_dt DATE,
register_loc VARCHAR2 (50),
visit_dt DATE,
visit_loc VARCHAR2 (50),
visit_comments VARCHAR2 (30)
);
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('02/26/2018','MM/DD/YYYY'), 'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('2/12/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('11/6/2017', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('10/23/2017','MM/DD/YYYY'), 'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('3/27/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('3/19/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('09/05/2017' ,'MM/DD/YYYY') ,'NEW YORK', to_date('9/11/2017', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('11/6/2017 ','MM/DD/YYYY'), 'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('3/19/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('9/11/2017', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('3/27/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('2/26/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('10/23/2017','MM/DD/YYYY'), 'NEW JERSEY', '');
INSERT INTO tst_tbl VALUES(1234, 'John', 'Smith', to_date('12/01/1980','MM/DD/YYYY') , to_date('2/7/2018' ,'MM/DD/YYYY') , 'NEW YORK',to_date('2/12/2018', 'MM/DD/YYYY'),'NEW JERSEY', '');
COMMIT;
I want immediate visit information(visit_dt, visit_loc, ..) following a register date.
eg:
1234, John, Smith, 12/01/1980, 09/05/2017, NEW YORK, 9/11/2017, NEW JERSEY
1234, John, Smith, 12/01/1980, 2/7/2018, NEW YORK, 2/12/2018, NEW JERSEY
I tried with the below logic to sort the register dates and visit dates and then use lead to retrieve the following date and filter only for register dates. But, i am unable to add other fields as shown above..
SELECT
dt, vst_type, register_dt, vst_dt
FROM
(
SELECT
id, dt, vst_type,
dt AS register_dt,
ROW_NUMBER () OVER
(
PARTITION BY id, dt ORDER BY
CASE
WHEN vst_type = 'REGISTER_DT' THEN 1 ELSE 2 END
)
AS vst_dt_rnum,
LEAD(dt) OVER (PARTITION BY id ORDER BY dt) AS vst_dt
FROM
(
SELECT id, register_dt AS dt, 'REGISTER_DT' vst_type FROM tst_tbl
UNION
SELECT id, visit_dt AS dt, 'VISIT_DT' vst_type FROM tst_tbl
)
)
WHERE vst_dt_rnum = 1 AND vst_type = 'REGISTER_DT'

If I understand correctly, and assuming that the ID is a unique identifier, you want to group the rows by (ID, REGISTER_DT) and from each group to keep just the rows with VISIT_DT >= REGISTER_DT, and then from each group select the earliest (oldest) row by VISIT_DT. If so, something like this should work:
select (columns you want)
from (
select t.*
, row_number() over (partition by id, register_dt
order by visit_dt) as rn
from tst_tbl t
where visit_dt >= register_dt
)
where rn = 1
;

I feel like you're overcomplicating things. Here's a way you can add an indicator if the visit date follows the register date by <30 days. If there's some reason this doesn't work for you, please edit your question and add more data (e.g. what should the output look like?)
select r.*,
case when (floor(visit_dt - register_dt) between 0 and 29)
then 'Y' else 'N'
end as less_than_30_days
from tst_tbl r;
But that's based on your sample output. Based on your query, it looks like you're trying to look for all visits which have any register date within the past 30 days (for that ID). If you'd prefer that instead, here's a simpler way to do it.
select v.*,
(select nvl(max('Y'),'N')
from tst_tbl r
where r.id = v.id
and (floor(v.visit_dt - r.register_dt) between 0 and 29))
as reg_within_30_days
from tst_tbl v;

Try this
Select id, last_name, first_name, dob, register_dt, register_loc,
visit_dt
From (
Select r.id, r.last_name, r.first_name, r.dob, r.register_dt, r.register_loc,
r.visit_dt, r.visit_loc, row_number() over (partition by r.register_dt order by r.visit_dt) r
From tst_tbl r
where register_dt < visit_dt
) x
Where x.r = 1

Related

Oracle SQL - Remove Null rows in aliased results

Hi devs I'm developing a small pro bono project that's use oracle sql but I'm not able to hide the null results.
Table Structure:
CREATE TABLE "church-members" (
ID NUMBER(10),
NAME varchar(30) NOT NULL,
LOGIN varchar(20) NOT NULL,
PASS varchar(12) NOT NULL,
REGISTER_YEAR_MONTH varchar(15) NOT NULL,
USER_SCORE NUMBER(10),
PRIMARY KEY (ID));
The queries:
INSERT INTO "church-members" VALUES
('1', 'John Doe', 'John', 'Xo8*d_d%f58*', '202204','1');
INSERT INTO "church-members" VALUES
('2', 'Mary Doe', 'Mary', 'dLoc&257dsew', '202203','2');
INSERT INTO "church-members" VALUES
('3', 'Robertson III', 'Robertson', 'koIIf59*Liu*', '202203','7');
INSERT INTO "church-members" VALUES
('4', 'Sonia MacDonald', 'Sonia', 'fYhfgtdjfi%', '202204','4');
INSERT INTO "church-members" VALUES
('5', 'Boris Johnston', 'Boris', 'do*&flddkIK%', '202201','2');
INSERT INTO "church-members" VALUES
('6', 'Ruth Henderson', 'Ruth', 'dF6%*&', '202202','2');
The Select:
SELECT
ID,
NAME,
LOGIN,
MAX(CASE WHEN REGISTER_YEAR_MONTH = '202203' THEN TO_CHAR(USER_SCORE) ELSE '' END) AS "MARCH SCORE",
MAX(CASE WHEN REGISTER_YEAR_MONTH = '202204' THEN TO_CHAR(USER_SCORE) ELSE '' END) AS "APRIL SCORE "
FROM
"church-members"
GROUP BY
ID,
NAME,
LOGIN
And the result fiddle:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=a4deac5e3eefb17dca97661552458a61
I got to this point using the information obtained in the answer from this link:
Select more than one column and remove NULL values from result
Looking at the fiddle example, the results with IDs 5 and 6 should not be showing because both are nulls.
But still null results are being shown...
Can anyone help me to solve it?
Add a HAVING clause requiring that each matching ID have at least data for at least one of the two months:
SELECT ID, NAME, LOGIN,
MAX(CASE WHEN REGISTER_YEAR_MONTH = '202203' THEN TO_CHAR(USER_SCORE) END) AS "MARCH SCORE",
MAX(CASE WHEN REGISTER_YEAR_MONTH = '202204' THEN TO_CHAR(USER_SCORE) END) AS "APRIL SCORE"
FROM "church-members"
GROUP BY ID, NAME, LOGIN
HAVING COUNT(CASE WHEN REGISTER_YEAR_MONTH IN ('202203', '202204') THEN 1 END) > 0;

ORA-01722 invalid number error message in SQL Developer

This is my table:
--Create Person Table
CREATE TABLE Person
(
Person_ID NUMBER(10) PRIMARY KEY,
First_Name NUMBER(15) NULL,
Last_Name VARCHAR2(15) NULL,
Middle_Name VARCHAR2(15) NULL,
Street_Address VARCHAR(35) NULL,
City VARCHAR2(10) NULL,
State VARCHAR2(2) NULL,
Zipcode VARCHAR2(5) NULL,
Country CHAR(2) NOT NULL,
Birth_Date DATE NOT NULL,
Gender CHAR(1) NOT NULL,
Phone VARCHAR2(10) NULL,
Email VARCHAR2(25) NULL,
Is_Patient CHAR(1) NULL,
Is_Physician CHAR(1) NULL,
Is_Employee CHAR(1) NULL,
Is_Volunteer CHAR(1) NULL
);
And I am trying to insert:
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12333', 'Victoria', 'Tirado', 'Amanda', '1951 Lane Drive', 'Bronx', 'NY', '10467', 'US', TO_DATE('8/4/1999',
'DD/MM/YYYY'),'F','7188751200', 'heyhey#gmail.com', 'N', 'N', 'N','Y');
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12444', 'Linda', 'Lewis', 'Dixon', '1366 Grey Lane', 'Bronx', 'NY', '10460', 'US', TO_DATE('7/17/1994',
'DD/MM/YYYY'),'F','7184561287', 'babygirl#gmail.com', 'N', 'N', 'N','Y');
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12555', 'Kristen', 'Wardell', 'Danielle', '8112 Noble Street', 'Bronx', 'NY', '10451', 'US', TO_DATE('10/6/1997',
'DD/MM/YYYY'),'F','7189451263', 'heygirl#gmail.com', 'N', 'N', 'N','Y');
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12666', 'Brittany', 'Edwards', 'Toni', '2264 Rosedale Lane', 'Bronx', 'NY', '10468', 'US', TO_DATE('4/21/1993',
'DD/MM/YYYY'),'F','7186552413', 'Itsme#gmail.com', 'N', 'N', 'N','Y');
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12777', 'Kristina', 'Goodwin', 'Sue', '1010 Beach Street', 'Bronx', 'NY', '10455', 'US', TO_DATE('5/11/1991',
'DD/MM/YYYY'),'F','7189478511', 'yougood#gmail.com', 'N', 'N', 'N','Y');
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12666', 'Rebecca', 'Gonzalez', 'Bianca', '8124 Elder Drive', 'Bronx', 'NY', '10474', 'US', TO_DATE('2/19/1996',
'DD/MM/YYYY'),'F','7181148792', 'artist#gmail.com', 'N', 'N', 'N','Y');
Select *
From PERSON;
But I am getting this message:
Error starting at line : 36 in command -
insert into PERSON
(Person_ID, First_Name, Last_Name, Middle_Name, Street_Address,
City, State, Zipcode, Country, Birth_Date, Gender, Phone,
Email, Is_Patient, Is_Physician, Is_Employee, Is_Volunteer)
values ('12666', 'Rebecca', 'Gonzalez', 'Bianca', '8124 Elder Drive', 'Bronx', 'NY', '10474', 'US', TO_DATE('2/19/1996',
'DD/MM/YYYY'),'F','7181148792', 'artist#gmail.com', 'N', 'N', 'N','Y')
Error report -
ORA-01722: invalid number
Is it because I accidentally made first name as Number(10) and not VarChar(10)?
Alter table person modify (first_name varchar2(15));
Then run your insert quety
Apart from the data type that you have already altered in the table, You also need to take care of the DATE.
You have used: TO_DATE('2/19/1996','DD/MM/YYYY'). It will throw an error: ORA-01843: not a valid month
It should be: TO_DATE('02/19/1996','DD/MM/YYYY')

Case and Decode

I have written a query to display the customer id, login id and sum of credit debt amount of the customer. If the customer due amount is to be debited a virtual column must be created and display as Due from customer else Due to Customer. Please help me solve the query.
I am attaching the table Structure and values along with my query.
-- Customer Table
CREATE TABLE isbs_customer_mst
(
cust_id VARCHAR2(30) NOT NULL,
login_id VARCHAR2(30) NOT NULL,
cust_nm VARCHAR2(30),
cust_addr VARCHAR2(300),
CONSTRAINT isbs_customer_mst_pk PRIMARY KEY (cust_id)
);
--Values
INSERT INTO ISBS_CUSTOMER_MST (CUST_ID, LOGIN_ID, CUST_NM, CUST_ADDR)
VALUES ('CUST0000000001', 'USER1', 'User Login ID 1', '143/1 Uthamar Gandhi Salai, Nungambakkam, Chennai - 34');
INSERT INTO ISBS_CUSTOMER_MST (CUST_ID, LOGIN_ID, CUST_NM, CUST_ADDR)
VALUES ('CUST0000000002', 'USER2', 'User Login ID 2', '143/2 Uthamar Gandhi Salai, Nungambakkam, Chennai - 34');
INSERT INTO ISBS_CUSTOMER_MST (CUST_ID, LOGIN_ID, CUST_NM, CUST_ADDR)
VALUES ('CUST0000000003', 'USER3', 'User Login ID 3', '143/3 Uthamar Gandhi Salai, Nungambakkam, Chennai - 34');
INSERT INTO ISBS_CUSTOMER_MST (CUST_ID, LOGIN_ID, CUST_NM, CUST_ADDR)
VALUES ('CUST0000000004', 'USER4', 'User Login ID 4', '143/4 Uthamar Gandhi Salai, Nungambakkam, Chennai - 34');
--Credit Debit Table
CREATE TABLE isbs_acct_ledger_det
(
acct_ledger_id VARCHAR2(30),
cust_id VARCHAR2(30),
credit_debit_amt VARCHAR2(30) NOT NULL,
credit_debit_dttm TIMESTAMP NOT NULL,
CONSTRAINT isbs_acct_ledger_det_pk PRIMARY KEY (acct_ledger_id),
CONSTRAINT isbs_acct_ledger_det_fk FOREIGN KEY (cust_id) REFERENCES
isbs_customer_mst (cust_id)
);
-- Values
INSERT INTO ISBS_ACCT_LEDGER_DET (ACCT_LEDGER_ID, CUST_ID, CREDIT_DEBIT_AMT, CREDIT_DEBIT_DTTM)
VALUES ('ACC0000000001', 'CUST0000000001', -1000.25, TO_DATE('01-10-2008 11:00:00', 'DD-MM-YYYY HH24:MI:SS'));
INSERT INTO ISBS_ACCT_LEDGER_DET (ACCT_LEDGER_ID, CUST_ID, CREDIT_DEBIT_AMT, CREDIT_DEBIT_DTTM)
VALUES ('ACC0000000002', 'CUST0000000002', -256.75, TO_DATE('01-10-2008 11:00:00', 'DD-MM-YYYY HH24:MI:SS'));
INSERT INTO ISBS_ACCT_LEDGER_DET (ACCT_LEDGER_ID, CUST_ID, CREDIT_DEBIT_AMT, CREDIT_DEBIT_DTTM)
VALUES ('ACC0000000003', 'CUST0000000002', 100.25, TO_DATE('05-10-2008 11:00:00', 'DD-MM-YYYY HH24:MI:SS'));
-- Query
SELECT c.CUST_NM
, c.LOGIN_ID
, SUM(a.CREDIT_DEBIT_AMT) "Outstanding Amt"
, CASE WHEN a.CREDIT_DEBIT_AMT <= -9999.99
THEN 'Due to Cust'
ELSE 'Due from Cust' END "Due"
FROM ISBS_CUSTOMER_MST c
JOIN ISBS_ACCT_LEDGER_DET a
ON c.CUST_ID = a.CUST_ID
GROUP BY c.CUST_NM, c.LOGIN_ID, a.CREDIT_DEBIT_AMT;
Thanks in advance
You probably need to remove a.CREDIT_DEBIT_AMT from group by and use CASE expression on SUM().
SELECT c.cust_nm,
c.login_id,
SUM(a.credit_debit_amt) "Outstanding Amt",
CASE
WHEN SUM(a.credit_debit_amt) <= -9999.99 THEN 'Due to Cust'
ELSE 'Due from Cust'
END "Due"
FROM isbs_customer_mst c
JOIN isbs_acct_ledger_det a
ON c.cust_id = a.cust_id
GROUP BY c.cust_nm,
c.login_id ;
You need to add case statement in group by clause:
SELECT c.CUST_NM , c.LOGIN_ID , SUM(a.CREDIT_DEBIT_AMT) "Outstanding Amt" , CASE WHEN a.CREDIT_DEBIT_AMT <= -9999.99 THEN 'Due to Cust' ELSE 'Due from Cust' END "Due" FROM ISBS_CUSTOMER_MST c JOIN ISBS_ACCT_LEDGER_DET a ON c.CUST_ID = a.CUST_ID GROUP BY c.CUST_NM, c.LOGIN_ID, CASE WHEN a.CREDIT_DEBIT_AMT <= -9999.99 THEN 'Due to Cust' ELSE 'Due from Cust' END "Due";
As it is the rule of aggregate functions to have all other selected attributes in group by clause
Why would you use sin function when you are dealing with sum. Sin gives the sin value of the parameter
Whereas abs returns the positive number, likewise
Select abs(-5) from dual ;
Will return 5 as output.
I dont think your requirement needs both these functions.

count from current row to last row

I have a requirement to pull the data in the below fashion.
progress_CMC_status progress_cmc_status_numbers cmc_status_numbers
Mature Draft 2 5
Review 0 3
Final Draft 0 3
Final Comment 0 3
Final Document 2 3
Archived Document 1 1
but i am getting output in different
progress_CMC_status progress_cmc_status_numbers cmc_status_numbers
Mature Draft 2 5
Review 0 0
Final Draft 0 0
Final Comment 0 0
Final Document 2 3
Archived Document 1 1
Tried the below code:
WITH sta AS
(SELECT 'Mature' "status1"
FROM DUAL
UNION
SELECT 'Review'
FROM DUAL
UNION
SELECT 'Final Draft'
FROM DUAL
UNION
SELECT 'Final Comment'
FROM DUAL
UNION
SELECT 'Final Document'
FROM DUAL
UNION
SELECT 'Archived'
FROM DUAL)
SELECT DECODE ("stat",
'Mature', 'Mature Draft',
'Review', 'Review',
'Final Draft', 'Final Draft',
'Final Comment', 'Final Comment',
'Final Document', 'Final Document',
'Archived', 'Archived Document'
) AS "progress_CMC_status",
NVL
(TO_CHAR ("progress_cmc_status_numbers"),
0
) AS "progress_cmc_status_numbers",
NVL (TO_CHAR ("cmc_status_numbers"), 0) AS "cmc_status_numbers"
FROM (SELECT "status1" AS "stat",
NVL
(TO_CHAR (NULL),
"progress_cmc_status_numbers"
) AS "progress_cmc_status_numbers",
NVL (TO_CHAR (NULL),
"cmc_status_numbers"
) AS "cmc_status_numbers"
FROM sta
LEFT JOIN
(SELECT VALUE AS "progress_CMC_status",
COUNT (*) AS "progress_cmc_status_numbers",
SUM (COUNT (*)) OVER (ORDER BY DECODE
(VALUE,
'Mature', 0,
'Review', 1,
'Final Draft', 2,
'Final Comment', 3,
'Final Document', 4,
'Archived', 5,
6
) DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS "cmc_status_numbers"
FROM ATTRIBUTES
WHERE attribute_type_id IN (
SELECT attribute_type_id
FROM attribute_types
WHERE name_display_code =
'RD')
AND VALUE IN
('Mature', 'Review', 'Final Draft',
'Final Comment', 'Final Document', 'Archived')
AND object_id IN (SELECT obj_id
FROM docs)
GROUP BY VALUE) a1 ON sta."status1" =
a1."progress_CMC_status"
)
ORDER BY DECODE ("progress_CMC_status",
'Mature Draft', 0,
'Review', 1,
'Final Draft', 2,
'Final Comment', 3,
'Final Document', 4,
'Archived Document', 5,
6
)
sample data
CREATE TABLE ATTRIBUTES
(
object_id NUMBER(4),
attribute_type_id NUMBER(4),
name_display_code VARCHAR2(5),
VALUE VARCHAR2(25)
);
begin
insert into attributes values (101,201,'RMD','A100');
insert into attributes values (102,201,'RMD','A200');
insert into attributes values (103,201,'RMD','A300');
insert into attributes values (104,200,'RD','Mature');
insert into attributes values (105,200,'RD','Mature');
insert into attributes values (106,200,'RD','Mature');
insert into attributes values (107,200,'RD','Mature');
insert into attributes values (108,200,'RD','Mature');
insert into attributes values (109,200,'RD','Archived');
insert into attributes values (110,200,'RD','Archived');
insert into attributes values (111,200,'RD','Archived');
insert into attributes values (112,200,'RD','Archived');
insert into attributes values (113,200,'RD','Mature');
insert into attributes values (114,200,'RD','Mature');
insert into attributes values (115,200,'RD','Mature');
insert into attributes values (116,200,'RD','Mature');
insert into attributes values (117,200,'RD','Final Document');
insert into attributes values (118,200,'RD','Final Document');
insert into attributes values (119,201,'RMD','A400');
insert into attributes values (120,201,'RMD','A500');
end;
/
CREATE TABLE docs
(
obj_id NUMBER(4)
);
BEGIN
INSERT INTO docs
VALUES (100);
INSERT INTO docs
VALUES (104);
INSERT INTO docs
VALUES (109);
INSERT INTO docs
VALUES (117);
INSERT INTO docs
VALUES (118);
INSERT INTO docs
VALUES (119);
INSERT INTO docs
VALUES (120);
END;
/
CREATE TABLE attribute_types
(
attribute_type_id NUMBER(4),
name_display_code VARCHAR2(5)
);
INSERT INTO attribute_types
VALUES (200, 'RD');
INSERT INTO attribute_types
VALUES (201, 'RMD');
WITH sta AS
(SELECT 'Mature' "status1" FROM DUAL UNION
SELECT 'Review' FROM DUAL UNION
SELECT 'Final Draft' FROM DUAL UNION
SELECT 'Final Comment' FROM DUAL UNION
SELECT 'Final Document' FROM DUAL UNION
SELECT 'Archived' FROM DUAL)
SELECT
DECODE ("stat",
'Mature', 'Mature Draft',
'Review', 'Review',
'Final Draft', 'Final Draft',
'Final Comment', 'Final Comment',
'Final Document', 'Final Document',
'Archived', 'Archived Document'
) AS "progress_CMC_status",
NVL("progress_cmc_status_numbers", 0) AS "progress_cmc_status_numbers",
SUM (NVL("progress_cmc_status_numbers", 0)) OVER (ORDER BY
-DECODE ("stat",
'Mature', 0,
'Review', 1,
'Final Draft', 2,
'Final Comment', 3,
'Final Document', 4,
'Archived', 5,
6)
) AS "cmc_status_numbers"
FROM
(
SELECT
"status1" AS "stat",
"progress_cmc_status_numbers"
FROM
sta
LEFT JOIN (
SELECT
VALUE AS "progress_CMC_status",
COUNT (*) AS "progress_cmc_status_numbers"
FROM ATTRIBUTES
WHERE
attribute_type_id IN (
SELECT attribute_type_id
FROM attribute_types
WHERE name_display_code = 'RD'
)
AND VALUE IN ('Mature', 'Review', 'Final Draft',
'Final Comment', 'Final Document', 'Archived')
AND object_id IN (SELECT obj_id FROM docs)
GROUP BY VALUE
) a1 ON sta."status1" = a1."progress_CMC_status"
)
ORDER BY DECODE ("progress_CMC_status",
'Mature Draft', 0,
'Review', 1,
'Final Draft', 2,
'Final Comment', 3,
'Final Document', 4,
'Archived Document', 5,
6)
fiddle

How to Duplicate multiple rows (Oracle)

I'm trying to make a Procedure that will duplicate multiple rows of a table (or only one single row) and incrementing the ID for each row insertion.
My problem is that inside my procedure I used a cursor to select the rows to duplicate, when i select all rows without WHERE condition in that cursor everything works fine.
But when i set a WHERE condition to select only one row... nothing happens
Here is my procedure
CREATE OR REPLACE PROCEDURE DuplicateEmployee (p_EmployeeID IN Employee.id%TYPE)
AS
p_New_EmployeeID Employee.id%TYPE;
CURSOR c_DuplicateEmployee IS
SELECT *
FROM Employee
WHERE Employee.id = p_EmployeeID; -- if this line is deleted all content is duplicated
row_Employee c_DuplicateEmployee%ROWTYPE;
BEGIN
FOR myEmployee IN c_DuplicateEmployee LOOP
p_New_EmployeeID := employee_seq.NEXTVAL;
INSERT INTO Employee(id, first_name, last_name, start_date, end_date, salary, city, description)
VALUES(p_New_EmployeeID, myEmployee.first_name, myEmployee.last_name, myEmployee.start_date, myEmployee.end_date, myEmployee.salary, myEmployee.city, myEmployee.description);
END LOOP;
COMMIT;
END DuplicateEmployee;
I know in this example having a procedure selecting a primary key to duplicate is pointless but in my production base it will be used to select a Foreign key.
Bellow is the code require to create a the test table and SEQUENCE I used for this procedure
CREATE TABLE Employee
(
ID VARCHAR2(4 BYTE) NOT NULL,
First_Name VARCHAR2(10 BYTE),
Last_Name VARCHAR2(10 BYTE),
Start_Date DATE,
End_Date DATE,
Salary NUMBER(8,2),
City VARCHAR2(10 BYTE),
Description VARCHAR2(15 BYTE)
);
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('01', 'Jason', 'Martin', to_date('19960725','YYYYMMDD'), to_date('20060725','YYYYMMDD'), 1234.56, 'Toronto', 'Programmer');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('02', 'Alison', 'Mathews', to_date('19760321','YYYYMMDD'), to_date('19860221','YYYYMMDD'), 6661.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('03', 'James', 'Smith', to_date('19781212','YYYYMMDD'), to_date('19900315','YYYYMMDD'), 6544.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('04', 'Celia', 'Rice', to_date('19821024','YYYYMMDD'), to_date('19990421','YYYYMMDD'), 2344.78, 'Vancouver', 'Manager');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('05', 'Robert', 'Black', to_date('19840115','YYYYMMDD'), to_date('19980808','YYYYMMDD'), 2334.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('06', 'Linda', 'Green', to_date('19870730','YYYYMMDD'), to_date('19960104','YYYYMMDD'), 4322.78, 'New York', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('07', 'David', 'Larry', to_date('19901231','YYYYMMDD'), to_date('19980212','YYYYMMDD'), 7897.78, 'New York', 'Manager');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('08', 'James', 'Cat', to_date('19960917','YYYYMMDD'), to_date('20020415','YYYYMMDD'), 1232.78, 'Vancouver', 'Tester');
Here for the Sequence that will manage Primary key (ID)
CREATE SEQUENCE "TEST"."EMPLOYEE_SEQ" MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 1 NOCACHE ORDER NOCYCLE ;
And here the code to execute the procedure
BEGIN
employeepackage.duplicateemployee(5);
END;
I really don't understand why it doesn't properly work for a single row when it's working to plicate all rows ? It there a limitation for cursors having less than 2 rows ?
Any help would be much appreciated ;)
Why do you need a cursor? You can do this with SQL directly:
INSERT INTO Employee(id, first_name, last_name,
start_date, end_date,
salary, city, description)
SELECT employee_seq.NEXTVAL, e.first_name, e.last_name,
e.start_date, e.end_date,
e.salary, e.city, e.description
FROM Employee e
WHERE e.id = p_EmployeeID;
Anyway, the actual problem is that your ID is a VARCHAR2(4), whereas you think it is a NUMBER. You actually do not have an employee with ID = 5, but you do have one with ID = '05'. So without changing anything, your procedure already works:
BEGIN
employeepackage.duplicateemployee('05');
END;
Of course, it would make sense to change the data type of ID.
Solution from Lukas if fine for my first and last table that will not need to call others PROCEDURE to duplicate multiple children, though for intermediate table I used :
PROCEDURE Duplicate_Company (p_IdCity IN City.IdCity%TYPE) AS
p_New_IdCompany Company.IdCompany%TYPE;
CURSOR c_DuplicateCompany IS
SELECT *
FROM Company c
WHERE c.IdCity = p_IdCity;
row_Company c_DuplicateCompany%ROWTYPE;
BEGIN
FOR c1 IN c_DuplicateCompany LOOP
p_New_IdCompany := company_seq.NEXTVAL;
INSERT INTO Company(IdCompany, IdCity, Name, CreationDate)
VALUES(p_New_IdCompany, c1.IdCity, c1.Name, c1.CreationDate);
-- Call the procedure to duplicate current employee
Duplicate_Employee(c1.IdCompany);
END LOOP;
END Duplicate_Company;
Is it a good approach ?

Resources