Recursive query for postgresSQL parent/child - sql

Asking for a little help on a recursive query syntax, and of course result.
As you will see I have a table with category parent and child ( a parent can have infinite children). Querying the category dictionary (linked to a real category)
And I want to return only the last child of every category tree
Updated my code, and information
EDIT
WITH RECURSIVE cat(id) AS (
SELECT
*
FROM
category_dictionary
LEFT JOIN category_dictionary.category ON category.id
WHERE
category.parent is NOT NULL
UNION
SELECT
*
FROM
cat
LEFT JOIN cat.category ON category.id
WHERE
category.parent is NOT NULL
)
SELECT
*
FROM
cat
Table information:
Category_dictionary is a table the join category on parameter category
Category is the main table with Parent entry.
Sample data:
category_dictionary entry:
ID : name (Men) : category_id
category entries:
category_id : name : parent (null or category_id)
As a result I want all the last child of each category entries, I mean the category that doesn't have child.

A recursive query is not needed to find the deepest children. Instead, one would look at entries that are not a parent (so no other child exists). Such entries ID is not included in the parent column.
You can then join this categories to other tables
SELECT *
FROM category cat
JOIN category_dictionary cat_dic ON cat.id = cat_dic.id
WHERE NOT EXISTS
(SELECT 1 FROM category cat2
WHERE cat2.parent = cat.id);

Related

Recursive SQL query with Ecto.Query

I have a categories table with the following columns:
id
name
parent_id (nullable)
And a books table which has a category_id column inside of it.
I want a function that takes a list of category ids (category_ids) and returns a query that gets books which belong to one of the given categories or their children (recursive).
I've already written a query that returns all of a given category's children. I could use that to fetch all of the subcategories of category_ids categories and use the new list. But it would send several queries to the database and I want to do it in one query. Here's the query:
with recursive cat_tree as (
select id,
name,
parent_id
from categories
where id = $1
union all
select child.id,
child.name,
child.parent_id
from categories as child
join cat_tree as parent on parent.id = child.parent_id
)
select *
from cat_tree;
EDITED
#raw_sql """
select id,
name,
parent_id
from categories
where id in (?)
union all
select child.id,
child.name,
child.parent_id
from categories as child
join cat_tree as parent on parent.id = child.parent_id
"""
def category_search_query(query, []), do: query
def category_search_query(query, category_ids) do
query
|> recursive_ctes(true)
|> with_cte("cat_tree", as: fragment(#raw_sql, ^category_ids))
|> join(:inner, [b], c in "cat_tree", on: c.id == b.category_id)
end
But when I pass [12, 13] (for example) to the function, it gives me the following error:
(DBConnection.EncodeError) Postgrex expected an integer in -9223372036854775808..9223372036854775807, got '\f\r'. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.
But when I pass just an integer (and not a list), it works correctly.
I would make a procedure to get list of categories as parameter (can be an array) and change your query to this :
create function funcName (categoryIds int[])
returns table ( bookid int ,categoryid int , ...<deffine book columns>)
as
$$
with recursive cat_tree as (
select
id,name,parent_id
from
categories
where id in (select unnest(categoryIds))
union all
select
child.id,child.name,child.parent_id
from
categories as child
join cat_tree as parent on parent.id = child.parent_id
)
select
b.*
from
cat_tree c
join books on c.id = b.categoryid
$$ Language sql;

How do I do an SQL query based on a foreign key field?

I have the following tables:
people:
id, name
parent:
id, people_id, name
I have tried the following:
SELECT * FROM people
LEFT JOIN parent ON people.id = parent.people_id
WHERE parent.name != 'Carol';
How do I find all the people whose parent's name is not Carol?
You can try below code
select people.name from people
inner join parent on people.id=parent.people_id
where parent.name not in ('Carol')
If the two tables are to be queried by using Foreign Key.
If you want to get all records from one table that have some related entry in a second table then use Inner join
Select * from People INNER JOIN parent ON people.id = parent.people_id
WHERE parent.name <> 'Carol'
Similarly LEFT JOIN will get all records from the LEFT linked table but if you have selected some columns from the RIGHT table, if there is no related records, these columns will contain NULL
First of all, why would you need two tables? why can't you have a single table named "Person" with ID,Name,ParentID columns
Where ParentID will be optional and reference the ID if it has got parent.
And run the following query
select * from PERSON where Name not like 'Carol%' and ParentID IS NOT NULL;
SELECT * FROM people WHERE EXISTS(SELECT 1 FROM parent WHERE people_id = id AND name <> 'Carol')
First of all the table structure you have taken restrict the future growth. Like in future if you want to add parents of your parents then it wont work in this table structure.
You can do like :
id | parent_id | people_name
Here you can make parent_id null for the parent and add parent_id as id for those who have parent. Here to retrieve you have to use SELF join(join in the same table)
`
Select * from people P
INNER JOIN parent PA ON PA.people_id = P._id
where PA.name not in ('Carol')
`
Difference between INNER JOIN and LEFT OUTER JOIN
is
1) INNER JOIN bring only similar data between two table
for ex if in people table parent_id table is nullable then it will not discard the complete row,but in case of LEFT OUTER JOIN it will bring all the rows from LEFT table as well as related table from right table.with all null in right joined row..

How to query items that have full match of its children in Child table

I have 2 tables: ParentChild and Child.
ParentChild table has 2 columns PID and CID, where 1 PID can map to multiple CIDs. Child table contains distinct CIDs. The thing is CIDs in Child table doesn't necessarily cover all CIDs in ParentChild table.
Now I want to find all PIDs that each PID has all its CIDs in Child table. Examples:
P1 has 3 CIDs, if all are in Child table, P1 is selected;
P2 has 2 CIDs, if not all are in Child table, P2 is not selected.
How do I write query in a clear and performant way?
One method uses aggregation:
select pc.pid
from parentchild pc left join
child c
on pc.cid = c.cid
group by pc.pid
having count(*) = count(c.cid);
The last condition checks that all children match.

SQL Server multiple table JOIN

I have 3 tables
Table 1 [StockItem] contains information about an item, I need only the Id from this table
Table 2 [Categories] contains the categories and subcategories with their names and URL slug
Table 3 [ItemCategories] contains the categories for each item and has only two columns, CategoryId and StockItemId
I have this query which now returns the category id and combined slug as category/subcategory...
WITH categoryPath(Id, Slug)
AS
(
SELECT
Id, Slug
FROM
Categories
WHERE
ParentCategoryId IS NULL
UNION ALL
SELECT
Categories.Id
,CAST(categoryPath.Slug + '/' + categories.Slug AS NVARCHAR(150))
FROM
Categories
JOIN
categoryPath ON Categories.ParentCategoryId = categoryPath.Id
)
SELECT *
FROM ItemCategories
JOIN categoryPath ON ItemCategories.StockId = categoryPath.Id
WHERE ItemCategories.StockId = 5
The result of this query looks like this:
What I want is to add to the result the Category Name from the [Categories] table which have the column [CategoryName], but I don't know how to add another JOIN to this already complex query.
If the column is indeed in that table, then you can use this SELECT in the CTE.
You don't need another join, you just need the name column to be part of the categoryPath CTE.
SELECT Id,
,Slug
,CategoryName
FROM Categories
WHERE ParentCategoryId IS NULL
UNION ALL
SELECT Categories.Id
,CAST(categoryPath.Slug + '/' + categories.Slug AS NVARCHAR(150))
,CategoryName
FROM Categories
Don't forget to update the CTE definition with:
WITH categoryPath(Id,Slug,CategoryName)

count child records based on parent and date sql

I am tryin to find the number of child records associated to a parent. This is based on 2 columns in the same table (master_ref for parent and ref for child). The difficulty i am finding is to only count the child if they have the same date_entered as the parent. Any help would be much appreciated.
Something like the following should work for you:
select parent.master_ref, COUNT(*)
from nodes parent join
nodes child
on child.ref = parent.master_ref
where parent.date = child.date
group by parent.master_ref;
You need to do a join in order to compare values between the parent and the child.
Assuming you have 3 tables like:
CREATE TABLE Parents (ID INT, date_entered DATE);
CREATE TABLE Children (ID INT, date_entered DATE);
CREATE TABLE Relation (master_ref INT, ref INT);
Following select statement should give you what you want:
SELECT p.ID, COUNT(*)
FROM Parents p
JOIN Relation r
ON p.ID = r.master_ref
JOIN Children c
ON c.ID = r.ref
WHERE c.date_entered = p.date_entered
GROUP BY p.ID
SQLFiddle with that code (without data): http://sqlfiddle.com/#!4/6e7d3/2/0