Selecting only rows containing at least the given elements in SQL - sql

Using PostgreSQL, and given the following sample table, how do I select all parents that have at least a child 10 and a child 20?
parent | child
--------+-------
1 | 10
1 | 20
1 | 30
2 | 10
2 | 20
3 | 10
In other words, this is the expected result:
parent
--------
1
2
In general, how do I select all parents that have at least all of the given children x1, x2, ..., xn? What is the most efficient way to do this?
Thanks!

SELECT parent FROM table WHERE child IN(10,20)
GROUP BY parent
HAVING COUNT(DISTINCT child)>=2
Fiddle

It's not completely clear what your asking. However, I shall give it a crack.
If you're going to manually define the children you can do a simple select statement:
SELECT DISTINCT parent
FROM table1
WHERE child IN ('10', '20')
This would select all Parents that have 10 or 20 as there child. To add more, just add the number to the IN() part.
If however you want to do this for a large number of children or perhaps an unknown number of children then you can create a temp table to store the children search values and join it to your main table. Something like:
CREATE TABLE #SearchChildren
(
Child int
)
Then input your search values into #SearchChildren. Need to know more about what your doing to do this bit.
SELECT DISTINCT a.parent
FROM table1 as a
JOIN #SearchChildren as s
ON a.child = s.Child
Without knowing more about what your trying to do it's difficult to give a full answer but hopefully this helps.

Related

SQL insert, select performance for categorized product table

I have relational category & product tables. Categories are hierarchical. I will have queries based on category, for example
select *
from products
where CatId = 3
or
select *
from products
where CatId = 1
I have 6 level of category and 24 million row for products, I have to find fast and optimal solutions. My question is which structure is suitable.
I write some options, feel free to suggest a better alternative.
Current category table:
Id ParentId Name
---------------------
1 null CatA
2 null CatB
3 1 CatAa
4 2 CatBa
Product table option 1
Id Cat Name
------------------
1 3 Product_1
2 4 Product_2
Product table option 2
Id CatLevel1 CatLevel2 ... Name
-------------------------------------
1 1 3 . Product_1
2 2 4 . Product_2
Product table option 3
Id Cats Name
------------------
1 1:3 Product_1
2 2:4 Product_2
Always keep option one, plus some denormalised tables (options two onwards) if you so desire. By keeping option one, you have the source truth to revert to or derive the others from.
Option two is only recommended if the searcher always knows what depth/level to search at. For example, if they know they need Level2=CATAb then it works, but if they don't know CATAb is at level two, they don't know which column to look in. It also relies on knowing how many levels to represent; if you can have a hundred levels, you need a hundred columns, and it's fragile of you need to add more depths. Generally, this doesn't apply and so is generally not a good optimisation.
Option three is a straight no. Never store multiple values in a one field (one column of one row). It will make Efficient searching of that column next to impossible.
The alternative to option three is to have a "link" table. Just two columns, category_id and product_id. Then you list all ancestors of a product, just on different rows.
category_id
product_id
1
1
3
1
2
2
4
2
These are all known as adjacency lists. A different model altogether is Nested Sets. I'm on my phone, and it's hard to describe without lots of formatting, but if you research online you'll find lots of information. They're much harder to comprehend and implement Initially, but very fast at retrieval when specifying a parent.
Your product table option 1 is fine and need no change
product_id,
category_id,
... other attributes
Your problem is in accessing the product based on the category hierarchy - which would make a need of a hierarchical query to get all categories in the tree below your selected category.
Instead of
select * from product where category_id = 1;
you'll need to write an additional hierarchical query to get the whole hierarchy tree
with cat_tree (id) as (
select id
from category where id = 1
UNION ALL
select ca.id
from cat_tree ct
join category ca
on ct.id = ca.parent_id
)
select * from product
where category_id in
(select id from cat_tree);
Which may not be practicable, but you may simplify it by denormalizing the category table
Let's assume your category data is such as
ID PARENT_ID
---------- ----------
1
3 1
5 3
6 3
The query below, which may be implemented as a MATERIALIZED VIEW that is refreshed on each category change pre-calculates all direct and indirect parent and child relations.
The result is
ID CHILD_ID
---------- ----------
1 1
1 3
1 5
1 6
3 3
3 5
3 6
5 5
6 6
E.g. for 1 you get itself, all its child's, their child's etc.
Using this category_denormobject your query can be simplified to
select *
from product
where category_id in
(select child_id from category_denorm where id = 1);

WITH RECURSIVE SELECT via secondary table

I'm having a bit of a hard time trying to piece this together. I'm not adept with databases or complex queries.
The Database
I'm using the latest MariaDB release.
I have a database table configuration like so, representing a hierarchical data structure:
|----------------------|
| fieldsets |
|----+-----------------|
| id | parent_field_id |
|----+-----------------|
| 1 | NULL |
| 2 | 1 |
|----------------------|
|-------------------------|
| fields |
|----+--------------------|
| id | parent_fieldset_id |
|----+--------------------|
| 1 | 1 |
| 2 | 1 |
|-------------------------|
The Problem
I'm trying to piece together a recursive query. I need to select every fieldset in a given hierarchy. For example, in the above, stripped-down example, I want to select fieldset of id = 1, and every descendant fieldset.
The IDs of the next rung down in any given level in the hierarchy are obtained only via columns of a secondary table.
The table fieldsets contains no column by which I can directly get all child fieldsets. I need to get all fields that are a child of a given fieldset, and then get any fieldsets that are a child of that field.
A Better Illustration of the Problem
This query does not work because of the reported error: "Restrictions imposed on recursive definitions are violated for table all_fieldsets"
However, it really illustrates what I need to do in order to get all descendant fieldsets in the hierarchy (remember, a fieldset does not contain the column for its parent fieldset, since a fieldset cannot have a fieldset as a direct parent. Instead, a fieldset has a parent_field_id which points to a row in the fields table, and that row in the fields table correspondingly has a column named parent_fieldset_id which points to a row back in the fieldsets table, which is considered the parent fieldset to a fieldset, just an indirect parent.
WITH RECURSIVE all_fieldsets AS (
SELECT fieldsets.* FROM fieldsets WHERE id = 125
UNION ALL
SELECT fieldsets.* FROM fieldsets
WHERE fieldsets.parent_field_id IN (
SELECT id FROM fields f
INNER JOIN all_fieldsets afs
WHERE f.parent_fieldset_id = afs.id
)
)
SELECT * FROM all_fieldsets
My Attempt
The query I have thus far (which does not work):
WITH RECURSIVE all_fieldsets AS (
SELECT fieldsets.* FROM fieldsets WHERE id = 125
UNION
SELECT fieldsets.* FROM fieldsets WHERE fieldsets.id IN (SELECT fs.id FROM fieldsets fs LEFT JOIN fields f ON f.id = fs.parent_field_id WHERE f.parent_fieldset_id = fieldsets.id)
)
SELECT * FROM all_fieldsets
My Research
I'm also having a hard time finding an example which fits my use-case. There's so many results for hierarchical structures that involve one table having only relations to itself, not via a secondary table, as in my case. It's difficult when you do not know the correct terms for certain concepts, and any layman explanation seems to yield too many tangential search results.
My Plea
I would be enormously grateful to all who can point out where I'm going wrong, and perhaps suggest the outline of a query that will work.
The main problem I see with your current code is that the recursive portion of the CTE (the query which appears after the union) is not selecting from the recursive CTE, when it should be. Consider this updated version:
WITH RECURSIVE all_fieldsets AS (
SELECT * FROM fieldsets WHERE id = 125
UNION ALL
SELECT f1.*
FROM fieldsets f1
INNER JOIN all_fieldsets f2
ON f1.parent_field_id = f2.id
)
SELECT *
FROM all_fieldsets;
Note that the join in the recursive portion of the CTE relates a given descendant record in fieldsets to its parent in the CTE.
I got home from work, and I just could not set this down!
But, out of that came a solution.
I highly recommend reading this answer about recursive queries to get a better idea of how they work, and what the syntax means. Quite brilliantly explained: How to select using WITH RECURSIVE clause
The Solution
WITH RECURSIVE all_fieldsets AS (
SELECT * FROM fieldsets fs
WHERE id = 59
UNION ALL
SELECT fs.* FROM fieldsets fs
INNER JOIN all_fieldsets afs
INNER JOIN fields f
ON f.parent_fieldset_id = afs.id
AND fs.parent_field_id = f.id
)
SELECT * FROM all_fieldsets
I had to use joins to get the information from the fields table, in order to get the next level in the hierarchy, and then do this recursively until there is an empty result in the recursive query.

How to concatenate field values with recursive query in postgresql?

I have a table in PostgreSQL database that contains parts of addresses in a form of a tree and looks like this:
Id | Name | ParentId
1 | London | 0
2 | Hallam Street| 1
3 | Bld 26 | 2
4 | Office 5 | 3
I would like to make a query to return an address, concatenated from all ancestor names. I need the result table to be like this:
Id | Address
1 | London
2 | London, Hallam Street
3 | London, Hallam Street, Bld 26
4 | London, Hallam Street, Bld 26, Office 5
I guess I have to use WITH RECURSIVE query, but all the examples I've found use the where clause, so I have to put WHERE name='Office 5' to get the result only for that particular row. But I need a concatenated address for each row of my initial table. How can this be done?
The trick with recursive queries is that you need to specify a seed query. This is the query that determines your root node, or the starting point to descend or ascend the tree that you are building.
The reason the WHERE clause is there is to establish the seed ID=1 or Name=Bld 26. If you want every record to have the tree ascended or descended (depending on what you specify in the unioned select), then you should just scrap the WHERE statement so all records are seeded.
Although, the example you give... you might want to start with WHERE ID=1 in the seed, write out the child ID and parent ID. Then in the Union'd SELECT join your derived Recursive table with your table from which you are selecting and join on the Derived Recursive table's Child to your table's parent.
Something like:
WITH RECURSIVE my_tree AS (
-- Seed
SELECT
ID as Child,
ParentID as Parent,
Name,
Name as Address
FROM <table>
WHERE <table>.ID = 1
UNION
-- Recursive Term
SELECT
table.id as Child,
table.parent_id as Parent,
table.name,
t.address || ', ' || table.name as Address
FROM my_tree as t
INNER JOIN <table> table ON
t.Child = table.Parent_Id
)
SELECT Child, Address from my_tree;
I've not used PostgreSQL before, so you might have to fuss a bit with the syntax, but I think this is pretty accurate for that RDBMS.

Simple SQL statement with parent/child hierarchies

It's been a while since I've needed to write SQL statements (and I don't even know if ever had enough knowledge to make this statement).
So, here's the deal. Table has two column. One is for parent id, other is for child Id.
parent_id | child_id
4 | 2
2 | 5
This is simply for saving composite parent/child hierarchies.
4, 2 line means that structure with id 4 refers to structure id 2 as a child.
2, 5 means structure with id 2 refers to structure with id 5 as a child.
And so on.
This is what I need to do:
I need to extract ALL structures, that are not referenced by any structure as a child (root structures).
What SQL (preferrably postgres) statement will accomplish that?
Finding all structures that are not a child of another structure:
select *
from YourTable
where Parent_Id not in (Select child_id from ...)
Assuming there is no scope for a grandparent, great-grandparent relationsips, I would recommend use a Left-JOIN in this case.
Somethink on the lines of:
Select * from Table
LEFt join Table on Parent_id=child_id
WHERE child_id is null
SELECT *
FROM structures
WHERE id not in ( SELECT child_id FROM Table ) AS dummy

how to query with child relations to same table and order this correctly

Take this table:
id name sub_id
---------------------------
1 A (null)
2 B (null)
3 A2 1
4 A3 1
The sub_id column is a relation to his own table, to column ID.
subid --- 0:1 --- id
Now I have the problem to make a correctly SELECT query to show that the child rows (which sub_id is not null) directly selected under his parent row. So this must be a correctly order:
1 A (null)
3 A2 1
4 A3 1
2 B (null)
A normal SELECT order the id. But how or which keyword help me to order this correctly?
JOIN isn't possible I think because I want to get all the rows separated. Because the rows will be displayed on a Gridview (ASP.Net) with EntityDataSource but the child rows must be displayed directly under his parent.
Thank you.
Look at Managing Hierarchical Data in MySQL.
Since recursion is an expensive operation because basicly you're firing multiple queries to your database you could consider using the Nested Set Model. In short you're assigning numbers to ranges in your table. It's a long article but it worth reading it. I've used it during my internship as a solution not to have 1000+ queries, But bring it down to 1 query.
Your handling 'overhead' now lies at the point of updating the table by adding, updating or deleting records. Since you then have to update all the records with a bigger 'right-value'. But when you're retrieving the data, it all goes with 1 query :)
select * from table1 order by name, sub_id will in this case return your desired result but only because the parents names and the child name are similar. If you're using SQL 2005 a recursive CTE will work:
WITH recurse (id, Name, childID, Depth)
AS
(
SELECT id, Name, ISNULL(childID, id) as id, 0 AS Depth
FROM table1 where childid is null
UNION ALL
SELECT table1.id, table1.Name, table1.childID, recurse.Depth + 1 AS Depth FROM table1
JOIN recurse ON table1.childid = recurse.id
)
SELECT * FROM recurse order by childid, depth
SELECT
*
FROM
table
ORDER BY
COALESCE(id,sub_id), id
btw, this will work only for one level.. any thing more than that requires recursive/cte function