Error using CTE in SQLITE3 - sql

I use these codes to get level(depth) of my parent/child categories table. and the isLeaf attribute:
with cteCat as (
select
id, parent,
[cteLevel] = 1
from Categories
where 1=1 and parent=0
union all
select
c.id, c.parent,
[cteLevel] = cc.cteLevel+1
from Categories c
join cteCat cc
on c.parent = cc.id
where 1=1
and c.parent <> 0
)
select
*
, CASE WHEN EXISTS (SELECT * FROM Categories c2 WHERE c2.parent = c1.id) THEN 0 ELSE 1 END AS IsLeaf
, (SELECT COUNT(*) FROM Categories c2 WHERE c2.parent = c1.id) AS Leafs
from cteCat c1
order by c1.id
result is:
id parent cteLevel IsLeaf Leafs
1 0 1 0 2
....
it's OK in SQLSERVER. but when i execute at sqlite, I get Error:
Error while executing SQL query on database 'ado':
no such column: cc.cteLevel
Any help? thanks.

SQLite does not support variable assignments like this.
However, you should be able to do the same with standard SQL:
WITH cteCat AS (
SELECT id,
parent,
1 AS cteLevel
FROM ...
WHERE ...
UNION ALL
SELECT c.id,
c.parent,
cc.cteLevel + 1
FROM ...
WHERE ...
)
SELECT ...

Related

SELECT first row in recursive part of table expression

I tried to get the first row for a WHERE statement in a recursive table expression. Sadly i'm getting this error:
The TOP or OFFSET operator is not allowed in the recursive part of a recursive common table expression 'cteTree'.
Here's my SQL Query:
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
LEFT JOIN Category AS cct ON cct.ParentCategoryID = ct.CategoryID
WHERE cct.CategoryID IS NULL
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT * FROM cteTree
The problem is in the second Case statement under UNION ALL.
SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1
Does someone know if there's another expression for selecting the first row in a recursive Table expression that works?
There are some tricks to use TOP (1) in a CTE, such as using ROW_NUMBER instead, or putting it into a TVF. But in your case you can just use a normal join:
You should also use NOT EXISTS instead of the LEFT JOIN IS NULL construct.
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
WHERE NOT EXISTS (SELECT 1
FROM Category AS cct
WHERE cct.ParentCategoryID = ct.CategoryID
)
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT *
FROM cteTree

Get all parent rows and each row followed by their child's rows

I've two tables one of them called
Main-Level
another one called
Sub-level
Sub-level has a foreign key from the Main level (the relation between them Main-Level has one or Many Sub-levels )
what I want is to create a query to show the Main-level row followed by all Sub-level rows such as below screen-shot either by native SQL query or LINQ.
Update:
I used below but the problem is it the result such as Full OUTer JOIN !
select * from Sublevel
right join Mainlevel
on Sublevel.mainlevelID=Mainlevel.id
order by coalesce(Sublevel.mainlevelID, Mainlevel.id),
(case when Sublevel.mainlevelID is null then 1 else 0 end),Mainlevel.id;
Update 2:
Also, I tried below query but with no luck :
SELECT
s.name,
s.Id,
CASE WHEN s.is_child = 1 THEN s.parentID END AS parent_id,
m.name
FROM
Mainlevel m
INNER JOIN (
SELECT id, name, parentID, 1 AS is_child
FROM Sublevel
UNION ALL
SELECT id, name,Null, 0 AS is_child
FROM Mainlevel
) s on m.id = s.mainlevelID
ORDER BY m.id,is_child, s.mainlevelID
My problem in simple language is How to make the child rows appeared below parent row
The overall plan is to have parent join (parent + child) order by (parent ID, child ID)
SELECT
c.level_id,
c.level_name,
c.level_code,
CASE WHEN c.is_child = 1 THEN c.parent_id END AS parent_id,
FROM
mainLevel p
INNER JOIN (
SELECT level_id, level_name, level_code, parent_id, 1 AS is_child
FROM subLevel
UNION ALL
SELECT level_id, level_name, level_code, level_id, 0 AS is_child
FROM mainLevel
) c on p.level_id = c.parent_id
ORDER BY p.level_id, is_child, c.level_id
Additional version to adopt to the newly clarified column availability
SELECT
w.name,
w.id,
CASE WHEN w.is_child = 1 THEN w.mid END AS parent_id
FROM
Mainlevel m
INNER JOIN (
SELECT id, name, parentID AS mid, 1 AS is_child
FROM Sublevel
UNION ALL
SELECT id, name, id AS mid, 0 AS is_child
FROM Mainlevel
) w on m.id = w.mid
ORDER BY m.id, is_child, w.id
You can use order by:
order by coalesce(parentid, id),
(case when parentid is null then 1 else 0 end),
id

SQL Get lowest level child and root node

I have database schema: [Id], [ParrentId], [some more tables]
I have hierarchy like:
1. a
2. aa
3. aaa_1
3. aaa_2
1. b
2. bb
1. c
2. cc
3. ccc_1
4. cccc
3. ccc_2
I want a (select * where X) => [X, lowest leve child] like:
[a, aaa_1] [a, aaa_2]; [cc, cccc] etc.
I can get lowest child with
SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;
but I don't know how to join it with root node.
Given:
The DBMS is SQL Server;
The highest level nodes of the tree have parent = NULL;
You want all the lowest leaves for all levels of the trees, not just the roots;
You want to have all the nodes at a lowest level, not just one;
This query would do it:
WITH r ( category_id, name, root, depth )
-- finds the root relationship
AS (
SELECT category_id, name, category_id, 0
FROM category
-- WHERE parent IS NULL -- this would only look at root nodes
UNION ALL
SELECT c.category_id, c.name, r.root, r.depth + 1
FROM r
JOIN category c
ON c.parent = r.category_id
), s ( category_id, name, root, window_id )
-- finds the lowest leaves
AS (
SELECT category_id, name, root, RANK() OVER(partition by root order by depth DESC)
FROM r
)
SELECT c.name AS NodeName, s.Name AS DeepLeafName
FROM category c
JOIN s
ON c.category_id = s.root
WHERE s.window_id = 1;
Here is the result set:
With SQL Server, you can try this :
With CTE as
(
Select ID as Child, lev = 1
from category
where ID = X
UNION ALL
Select category.ID, CTE.lev + 1
from category
inner join CTE ON category.ParentID = CTE.Child
)
select CTE_1.Child, CTE_2.Child
from CTE as CTE_1
inner join CTE as CTE_2
where CTE_1.lev = 1 AND CTE_2.lev = (select MAX(CTE.lev) from CTE)

SQL recursive query that gets all ancestors of an item

ID parent_id name
---------------------
1 2 first
2 4 second
3 3 third
4 5 fourth
5 - fifth
Ancestors list of first should be (2, 4, 5)
with name_tree as (
select id, parent_id, name
from the_unknown_table
where id = 1 -- this is the starting point you want in your recursion
union all
select c.id, c.parent_id, c.name
from the_unknown_table c
join name_tree p on p.parent_id = c.id -- this is the recursion
)
select *
from name_tree
where id <> 1; -- exclude the starting point from the overall result
SQLFiddle: http://sqlfiddle.com/#!3/87d0c/1
You can use something like this:
with parents as
(
select ID, parent_ID
from t
where parent_ID is not null
union all
select p.ID, t.parent_ID
from parents p
inner join t on p.parent_ID = t.ID
and t.parent_ID is not null
and t.ID <> t.parent_ID
)
select *
, parents = '(' + stuff
(
(
select ', ' + cast(p.parent_ID as varchar(100))
from parents p
where t.ID = p.ID
for xml path('')
), 1, 2, ''
) + ')'
from t
order by ID
SQL Fiddle with demo.
This combines two very common T-SQL techniques - using a CTE to get a hierarchy and using FOR XML PATH to get a CSV list.

Selecting Parent Rows With Multiple Conditions

I had most of this query down until a new condition arose and it has confounded me. Given the following simplified table schema:
Parent Table:
ID
FName
LName
Child Table:
[Index]
ParentID
Active_Flag
ExpirationDate
What I want to do is get Parent rows for which:
There are no children.
There are children whose Active_Flag is 1 but whose expiration dates are blank or NULL.
There are indeed children but none have the Active_Flag set to 1.
The following query came up with my first two criteria:
SELECT p.ID, p.LNAME, p.FNAME,
CASE
WHEN COUNT(ct.indx) = 0 THEN 'None'
WHEN ct.ExpirationDate is NULL or ct.ExpirationDate = '' THEN 'No expiration date'
END AS Issue
FROM ParentTable AS p
LEFT JOIN ChildTable ct
ON p.ID = ct.ParentID
GROUP BY p.ID, p.LNAME, p.FNAME, ct.[INDEX], ct.ExpirationDate
HAVING (COUNT(ct.[INDEX]) = 0) OR (ct.ExpirationDate IS NULL OR ct.ExpirationDate = '')
ORDER BY p.LNAME
I don't know how to account for #3. Any help is appreciated. Thanks in advance.
You can also do this in the HAVING clause:
SELECT p.ID, p.LNAME, p.FNAME,
(CASE WHEN COUNT(ct.indx) = 0 THEN 'None'
WHEN ct.ExpirationDate is NULL or ct.ExpirationDate = '' THEN 'No expiration date'
WHEN sum(case when ActiveFlag = 1 then 1 else 0 end) = 0 then 'No active children'
END) AS Issue
FROM ParentTable p LEFT JOIN
ChildTable c
ON p.ID = ct.ParentID
GROUP BY p.ID, p.LNAME, p.FNAME
HAVING (COUNT(ct.[INDEX]) = 0) OR
(ct.ExpirationDate IS NULL OR ct.ExpirationDate = '') or
sum(case when ActiveFlag = 1 then 1 else 0 end) = 0
ORDER BY p.LNAME
The DISTINCT in the SELECT is redundant. You do not need it with an aggregation.
You can simplify the having to "sum(ActiveFlag)" if the activeFlag is indeed an integer. If not, then it should be "= '1'" rather than "= 1'.
you can use unions for this query.....
I am not sure about this query ....but it will help you in solving your prolem
select p.id,p.lname,p.fname from parents where (p.id not in (select pid from children))
union
select p.id,p.lname,p.fname from parents p,children c inner join on c.pid=p.id where c.active_flag=1 and c.expiration_date is null or c.expiration_date=''
union
select p.id,p.lname,p.fname from parents p inner join on c.pid=p.id where c.active_flag<>1;
SELECT *
FROM parenttable pt
-- condition 1: There should be no parents without children at all
WHERE NOT EXISTS (
SELECT * FROM childtable c1
WHERE c1.parent_id = pt.id
)
-- condition 2: There should be no children with a flag but without a date
OR EXISTS (
SELECT * FROM childtable c2
WHERE c2.parent_id = pt.id
AND c2.active_flag = 1 AND c2.expire_date IS NULL
)
-- condition 3: There should at least be a child with the active_flag
OR NOT EXISTS (
SELECT * FROM childtable c3
WHERE c3.parent_id = pt.id
AND c2.active_flag = 1
)
;
-- Active flags for children c1 and c2
-- c1 c2 (X= child doesn't exists)
-----+-----+---+
-- X X rule1+rule3 # no children at all
-- 0 X rule3 # only one child
-- 1 X # idem
-- 0 0 rule 3 # two children
-- 0 1
-- 1 0
-- 1 1
--
-- , which means that rule3 implies rule1, and rule1 is redundant
-------------------------
SELECT *
FROM parenttable pt
-- condition 1+3: There should at least be a child with the active_flag
WHERE NOT EXISTS (
SELECT * FROM childtable c1
WHERE c1.parent_id = pt.id
AND c1.active_flag = 1
)
-- condition 2: There should be no children with a flag but without a date
OR EXISTS (
SELECT * FROM childtable c2
WHERE c2.parent_id = pt.id
AND c2.active_flag = 1 AND c2.expire_date IS NULL
)
;