SQL - How to find all linked ids from a table - sql

I have a table that shows the relationships between people a bit like this.
id linked_id
1 2
1 3
3 4
4 1
There is no apparent order to the table or the relationships.
I'm trying to find a way to list all the ids that have any kind of link to a given id. So for examples from the table above:
id = 1 would return 1, 2, 3 and 4.
id = 2 would also return 1, 2, 3 and 4
It's an oracle database, and the query would have to be in plain SQL. Thanks for your help, this has been driving me nuts.

You could use something like this:
SELECT linked_id
FROM DATA
START WITH ID = :1
CONNECT BY NOCYCLE PRIOR ID = linked_id
OR ID = PRIOR linked_id
UNION
SELECT ID
FROM DATA
START WITH linked_id = :1
CONNECT BY NOCYCLE PRIOR ID = linked_id
OR ID = PRIOR linked_id
UNION
SELECT :1 FROM dual

Related

Nested decode statement in ORACLE SQL

I'm writing simple SQL Statement where I need to found my boss's boss. For example if there is three ranks in the column like (lil boss,boss and big boss) and I input lil boss id it need to return the the big boss id. I think that this query must be write white case or decode statements but i have struggles with that. In this code everything would run fine except if I input lil boss ID.Can I use nested decode or case statements? I think that UNION statement would be solution also .
If my table has three column:
id
status
boss_id
1
big boss
null
2
big boss
null
3
lil boss
4
4
boss
2
If I input 3 it have to return 2
select decode(status,'big boss', id,
'boss',boss_id,
'lil boss',boss_id)
from bosses
where id=123113;
I need to found my boss's boss
Use a hierarchical query:
SELECT CONNECT_BY_ROOT id AS root_id,
CONNECT_BY_ROOT status AS root_status,
id,
status
FROM bosses
WHERE LEVEL = 3
OR (LEVEL < 3 AND CONNECT_BY_ISLEAF = 1)
START WITH id = 2
CONNECT BY PRIOR boss_id = id;
Which, for the sample data:
CREATE TABLE bosses (id, status, boss_id) AS
SELECT 1, 'big boss', null FROM DUAL UNION ALL
SELECT 2, 'big boss', null FROM DUAL UNION ALL
SELECT 3, 'lil boss', 4 FROM DUAL UNION ALL
SELECT 4, 'boss', 2 FROM DUAL;
Outputs:
ROOT_ID
ROOT_STATUS
ID
STATUS
3
lil boss
2
big boss
and if you START WITH id = 2:
SELECT CONNECT_BY_ROOT id AS root_id,
CONNECT_BY_ROOT status AS root_status,
id,
status
FROM bosses
WHERE LEVEL = 3
OR (LEVEL < 3 AND CONNECT_BY_ISLEAF = 1)
START WITH id = 2
CONNECT BY PRIOR boss_id = id;
Then it outputs:
ROOT_ID
ROOT_STATUS
ID
STATUS
2
big boss
2
big boss
fiddle
I was able to come to a solution to this problem. For this purpose I used hierarchical queries and the solution of #MT0. Here is the code that not need to use level. I think it is the best and simpliest solution to the problem. Thanks for helping me!
select id as bossid,
status as boss_status
from bosses b
where b.status='big boss'
start with id ='4'
connect by prior boss_id = id;

Select all related records

I have a table (in SQL Server) that stores records as shown below. The purpose for Old_Id is for change tracking.
Meaning that when I want to update a record, the original record has to be unchanged, but a new record has to be inserted with a new Id and with updated values, and with the modified record's Id in Old_Id column
Id Name Old_Id
---------------------
1 Paul null
2 Paul 1
3 Jim null
4 Paul 2
5 Tim null
My question is:
When I search for id = 1 or 2 or 4, I want to select all related records.
In this case I want see records the following ids: 1, 2, 4
How can it be written in a stored procedure?
Even if it's bad practice to go with this, I can't change this logic because its legacy database and it's quite a large database.
Can anyone help with this?
you can do that with Recursive Common Table Expressions (CTE)
WITH cte_history AS (
SELECT
h.id,
h.name,
h.old_id
FROM
history h
WHERE old_id IS NULL
and id in (1,2,4)
UNION ALL
SELECT
e.id,
e.name,
e.old_id
FROM
history e
INNER JOIN cte_history o
ON o.id = e.old_id
)
SELECT * FROM cte_history;

How to get the root record in Oracle database

Starting from the generic node (or leaf) how to get the root node?
SELECT
ID,MSG_ID,PARENT_ID
FROM
TABLE_X
CONNECT BY PRIOR PARENT_ID = ID;
ID MSG_ID PARENT_ID
4 3 NULL
5 93bea0f71b07-4037-9009-f148fa39bb62 4
4 3 NULL
6 6f5f5d4ab1ec-4f00-8448-7a6dfa6461b2 4
4 3 NULL
7 3 NULL
8 7e0fae569637-4d29-9075-c273eb39ae8e 7
7 3 NULL
9 8a3e7485b3e8-45b1-a31d-c52fd32111c0 7
7 3 NULL
10 fcc622d5af92-4e61-8d7c-add3da359a8b 7
7 3 NULL
How to get the root msg_id?
You were on the right path, but you are missing two essential ingredients.
First, to indicate the starting point, you need to use the start with clause. Obviously, it will be something like start with id = <input value>. You didn't tell us how you will provide the input value. The most common approach is to use bind variables (best for many reasons); I named the bind variable input_id in the query below.
Second, you only want the "last" row (since you are navigating the tree in the opposite direction: towards the root, not from the root; so the root is now the "leaf" as you navigate this way). For that you can use the connect_by_isleaf pseudocolumn in the where clause.
So, the query should look like this: (note that I am only selecting the root message id, as that is all you requested; if you need more columns, include them in select)
select msg_id
from table_x
where connect_by_isleaf = 1 -- keep just the root row (leaf in this traversal)
start with id = :input_id -- to give the starting node
connect by prior parent_id = id
;
You can use a recursive query:
with cte (id, msg_id, parent_id) as (
select id, msg_id, parent_id, from mytable where id = ?
union all
select t.id, t.msg_id, t.parent_id
from mytable t
inner join cte c on c.parent_id = t.id
)
select * from cte where parent_id is null

Oracle sql with regex - getting duplicate records -

here is my table records - table name is temp.
1 | java,c,.net
2 | oracle,hadoop,ruby
Actually i am looking for data like this.
1| java
1 | c
1 | .net
2 | oracle
2| hadoop
2 | ruby
I written below query and expected result is not matching. can you please check verify my query why it is causing deplicate,
select id,
regexp_substr(liked,'[^,]+', 1, level) from
temp connect by regexp_substr(liked,'[^,]+', 1, level) is not null order by id
You want to do something like this:
SELECT id, REGEXP_SUBSTR(liked, '[^,]+', 1, LEVEL)
FROM temp
CONNECT BY REGEXP_SUBSTR(liked, '[^,]+', 1, LEVEL) IS NOT NULL
AND PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
ORDER BY id;
This way using DISTINCT is not necessary.
EDIT: Without using a random number in the CONNECT BY clause, you will get an error as Oracle will think it is an infinite loop.
To get the exact result you're looking for you need to both use DISTINCT and you need to order by LEVEL within ID:
SELECT ID, LIKED
FROM (select DISTINCT id, level, regexp_substr(liked,'[^,]+', 1, level) as liked
from temp
connect by regexp_substr(liked,'[^,]+', 1, level) is not null
order by id, level);
SQLFiddle here
Best of luck.

Hierarchical queries in Oracle

I have a table with a structure like (id, parent_id) in Oracle11g.
id parent_id
---------------
1 (null)
2 (null)
3 1
4 3
5 3
6 2
7 2
I'd like to query it to get all the lines that are hierarchically linked to each of these id, so the results should be :
root_id id parent_id
------------------------
1 3 1
1 4 3
1 5 3
2 6 2
2 7 2
3 4 3
3 5 3
I've been struggling with the connect by and start with for quite some time now, and all i can get is a fraction of the results i want with queries like :
select connect_by_root(id) root_id, id, parent_id from my-table
start with id=1
connect by prior id = parent_id
I'd like to not use any for loop to get my complete results.
Any Idea ?
Best regards,
Jérôme Lefrère
PS : edited after first answer, noticing me i had forgotten some of the results i want...
The query you posted is missing the from clause and left an underscore out of connect_by_root, but I'll assume those aren't actually the source of your problem.
The following query gives you the result you're looking for:
select * from (
select connect_by_root(id) root_id, id, parent_id
from test1
start with parent_id is null
connect by prior id = parent_id)
where root_id <> id
The central problem is that you were specifying a specific value to start from, rather that specifying a way to identify the root rows. Changing id = 1 to parent_id is null allows the entire contents of the table to be returned.
I also added the outer query to filter the root rows out of the result set, which wasn't mentioned in your question, but was shown in your desired result.
SQL Fiddle Example
Comment Response:
In the version provided, you do get descendants of id = 3, but not in such a way that 3 is the root. This is because we're starting at the absolute root. Resolving this is easy, just omit the start with clause:
SELECT *
FROM
(SELECT connect_by_root(id) root_id,
id,
parent_id
FROM test1
CONNECT BY
PRIOR id = parent_id)
WHERE root_id <> id
SQL Fiddle Example
try this:
select connect_by_root(id) root_id, id, parent_id
from your_table
start with parent_id is null
connect by prior id = parent_id
It will give you the exact result you want:
select connect_by_root(id) as root, id, parent_id
from test1
connect by prior id=parent_id
start with parent_id is not null;