Recursive SQL query with Ecto.Query - sql

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;

Related

Get rows of parents without children

I have this table:
UID
CHILD_UID
a
{}
b
{}
c
{a}
UID is a VARCHAR and CHILD_UID is a VARCHAR[]. I need a query that returns b,c. I mean a list of parents without children, checking that the uid is not in any of the child_uid rows.
a is not a parent, is a child of c.
b is a parent.
c is a parent.
I have tried this but with no results:
SELECT *
FROM table
WHERE NOT uid = ANY(SELECT child_uid FROM table);
In words, get the items that its uid is not in the child_uid column.
child_uid is an array, so it should be converted to a list of childs (using UNNEST)
SELECT * FROM table
WHERE NOT uid = ANY(SELECT unnest(child_uid) FROM table);

Recursive query for postgresSQL parent/child

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);

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)

How to get all child of a given id in SQL Server query

I have two tables in SQL Server database:
category(
itemid,
parentid
)
ArticleAssignedCategories(
categid,
artid
)
categid is a foreign key of itemid
I want to get count of artids and child of that for given itemid (child means categories with parentid of given itemid.)
For example; If given itemid = 1 and in table category have (3,1),(4,1)(5,3)
All of 3, 4, 5 are child of 1
Can anyone help me to write a good query?
Recursive queries can be done using CTE
with CTE(itemid, parentid)
as (
-- start with some category
select itemid, parentid
from category where itemid = <some_itemid>
union all
-- recursively add children
select c.itemid, c.parentid
from category c
join CTE on c.parentid = CTE.itemid
)
select count(*)
from ArticleAssignedCategories a
join CTE on CTE.itemid = a.categid
Here is the query. I hope this may help you
select b.artid,count(b.artid) from category a
inner join ArticleAssignedCategories b on a.itemid = b.artid
group by b.artid

Counting a root's children in SQL

Let's say I have a table with 3 columns:
item ID 'ID'
parent ID 'ParentID'
item name 'Title'
Now, how should I count how many children a Root has?
SELECT COUNT(*)
FROM T
WHERE ParentID = #ParentID
If you want descendants not just immediate children you would need a recursive CTE.
;WITH R AS
(
SELECT ID
FROM T
WHERE ParentID = #RootID
UNION ALL
SELECT T.ID
FROM T
JOIN R ON R.ID = T.ParentID
)
SELECT COUNT(*)
FROM R