Oracle get latest value based on parent/child partition - sql

I have a table named "customer" that looks like this:
ID ALPHA BRAVO CHARLIE DATE
-------------------------------------------------
1 111 222 333 02/02/2019
2 333 444 555 11/11/2019
3 666 555 777 12/12/2019
4 777 888 999 05/05/2020
5 100 101 110 12/25/2020
and I need to get the following output:
ID ALPHA BRAVO CHARLIE DATE NEW_COL ROW_NUM
-----------------------------------------------------------------------
1 111 222 333 02/02/2019 333 4
2 333 444 555 11/11/2019 333 3
3 666 555 777 12/12/2019 333 2
4 777 888 999 05/05/2020 333 1
5 100 101 110 12/25/2020 010 1
The ALPHA, BRAVO, and CHARLIE columns represent customer IDs. A given customer can have multiple IDs in the system. Records 1-4 represent IDs belonging to the same customer, let's say John. As per the table, John has 12 IDs, and his latest ID is 999. Record 5 represents another customer, let's say Jane. Jane has three IDs, and her last ID is 110.
The purpose of the ROW_NUM column is to get the last CUSTOMER.CHARLIE value. The idea is to use the first CHARLIE value as the partition. Basically, the goal is to get one parent:many children mapping. In this case, the ID 333 should be tied to 555, 777, and 999.
Here is the DDL/DML:
CREATE TABLE CUSTOMER
(ID NUMBER(20) NOT NULL,
ALPHA NUMBER(20) NOT NULL,
BRAVO NUMBER(20) NOT NULL,
CHARLIE NUMBER(20) NOT NULL,
CREATEDDATE DATE
);
INSERT INTO CUSTOMER
VALUES
(1, 111, 222, 333, to_date('02-FEB-19','DD-MON-RR'));
INSERT INTO CUSTOMER
VALUES
(2, 333, 444, 555, to_date('11-NOV-19','DD-MON-RR'));
INSERT INTO CUSTOMER
VALUES
(3, 666, 555, 777, to_date('12-DEC-19','DD-MON-RR'));
INSERT INTO CUSTOMER
VALUES
(4, 777, 888, 999, to_date('05-MAY-20','DD-MON-RR'));
INSERT INTO CUSTOMER
VALUES
(5, 100, 101, 110, to_date('25-DEC-20','DD-MON-RR'));
COMMIT;
I have tried the following query, but it fails to populate the partition column correctly:
WITH
charlies
AS
(SELECT DISTINCT charlie
FROM customer),
mult_customers
AS
(SELECT c.*, c.charlie AS NEW_COL
FROM customer c
UNION
SELECT c.*,
CASE WHEN c.alpha = e.charlie THEN c.alpha ELSE c.bravo END AS NEW_COL
FROM customer c
JOIN charlies e ON e.charlie = c.alpha OR e.charlie = c.bravo),
ranked
AS
(SELECT mc.*,
ROW_NUMBER ()
OVER (PARTITION BY NEW_COL ORDER BY createddate DESC) AS row_num
FROM mult_customers mc)
SELECT *
FROM ranked
ORDER BY ID;
Thanks for any help provided.

You task is known as connected components. I wrote about 7-8 years ago solution for this and even pl/sql package: http://orasql.org/2017/09/29/connected-components/
This PL/SQL solution is much more effective then pure SQL solutions: http://orasql.org/2014/02/28/straight-sql-vs-sql-and-plsql/
Let me know if you need help with adopting it for your task.

Related

SQL needed to find if a property exists in multiple counties

I need some SQL to determine if a property exists in multiple counties.
I have a list of distinct property ids and county ids, but I'm not sure how to find if the property exists in more than one county.
TABLE: PROPERTIES
PROPERTYID
COUNTYID
12345
1111
12345
1112
23456
1111
34567
2222
In this example, I need some sql that will only show me property 12345 since it exists in both county 1111 and 1112.
I'm sure there is some easy SQL, but I can't figure it out.
Sample data:
SQL> with properties (propertyid, countryid) as
2 (select 12345, 1111 from dual union all
3 select 12345, 1112 from dual union all
4 select 23456, 1111 from dual union all
5 select 34567, 2222 from dual
6 )
Query:
7 select propertyid
8 from properties
9 group by propertyid
10 having count(distinct countryid) > 1;
PROPERTYID
----------
12345
SQL>

More than one occurrence from table

create table customer (cif number, name varchar(20),mobile number);
insert into table customer values(121,'ANT',789);
insert into table customer values(122,'ANT',789);
insert into table customer values(123,'ENT',789);
insert into customer values(124,'ENT',789);
insert into customer values(125,'BEE',123);
insert into customer values(126,'BEE',123);
insert into customer values(127,'BRO',789);
insert into customer values(128,'FIO',789);
commit;
I want retrieve data from customer table based on name and mobile more than one occurrences.
Can anyone help me out
Result like
You can use COUNT() aggregation as Analytic function along with grouping by those columns through use of PARTITION BY clause as
SELECT cif, name, mobile
FROM (SELECT c.*,
COUNT(*) OVER (PARTITION BY name, mobile) AS cnt
FROM customer c )
WHERE cnt > 1
Demo
Source:
SQL> select * From customer;
CIF NAME MOBILE
---------- -------------------- ----------
121 ANT 789
122 ANT 789
123 ENT 789
124 ENT 789
125 BEE 123
126 BEE 123
127 BRO 789
128 FIO 789
8 rows selected.
Based on result you posted, you're looking for rows that aren't unique per name and mobile. If that's so, here's another option:
SQL> select *
2 from customer a
3 where exists (select null
4 from customer b
5 where b.name = a.name
6 and b.mobile = a.mobile
7 group by b.name, b.mobile
8 having count(*) > 1
9 );
CIF NAME MOBILE
---------- -------------------- ----------
121 ANT 789
122 ANT 789
123 ENT 789
124 ENT 789
125 BEE 123
126 BEE 123
6 rows selected.
SQL>

Combine multiple records into one based on a condition

I want to combine each product_code's (comma-separated) in a single entry/record if all other values in multiple records are the same except the product_code
The dataset looks like below-
category_id subcat_id product_code customer_id quantity value
123 456 AB 111 2 1
123 456 CD 111 2 1
123 789 AB 111 2 2
123 789 CD 111 2 2
The result should look like-
category_id subcat_id product_code customer_id quantity value
123 456 AB,CD 111 2 1
123 789 AB,CD 111 2 2
Use string_agg();
select category_id, subcat_id, customer_id, quantity, value,
string_agg(product_code, ',')
from t
group by category_id, subcat_id, customer_id, quantity, value;
That said, I recommend arrays instead of strings for storing such values.

finding manager id from employee table

I have a table data like in the below.
Emp_id Emp_name Dept_id
111 aaa 1
222 bbb 2
333 ccc 3
444 ddd 4
555 eee 5
Then i want to populate new column manager id as next emp_id from the employee table like in the below.
Emp_id Emp_name Dept_id Manager_id
111 aaa 1 222
222 bbb 2 333
333 ccc 3 444
444 ddd 4 555
555 eee 5 111
Thanks for your help in advance!
You can return the value as:
select t.*,
coalesce(lead(empid) over (order by empid),
min(empid) over ()
) as manager_id
from t;
Perhaps a select query is sufficient. Actually modifying the table is a bit tricky and the best syntax depends on the database.

t-sql update issue

tbl_indicator
grp_nbr, sect_nbr, indicat
001234 100 p
002345 101 s
tbl_group
grp_id, grp_nbr, sect_nbr, indicat
333 001987 100 a
555 001987 100 p
444 002987 101 s
222 02987 101 y
Here (in tbl_group) grp_id is the primary Key
tbl_order
order_id, grp_id
5000 333
5001 555
5002 555
5003 555
5004 444
5005 444
5006 222
In tbl_order, grp_id is a foreign Key to grp_id in tbl_group.
In table tbl_indicator, for one set of grp_nbr and sect_nbr there is an indicat, for the same set of grp_nbr and sect_nbr there is a correct indicat (555,1, 100, p) and a junk indicat (333, 1, 100, a) in table tbl_group, but both these grp_id (333, 555) are present in table tbl_orders.
And one more thing here is that the junk data (indicat) in group table (222, 02987, 101, y) the grp_nbr has one character length less than the grp_nbr in tbl_indicat. It should use something 'LIKE' operator
How can we handle this??
Now I need to update tbl_order table in such a way that the junk grp_id s should be replaced with correct grp_id s
The output should like:
tbl_orders
order_id, grp_id
5000 555
5001 555
5002 555
5003 555
5004 444
5005 444
5006 444
I already have answer if both the column data in tbl_indicat and tbl_grp tables are same...
It was answered by 'Dr. Wily's Apprentice'
sql update (help me )
but how to handle if the data is different (like missing some strings in starting)?
UPDATE o
SET o.grp_id = g2.grp_id
FROM tbl_order AS o
INNER JOIN tbl_group AS g1
ON g1.grp_id = o.grp_id
INNER JOIN tbl_group AS g2
ON g2.sect_nbr = g1.sect_nbr
AND g2.grp_nbr = '0' + g1.grp_nbr