Oracle sql recursive - sql

I have the following table:
+----+-----------+------+
| ID | Parent_ID | Name |
+----+-----------+------+
| 1 | null | A |
| 2 | null | B |
| 3 | null | C |
| 4 | 1 | D |
| 5 | 4 | E |
| 6 | 2 | F |
+----+-----------+------+
And I need to get table like this:
+----+------+
| ID | Name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | AD |
| 5 | ADE |
| 6 | BF |
+----+------+
I checked the ORACLE related questions here, but I did't find anything useful.

Try this.
SELECT ID, REPLACE (SYS_CONNECT_BY_PATH (NAME, ' '), ' ')
FROM TABLE1
START WITH PARENT_ID IS NULL
CONNECT BY PRIOR ID = PARENT_ID
ORDER BY ID;
SQL Fiddle

Try this
WITH b(ID, Parent_ID) AS (
-- Anchor member.
SELECT ID,
Parent_ID
FROM DataTable
WHERE Parent_ID IS NULL
UNION ALL
-- Recursive member.
SELECT a.ID,
a.Parent_ID
FROM DataTable a, b
WHERE a.Parent_ID = b.id
)
SELECT ID,
Parent_ID
FROM b;

Related

Count NULL values by column in SQL

Suppose I have the following table:
table
| a | b | c |
|:-----|:----|:-----|
| 1 | a | NULL |
| NULL | b | NULL |
| 3 | c | NULL |
| 4 | d | 23 |
| NULL | e | 231 |
How can I count the number of NULL values by each column?
My final result would be:
| column_name | n_nulls |
|:---------------|:----------|
| a | 2 |
| b | 0 |
| c | 3 |
You can use union all:
select 'a', count(*) - count(a) as n_nulls from t
union all
select 'b', count(*) - count(b) as n_nulls from t
union all
select 'c', count(*) - count(c) as n_nulls from t;
Redshift is a column-store database, so there probably is not a more efficient method.

Extend query to include Parent Ids

I have the following SQL statement -
SELECT
gen_id, gen_name, COUNT(*)
FROM
table
WHERE
parent_id in (1,2,3,4)
GROUP BY
gen_id, gen_name
HAVING
COUNT(*) > 1
This works a treat and brings back the following result -
+--------+----------+------------------+
| gen_id | gen_name | (No column name) |
+--------+----------+------------------+
| 1 | test1 | 2 |
| 2 | test2 | 4 |
| 6 | test6 | 2 |
| 9 | test9 | 2 |
+--------+----------+------------------+
My question being is, the only one of the above results I can determine which parents it has is test2 as it has a count for all four parent_id's. How can I modify my SQL to bring back a result set whereby I can see which parent ids belong to which gen_ids.
Results something like -
+--------+----------+-----------+
| gen_id | gen_name | parent_id |
+--------+----------+-----------+
| 1 | test1 | 1 |
| 1 | test1 | 2 |
| 2 | test2 | 1 |
| 2 | test2 | 2 |
| 2 | test2 | 3 |
| 2 | test2 | 4 |
| 6 | test6 | 3 |
| 6 | test6 | 4 |
| 9 | test9 | 2 |
| 9 | test9 | 4 |
+--------+----------+-----------+
One way to do it is using exists:
SELECT
gen_id, gen_name, parent_id
FROM
table AS t0
WHERE
parent_id in (1,2,3,4)
AND EXISTS
(
SELECT 1
FROM table AS t1
WHERE t0.gen_Id = t1.gen_id
AND t0.parent_id != t1.parent_Id
)
Another option would be to use a cte:
WITH CTE AS
(
SELECT
gen_id, gen_name, parent_id, COUNT(*) OVER(PARTITION BY gen_id) As cnt
FROM
table
WHERE
parent_id in (1,2,3,4)
)
SELECT gen_id, gen_name, parent_id
FROM CTE
WHERE cnt > 1
simple remove count and group by
SELECT
gen_id, gen_name, parent_id
FROM
table
WHERE
parent_id in (1,2,3,4)

How to Find Items that Do NOT Have a pre-Pipe "Base" Value

I have a database with a column (obj_id) in a table (parts) where I SHOULD have an obj_id of 12345 that is a set for another row that would have 12345|.
So:
select obj_id from parts where obj_id like '12345%';
12345
12345|A
12345|B
12345|77
Now, someone violated the guideline and put in some items with the piped-value but not the base value w/o the pipe (e.g. 12378|J, 12378|8 but not 12378).
I need to know how to write a SQL query to find these piped-values that do NOT have their matching base (non-piped) value in the table.
Without some realistic sample data to work with it's hard to know what you really want. Below a 2 queries that may be of assistance, but perhaps it will also make you note how useful sample data can be:
See this working at SQL Fiddle
CREATE TABLE PARTS
(id int, OBJ_ID varchar2(200))
;
INSERT ALL
INTO PARTS (id, OBJ_ID)
VALUES (1,'12345 12345|A 12345|B 12345|77')
INTO PARTS (id, OBJ_ID)
VALUES (2,'12346|A 12346|B 12346|77')
INTO PARTS (id, OBJ_ID)
VALUES (3,'12378|J, 12378|8')
INTO PARTS (id, OBJ_ID)
VALUES (4,NULL)
INTO PARTS (id, OBJ_ID)
VALUES (5,'fred. wilma, barney, betty')
SELECT * FROM dual
;
Query 1:
select
*
from PARTS p
where instr(p.OBJ_ID,' ') > instr(p.OBJ_ID,'|')
Results:
| ID | OBJ_ID |
|----|----------------------------|
| 2 | 12346|A 12346|B 12346|77 |
| 3 | 12378|J, 12378|8 |
| 5 | fred. wilma, barney, betty |
Query 2:
select
id, rn_a, regexp_substr (OBJ_ID_SPLIT, '[^|]+', 1, rn_b) as OBJ_ID_SPLIT
from (
select
p.id, c1.rn_a, regexp_substr (p.OBJ_ID, '[^ ]+', 1, c1.rn_a) as OBJ_ID_SPLIT
from PARTS p
cross join (select rownum as rn_a
from (select max(length (regexp_replace (OBJ_ID, '[^|]+'))) + 1 as mx
from PARTS
)
connect by level <= mx) c1
where p.OBJ_ID like '%|%'
) d
cross join (select 1 rn_b from dual union all select 2 from dual) c2
order by id, rn_a
Results:
| ID | RN_A | OBJ_ID_SPLIT |
|----|------|--------------|
| 1 | 1 | 12345 |
| 1 | 1 | (null) |
| 1 | 2 | 12345 |
| 1 | 2 | A |
| 1 | 3 | 12345 |
| 1 | 3 | B |
| 1 | 4 | 12345 |
| 1 | 4 | 77 |
| 2 | 1 | 12346 |
| 2 | 1 | A |
| 2 | 2 | 12346 |
| 2 | 2 | B |
| 2 | 3 | 12346 |
| 2 | 3 | 77 |
| 2 | 4 | (null) |
| 3 | 1 | 12378 |
| 3 | 1 | J, |
| 3 | 2 | 12378 |
| 3 | 2 | 8 |
| 3 | 3 | (null) |
| 3 | 4 | (null) |

pl/sql hierachical data arrangement

Table tMain:
+----+-------+---------+
| ID | name | id_ref |
+----+-------+---------+
| 1 | amine | 4 |
| .. | | |
+----+-------+---------+
Table tTree:
+----+--------+-----------+
| ID | name | id_parent |
+----+--------+-----------+
| 1 | root | null |
| 2 | child1 | 1 |
| 3 | child2 | 2 |
| 4 | child3 | 3 |
+----+--------+-----------+
So I have to tables which i would like to "connect". Table "tMain" contains some data and a foreign key referencing the tTree table (reference to the lowest child item). What I would like is to use one query to get all the data from tMain together with the assembled path from tTree. It would look like this:
+----+-------+--------+---------------------------+
| ID | name | id_ref | Path |
+----+-------+--------+---------------------------+
| 1 | amine | 4 | root/child1/child2/child3 |
| .. | | | |
+----+-------+--------+---------------------------+
WITH tree$ AS (
SELECT T.id, ltrim(sys_connect_by_path(name, '/'), '/') AS path
FROM tTree T
START WITH id_parent IS NULL
CONNECT BY PRIOR ID = id_parent
)
SELECT T.id, M.name, M.id_ref, T.path
FROM tMain M
JOIN tree$ T ON T.id = M.id_ref
;

group by top two results based on order

I have been trying to get this to work with some row_number, group by, top, sort of things, but I am missing some fundamental concept. I have a table like so:
+-------+-------+-------+
| name | ord | f_id |
+-------+-------+-------+
| a | 1 | 2 |
| b | 5 | 2 |
| c | 6 | 2 |
| d | 2 | 1 |
| e | 4 | 1 |
| a | 2 | 3 |
| c | 50 | 4 |
+-------+-------+-------+
And my desired output would be:
+-------+---------+--------+-------+
| f_id | ord_n | ord | name |
+-------+---------+--------+-------+
| 2 | 1 | 1 | a |
| 2 | 2 | 5 | b |
| 1 | 1 | 2 | d |
| 1 | 2 | 4 | e |
| 3 | 1 | 2 | a |
| 4 | 1 | 50 | c |
+-------+---------+--------+-------+
Where data is ordered by the ord value, and only up to two results per f_id. Should I be working on a Stored Procedure for this or can I just do it with SQL? I have experimented with some select TOP subqueries, but nothing has even come close..
Here are some statements to create the test table:
create table help(name varchar(255),ord tinyint,f_id tinyint);
insert into help values
('a',1,2),
('b',5,2),
('c',6,2),
('d',2,1),
('e',4,1),
('a',2,3),
('c',50,4);
You may use Rank or DENSE_RANK functions.
select A.name, A.ord_n, A.ord , A.f_id from
(
select
RANK() OVER (partition by f_id ORDER BY ord asc) AS "Rank",
ROW_NUMBER() OVER (partition by f_id ORDER BY ord asc) AS "ord_n",
help.*
from help
) A where A.rank <= 2
Sqlfiddle demo