BOM Explosion with Blanks - sql

I have a simple BOM table that contains a parent and a child column.
Every row contains values. There are no blanks.
To bring that table into a BI-Tool I need to add blank values for the parents, like here shown:
As you can see, if an EntityKay has no parent key, then there should be a blank value.
How would you do that?
Example:
Current State:
Child Parent
4 1
5 1
6 2
7 3
8 3
9 3
Needed Result:
Child Parent
1
2
3
4 1
5 1
6 2
7 3
8 3
9 3

This query provides the missing keys, i.e. the parents, that are no childs are selected with the parent null
select distinct Parent Child, null Parent from bom
where Parent not in (select Child from bom)
order by 1;
CHILD PARENT
---------- ------
1
2
3
You must add those records (with an INSERT) to your table and probably update additionaly the description.
insert into bom (Child, Parent)
select distinct Parent Child, null Parent from bom
where Parent not in (select Child from bom)
Result
select * from bom;
CHILD PARENT
---------- ----------
4 1
5 1
6 2
7 3
8 3
9 3
2
3
1
To get the extended data without modifying the table use an UNION ALL of the ofiginal table with the query providing the missing part.
You may add also the names for the new rows using the DECODE as show below in the example
select
Child, Parent, name
from BOM union all
select distinct
Parent Child, null Parent,
decode(Parent,1,'North America',2,'Europa',3,'Asia')
from bom
where Parent not in (select Child from bom)

Related

Get all of the low-level children of a node

I am struggling with the writing of a query for PostgreSQL. The table resembles a tree and every item can have n children.
I would like to get only and all the children of the lowest level(s) (which don't have any children by themselves) of a given element of the tree.
The structure:
CREATE TABLE items
(
ikey integer NOT NULL,
description character varying(255),
parent integer,
CONSTRAINT i_pk PRIMARY KEY (ikey),
CONSTRAINT i_relation FOREIGN KEY (parent)
REFERENCES items (ikey) MATCH SIMPLE
)
Some values of the table would look like this:
1 "Products" NULL --Parent
2 "Metal" 1
3 "Nails" 2
4 "Chains" 2
5 "Bicycle Chains" 4
6 "Shimano Bicycle Chains" 5
7 "Shimano Bicycle Chains" 5
8 "7mm chain, black" 4
9 "Wood" 1
10 "Cutting Boards" 8
11 "Cutting Board Holder" 8
Most of the solutions on SO deal with not very deep trees which have just 1-2 levels. Or it is known from which parent the children are required.
I would like to select all children of "Chains" (4), which would give the following result:
6 "Shimano Bicycle Chains" 5
7 "Shimano Bicycle Chains" 5
8 "7mm chain, black" 4
To be honest, recursive queries are not my strongest skill. I had the idea already to search upside down - getting all items first which are never used as parents and then going down, but this only shifts the problem to the point again that I have to go down recursively from the given parent which seems a bit over the top.
If you want to find all children items of an item in all levels you can write a recursive CTE. For example:
with recursive
n as (
select * from items where parent = 4 -- Children of "Chains"
union all
select i.*
from n
join items i on i.parent = n.ikey
)
select * from n
Result:
ikey description parent
----- ------------------------- ------
5 Bicycle Chains 4
8 7mm Chain Black 4
6 Shimano Bicycle Chains 1 5
7 Shimano Bicycle Chains 2 5
See running example at DB Fiddle.
You do not need a recursive query. Instead, you can simply select the rows with ikeys that do not occur as parents:
select i1.* from items i join items i1 on i.ikey = i1.parent where i.parent = 4 and
not exists (select 1 from items i2 where i2.parent = i1.ikey)
Another recursive CTE, with extras.
The base key that started the recursion & level of the tree.
WITH RECURSIVE RCTE_OFFSPRING AS (
SELECT ikey as base, 0 as lvl
, ikey, description, parent
FROM items
WHERE ikey = 4
UNION ALL
SELECT cte.base, cte.lvl + 1
, itm.ikey, itm.description, itm.parent
FROM items itm
JOIN RCTE_OFFSPRING cte
ON cte.ikey = itm.parent
)
SELECT *
FROM RCTE_OFFSPRING
WHERE lvl > 0
ORDER BY base, lvl, ikey
base
lvl
ikey
description
parent
4
1
5
Bicycle Chains
4
4
1
8
7mm chain, black
4
4
2
6
Shimano Bicycle Chains
5
4
2
7
Shimano Bicycle Chains
5
db<>fiddle here

SQL products contain other

I have a table look like this
Parent Child
10 2
10 3
10 4
10 5
11 2
11 3
as you can see parent 10 also contain parent 11 and thats what I want to display in the table, I want to add to it a row with that data :
Parent Child
10 2
10 3
10 4
10 5
**10 11**
11 2
11 3
You can get parents that "contain" other parents using a self-join and aggregation:
with t as (
select t.*, count(*) over (partition by parent) as num_child
from yourtable
)
select tp2.parent, tp.parent
from t tp join
t tp2
on tp.child = tc2.child and tp.parent <> tp2.parent
group by tp.parent, tp2.parent, tp.num_child
having count(*) = tp.num_child -- all children match
(This version assumes no duplicate rows.)
You can then use insert to add these into the table.
Note: If two parents have the same children, two rows will be inserted.

How to define the number of levels in a hierarchy using sql?

My situation is the following. I have a table containing a product hierarchy. The following table is an extract of the dataset:
child parent
1 2
2 3
4 5
6 7
I want to add a column containing the depth (of the child) of the hierarchy. Something as follows:
child parent depth
1 2 2
2 3 1
4 5 1
6 7 1
How would I do that in oracle? Thank you!
Something like:
SELECT child, parent, level
FROM your_table
START WITH parent NOT IN ( SELECT child FROM your_table )
CONNECT BY parent = PRIOR child;
Outputs:
CHILD PARENT LEVEL
----- ------ -----
1 2 2
2 3 1
4 5 1
6 7 1

Selecting Records As Paired Parent Child

Records are being saved as parent-child in the same table linked with pairkey column and then even parents are divided into two legs linked with scndleg column. That is, there will be two parent legs interlinked with scndleg column and each parent will have children having seqnno of parent in pairkey column.
Look at this fiddle
I need to select one complete batch as
Both legs of interlinked parents
UNION
All children of these two parents
and then the other batch following same pattern and so on shown in the fiddle
Edit New version of the query (link to SQLFiddle at the end):
SELECT
seqnno,
narration,
pairkey,
scndleg
FROM (
SELECT p.*, LEAST(seqnno, scndleg) related_leg_min_id
FROM pairs p
)
START WITH scndleg IS NOT NULL
CONNECT BY pairkey = PRIOR seqnno AND scndleg IS NULL
ORDER BY connect_by_root(related_leg_min_id), scndleg DESC NULLS LAST, pairkey
;
Output:
SEQNNO NARRATION PAIRKEY SCNDLEG
---------- ------------------------ ---------- ----------
1 1st leg parent 1 4
4 2nd leg parent 4 1
2 1st leg child 1
3 1st leg child 1
5 2nd leg child 4
6 2nd leg child 4
7 another 1st leg parent 7 10
10 another 2nd leg parent 10 7
8 another 1st leg child 7
9 another 1st leg child 7
11 another 2nd leg child 10
12 another 2nd leg child 10
SQLFiddle

Update Multiple Tables with an Especifc Order between them

Ok, maybe is a tricky question, but I know it could happen to some of us. I will try to make it as shorter as possible.
I have 4 Tables, 1 parent table and 3 child tables. (for this example they have the same columns, in my real case the don't). Like this.
(All the columns in the 4 tables are integer)
PARENT
ID POSITION
1 1
2 1
3 1
4 1
CHILD1
IDPARENT, POSITION
1 2
1 3
1 4
1 6
2 3
2 4
3 2
3 5
CHILD2
IDPARENT, POSITION
1 5
1 7
2 2
3 3
CHILD3
IDPARENT, POSITION
4 2
4 3
3 6
All the tables have a column named position where this are the rules:
The table PARENT always have position = 1
The position number is a consecutive per parent who cannot be duplicated across all the tables. (i.e the parent 1, have children in child1, child2 and child3, but the position is consecutive 1,2,3,4,5,6,7 it doesnt get repeated across them)
The consecutive could not be consecutive in the same table (i.e. parent 1 has in child 1 the position 2 3 4 6, but the 5 and 7 are in the child 2).
A parent can and cannot have children in all the tables (It could have in child1 and child3 athe same time or just child2 or in no one).
These tables are static, it is always the parent and 3 children.
I have written a query to get the idparent with their respective childrens and position.
SELECT C.IDPARENT, '1' AS CHILD, c.POSITION
FROM Child1 c
UNION ALL
SELECT C.IDPARENT, '2' AS CHILD, c.POSITION
FROM Child2 c
UNION ALL
SELECT C.IDPARENT, '3' AS CHILD, c.POSITION
FROM Child3 c
UNION ALL
SELECT P.ID, '0' AS CHILD, P.POSITION
FROM PARENT P
ORDER BY IDPARENT, POSITION, CHILD
It gets this output with the information above.
IDPARENT CHILD POSITION
1 0 1
1 1 2
1 1 3
1 1 4
1 2 5
1 1 6
1 2 7
2 0 1
2 2 2
2 1 3
2 1 4
3 0 1
3 1 2
3 2 3
3 1 5
3 3 6
4 0 1
4 3 2
4 3 3
As you can see, the information get correctly outputed and thats what i want. But there is Bad record here
3 2 3
3 1 5
It jumps from 3 to 5 instead to 4 because sometimes in the table those records get deleted by an outside web application.
So after all of this, this is my problem what refers to the subject of this question.
How could I make a massive update to all those tables, in a proper order, that records like
3 2 3
3 1 5
3 3 6
get converted into
3 2 3
3 1 4
3 3 5
Note: In my example i just did one bad record. In my real case, i got a lot of them.
I have written that query so far, and that's the data that I want to update, but I know you cannot update massive tables, just one by one, but I dont know how to update them with the consecutive order if I just can update one table at time. Because when the next query runs to update the next table, it will have now different data.
Thanks to all of you in advance for reading this long question and for the help provided.
;with cte as
(
SELECT C.IDPARENT, c.POSITION
FROM Child1 c
UNION ALL
SELECT C.IDPARENT, c.POSITION
FROM Child2 c
UNION ALL
SELECT C.IDPARENT, c.POSITION
FROM Child3 c
UNION ALL
SELECT P.ID, P.POSITION
FROM PARENT P
)
select C.*, row_number() over(partition by IDPARENT order by POSITION) as rn
into #tmp
from cte as C
update C
set Position = T.rn
from Child1 as C
inner join #tmp as T
on C.IDParent = T.IDParent and
C.Position = T.Position
update C
set Position = T.rn
from Child2 as C
inner join #tmp as T
on C.IDParent = T.IDParent and
C.Position = T.Position
update C
set Position = T.rn
from Child3 as C
inner join #tmp as T
on C.IDParent = T.IDParent and
C.Position = T.Position
drop table #tmp
Above is the query for you. Here you can try with sample data in table variables. The syntax for the inserts is for SQL Server 2008.