Removing orphans (SQLite 2) in dtree - sqlite2

I have SQLite 2 table:
Columns:
id, parent_id, value
1,0,'TOP FIRST' <---- top category "FIRST"
2,1,'11111-A' <----subcategory of id 1 (FIRST)
3,1,'11111-B' <----subcategory of id 1
4,0,'TOP TWO' <---- top category "TWO"
5,4,'22222-A' <----subcategory of id 4 (TWO)
6,4,'22222-B' <----subcategory of id 4
7,100,'to remove' <----- ORPHAN (There is no top category with id=100)
8,100,'to remove' <----- ORPHAN (There is no top category with id=100)
9,4,'22222-C' <----subcategory of id 4 (TWO)
How to remove orphans (id: 7, 8) from this table?

You want to delete all rows for which no parent exists. This can be done with a correlated subquery:
DELETE FROM MyTable
WHERE NOT EXISTS (SELECT 1
FROM MyTable AS T2
WHERE T2.id = MyTable.parent_id);

Related

PostgreSQL: Merging sets of rows which text fields are contained in other sets of rows

Given the following table, I need to merge the fields in different "id" only if they are the same type (person or dog), and always as the value of every field of an "id" is contained in the values of other "ids".
id
being
feature
values
1
person
name
John;Paul
1
person
surname
Smith
2
dog
name
Ringo
3
dog
name
Snowy
4
person
name
John
4
person
surname
5
person
name
John;Ringo
5
person
surname
Smith
In this example, the merge results should be as follows:
1 and 4 (Since 4's name is present in 1's name and 4's surname is empty)
1 and 5 cannot be merged (the name field show different values)
4 and 5 can be merged
2 and 3 (dogs) cannot be merged. They have only the field "name" and they do not share values.
2 and 3 cannot be merged with 1, 4, 5 since they have different values in "being".
id
being
feature
values
1
person
name
John;Paul
1
person
surname
Smith
2
dog
name
Ringo
3
dog
name
Snowy
5
person
name
John;Ringo
5
person
surname
Smith
I have tried this:
UPDATE table a
SET values = (SELECT array_to_string(array_agg(distinct values),';') AS values FROM table b
WHERE a.being= b.being
AND a.feature= b.feature
AND a.id<> b.id
AND a.values LIKE '%'||a.values||'%'
)
WHERE (select count (*) FROM (SELECT DISTINCT c.being, c.id from table c where a.being=c.being) as temp) >1
;
This doesn't work well because it will merge, for example, 1 and 5. Besides, it duplicates values when merging that field.
One option is to aggregate names with surnames on "id" and "being". Once you get a single string per "id", a self join may find when a full name is completely included inside another (where the "being" is same for both "id"s), then you just select the smallest fullname, candidate for deletion:
WITH cte AS (
SELECT id,
being,
STRING_AGG(values, ';') AS fullname
FROM tab
GROUP BY id,
being
)
DELETE FROM tab
WHERE id IN (SELECT t2.id
FROM cte t1
INNER JOIN cte t2
ON t1.being = t2.being
AND t1.id > t2.id
AND t1.fullname LIKE CONCAT('%',t2.fullname,'%'));
Check the demo here.

Finding the root node of a tree in Postgress

I am using PostgreSQL and I have data in a table like this.
id parent_id
1 NULL
2 1
3 2
4 NULL
5 4
I would like to get the following result.
id. root_id
1 NULL
2 1
3 1
4 NULL
5 4
ie for each id I would like to go up the parents until I find a parent node that does not have a parent - the ultimate ancestor so to speak.
Would be much obliged for some SQL-fu that solves this.
Thanks!
You should start from the roots and walk the tree toward the leaves:
with recursive my_tree as (
select id, parent_id, id as root
from my_table
where parent_id is null
union all
select m.id, m.parent_id, t.root
from my_table m
join my_tree t on t.id = m.parent_id
)
select id, root
from my_tree
order by id;
Note that according to the definition (a node is a root when it does not have a parent), the root of node 1 is 1, not null.
Test it in db<>fiddle.

How can I join table to delete duplicates entry in PostgreSQL

I actually have this which is working :
DELETE FROM "WU_MatchingUsers" WHERE "id" IN (SELECT "id" FROM (SELECT "id", ROW_NUMBER() OVER( PARTITION BY "IDWU_User1", "IDWU_User2" ORDER BY "id" ASC) AS row_num FROM "WU_MatchingUsers") t WHERE t.row_num >1);
This delete all duplicates entry by the more recent one in "WU_MatchingUsers" but now I have another table which is : "WU_UsersSpheres" which contain Sphere id associate with user ID.
Now I would like that my query can filter / delete only Users from a specific Spheres.
So Wu_UserSpheres look like this :
id | idSpheres | IDUser
1 1 1
2 1 2
3 2 3
4 2 4
5 2 5
So the goal is to only delete duplicate of my matching where id of the users are in a specific Spheres.

PostgreSQL: recursively join a second table

I'm struggling with recursion in PostgreSQL. I need to join a first table with a second one, and then recursively join within the second table. I looked at quite a number of examples, but most are about finding the parent records within a single table, and this has left me utterly confused.
Here's a minimal example with tables thing and category. Records in thing may or may not have a category:
id
name
category
1
a5
3
2
passat
2
3
apple
NULL
Records in category may have one or more parents in the same table:
id
name
parent_category
1
vehicle
NULL
2
car
1
3
coupe
2
The result I'm looking for is the combination of all things with their categories, as well as the category level (1 for the direct parent, 2 for the level above).
thing_name
category_name
level
a5
coupe
1
a5
car
2
a5
vehicle
3
passat
car
1
passat
vehicle
2
apple
NULL
NULL
I have a DB Fiddle here: https://www.db-fiddle.com/f/b7V8ddragZZ9x2RsMkdFYn/5
CREATE TABLE category (
id INT,
name TEXT,
parent_category INT
);
INSERT INTO category VALUES (1, 'vehicle', null);
INSERT INTO category VALUES (2, 'car', 1);
INSERT INTO category VALUES (3, 'coupe', 2);
CREATE TABLE thing (
id INT,
name TEXT,
category INT
);
INSERT INTO thing VALUES (1, 'a5', 3);
INSERT INTO thing VALUES (2, 'passat', 2);
INSERT INTO thing VALUES (3, 'apple', null);
Use a CTE to join the tables, giving you a tree-like view of combined thing_categories, which you can then use with a normal recursive CTE.
with recursive join_thing_category as (
select thing.id as thing_id,
thing.name as thing_name,
thing.category as thing_category,
category.id as category_id,
category.name as category_name,
category.parent_category as parent_category
from thing left join category on thing.category=category.id
),
recursive_part(n) as (
select thing_id, thing_name, thing_category, category_id, category_name, parent_category, (0*parent_category) + 1 as level from join_thing_category
union all
select 1, thing_name, thing_category, cat.id category_id, cat.name, cat.parent_category as parent, level+1 as level from recursive_part rp cross join category cat
where cat.id=rp.parent_category
)
select thing_name, category_name, level from recursive_part order by 1, 2, 3 limit 1024;
thing_name
category_name
level
a5
car
2
a5
coupe
1
a5
vehicle
3
apple
passat
car
1
passat
vehicle
2
View on DB Fiddle
The (0*parent_category) + 1 as level bit is so that things with no category get NULL as their level instead of 1.

How to sum items from subtable in SQL

Let's say I have table orders
id name
1 order1
2 order2
3 order3
and subtable items
id parent amount price
1 1 1 10
2 1 3 20
3 2 2 5
4 2 5 1
I would like to create query with order with added column value. it should calculate order with all relevant items
id name value
1 order1 70
2 order2 15
3 order3 0
Is this possible with TSQL
GROUP BY and SUM would do it, need to use left join and isnull as you don't have items for all orders.
SELECT o.id, o.name, isnull(sum(i.amount*i.price),0) as value
FROM orders o
left join items i
on o.id = i.parent
group by o.id, o.name
I think you're looking for something like this
SELECT o.name, i.Value FROM orders o WITH (NOLOCK)
LEFT JOIN (SELECT parent, SUM(price) AS Value FROM items WITH (NOLOCK) GROUP BY parent) i
ON o.id = i.parent
...seems like RADAR beat me to the answer.
EDIT: missing the ON line.