为什么在我的嵌套集合层次结构中我的 HAVING 子句没有正确比较 int 值?
Why in my nested set hierarchy is my HAVING clause not comparing int values properly?
我正在使用嵌套集模型在 postgres 中为类别层次结构建模,如说明 here。我正在使用查询来查找节点的直接下属,该节点在以下文章的摘录中进行了解释:
Imagine you are showing a category of electronics products on a
retailer web site. When a user clicks on a category, you would want
to show the products of that category, as well as list its immediate
sub-categories, but not the entire tree of categories beneath it. For
this, we need to show the node and its immediate sub-nodes, but no
further down the tree. For example, when showing the PORTABLE
ELECTRONICS category, we will want to show MP3 PLAYERS, CD PLAYERS,
and 2 WAY RADIOS, but not FLASH.
This can be easily accomplished by adding a HAVING clause to our
previous query:
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
)AS sub_tree WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name GROUP BY node.name HAVING depth <= 1 ORDER BY node.lft;
我的架构在以下方面与文章架构略有不同:
- 我正在使用 postgres。文章正在使用 MySql.
- 我的类别定义位于单独的 table
- 我的层次结构 table 称为
category_tree
。文章称他们为nested_category
这是我的类别table定义:
(
category_id uuid NOT NULL,
name text COLLATE pg_catalog."default" NOT NULL,
description text COLLATE pg_catalog."default",
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
updated_by text COLLATE pg_catalog."default" NOT NULL,
image_url text COLLATE pg_catalog."default",
is_main boolean NOT NULL DEFAULT false,
CONSTRAINT category_category_id_pk PRIMARY KEY (category_id),
CONSTRAINT category_category_name_key UNIQUE (name)
)
这是我的类别树 table 定义:
(
category_tree_id uuid NOT NULL,
category_id uuid NOT NULL,
lft integer NOT NULL,
rgt integer NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
updated_by text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT category_tree_pkey PRIMARY KEY (category_tree_id)
)
我已将文章中的查询翻译为适合我的数据库中的架构如下:
SELECT node.category_tree_id,
node.category_id,
cat.name,
node.lft,
node.rgt,
( Count(parent.category_tree_id) - ( sub_tree.depth + 1 ) ) AS depth,
node.created_at,
node.updated_at,
node.updated_by
FROM category_tree AS node
JOIN category cat
ON cat.category_id = node.category_id,
category_tree AS parent,
category_tree AS sub_parent,
(SELECT node.category_tree_id,
( Count(parent.category_tree_id) - 1 ) AS depth
FROM category_tree AS node,
category_tree AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_tree_id = 'f47f6269-9605-459e-a319-2b0177f9f4d0' //lookup by category tree id
GROUP BY node.category_tree_id
ORDER BY node.lft) AS sub_tree
WHERE ( node.lft BETWEEN parent.lft AND parent.rgt )
AND ( node.lft BETWEEN sub_parent.lft AND sub_parent.rgt )
AND ( sub_parent.category_tree_id = sub_tree.category_tree_id )
GROUP BY node.category_tree_id,
depth,
cat.name
HAVING depth <= 1
ORDER BY node.lft
此查询没有产生任何结果,问题似乎来自 HAVING depth ...
子句。我的 category_tree
table 中有两行 depth
的值分别是 0
和 1
,如下所示:
"f47f6269-9605-459e-a319-2b0177f9f4d0", "d06a143b-523e-4136-8a17-1049abbf76f4", "Parent", 7, 10, 0
"14b939e9-6784-4905-ba24-f67768c57085", "21191930-a5b9-4868-883f-3798f29d70a3", "Child", 8, 9, 1
奇怪的是,如果我更改子句 HAVING depth > 1
我实际上得到了我期望的两个结果,这显然是错误的。
我的问题是什么?
我在 MySQL 上测试了您的查询,我发现您在外部查询中定义的 depth
别名与子查询的列 subtree.depth
.
不明确
如果我将外部查询的别名重命名为不同的名称,例如depth2
,并在 HAVING depth2 <= 1
中引用它,如您所料,我得到了“parent”和“child”的两行。
回答我自己的问题。
您不能在定义它的同一级别上使用列别名,并且只允许在使用聚合的查询中使用。如果要避免重复表达式,请使用派生的 table。 -source
完整查询:
SELECT node.category_tree_id,
node.category_id,
cat.name,
node.lft,
node.rgt,
(Count(parent.category_tree_id) - ( sub_tree.depth + 1 )) AS depth,
node.created_at,
node.updated_at,
node.updated_by
FROM category_tree AS node
JOIN category cat
ON cat.category_id = node.category_id,
category_tree AS parent,
category_tree AS sub_parent,
(SELECT node.category_tree_id,
( Count(parent.category_tree_id) - 1) AS depth
FROM category_tree AS node,
category_tree AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_tree_id = 'f47f6269-9605-459e-a319-2b0177f9f4d0'
GROUP BY node.category_tree_id
ORDER BY node.lft) AS sub_tree
WHERE (node.lft BETWEEN parent.lft AND parent.rgt)
AND (node.lft BETWEEN sub_parent.lft AND sub_parent.rgt)
AND (sub_parent.category_tree_id = sub_tree.category_tree_id)
GROUP BY node.category_tree_id,
depth,
cat.name
HAVING (Count(parent.category_tree_id) - ( sub_tree.depth + 1 )) <= 1
ORDER BY node.lft
我正在使用嵌套集模型在 postgres 中为类别层次结构建模,如说明 here。我正在使用查询来查找节点的直接下属,该节点在以下文章的摘录中进行了解释:
Imagine you are showing a category of electronics products on a retailer web site. When a user clicks on a category, you would want to show the products of that category, as well as list its immediate sub-categories, but not the entire tree of categories beneath it. For this, we need to show the node and its immediate sub-nodes, but no further down the tree. For example, when showing the PORTABLE ELECTRONICS category, we will want to show MP3 PLAYERS, CD PLAYERS, and 2 WAY RADIOS, but not FLASH.
This can be easily accomplished by adding a HAVING clause to our previous query:
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth FROM nested_category AS node, nested_category AS parent, nested_category AS sub_parent, ( SELECT node.name, (COUNT(parent.name) - 1) AS depth FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.name = 'PORTABLE ELECTRONICS' GROUP BY node.name ORDER BY node.lft )AS sub_tree WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt AND sub_parent.name = sub_tree.name GROUP BY node.name HAVING depth <= 1 ORDER BY node.lft;
我的架构在以下方面与文章架构略有不同:
- 我正在使用 postgres。文章正在使用 MySql.
- 我的类别定义位于单独的 table
- 我的层次结构 table 称为
category_tree
。文章称他们为nested_category
这是我的类别table定义:
(
category_id uuid NOT NULL,
name text COLLATE pg_catalog."default" NOT NULL,
description text COLLATE pg_catalog."default",
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
updated_by text COLLATE pg_catalog."default" NOT NULL,
image_url text COLLATE pg_catalog."default",
is_main boolean NOT NULL DEFAULT false,
CONSTRAINT category_category_id_pk PRIMARY KEY (category_id),
CONSTRAINT category_category_name_key UNIQUE (name)
)
这是我的类别树 table 定义:
(
category_tree_id uuid NOT NULL,
category_id uuid NOT NULL,
lft integer NOT NULL,
rgt integer NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
updated_by text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT category_tree_pkey PRIMARY KEY (category_tree_id)
)
我已将文章中的查询翻译为适合我的数据库中的架构如下:
SELECT node.category_tree_id,
node.category_id,
cat.name,
node.lft,
node.rgt,
( Count(parent.category_tree_id) - ( sub_tree.depth + 1 ) ) AS depth,
node.created_at,
node.updated_at,
node.updated_by
FROM category_tree AS node
JOIN category cat
ON cat.category_id = node.category_id,
category_tree AS parent,
category_tree AS sub_parent,
(SELECT node.category_tree_id,
( Count(parent.category_tree_id) - 1 ) AS depth
FROM category_tree AS node,
category_tree AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_tree_id = 'f47f6269-9605-459e-a319-2b0177f9f4d0' //lookup by category tree id
GROUP BY node.category_tree_id
ORDER BY node.lft) AS sub_tree
WHERE ( node.lft BETWEEN parent.lft AND parent.rgt )
AND ( node.lft BETWEEN sub_parent.lft AND sub_parent.rgt )
AND ( sub_parent.category_tree_id = sub_tree.category_tree_id )
GROUP BY node.category_tree_id,
depth,
cat.name
HAVING depth <= 1
ORDER BY node.lft
此查询没有产生任何结果,问题似乎来自 HAVING depth ...
子句。我的 category_tree
table 中有两行 depth
的值分别是 0
和 1
,如下所示:
"f47f6269-9605-459e-a319-2b0177f9f4d0", "d06a143b-523e-4136-8a17-1049abbf76f4", "Parent", 7, 10, 0
"14b939e9-6784-4905-ba24-f67768c57085", "21191930-a5b9-4868-883f-3798f29d70a3", "Child", 8, 9, 1
奇怪的是,如果我更改子句 HAVING depth > 1
我实际上得到了我期望的两个结果,这显然是错误的。
我的问题是什么?
我在 MySQL 上测试了您的查询,我发现您在外部查询中定义的 depth
别名与子查询的列 subtree.depth
.
如果我将外部查询的别名重命名为不同的名称,例如depth2
,并在 HAVING depth2 <= 1
中引用它,如您所料,我得到了“parent”和“child”的两行。
回答我自己的问题。
您不能在定义它的同一级别上使用列别名,并且只允许在使用聚合的查询中使用。如果要避免重复表达式,请使用派生的 table。 -source
完整查询:
SELECT node.category_tree_id,
node.category_id,
cat.name,
node.lft,
node.rgt,
(Count(parent.category_tree_id) - ( sub_tree.depth + 1 )) AS depth,
node.created_at,
node.updated_at,
node.updated_by
FROM category_tree AS node
JOIN category cat
ON cat.category_id = node.category_id,
category_tree AS parent,
category_tree AS sub_parent,
(SELECT node.category_tree_id,
( Count(parent.category_tree_id) - 1) AS depth
FROM category_tree AS node,
category_tree AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_tree_id = 'f47f6269-9605-459e-a319-2b0177f9f4d0'
GROUP BY node.category_tree_id
ORDER BY node.lft) AS sub_tree
WHERE (node.lft BETWEEN parent.lft AND parent.rgt)
AND (node.lft BETWEEN sub_parent.lft AND sub_parent.rgt)
AND (sub_parent.category_tree_id = sub_tree.category_tree_id)
GROUP BY node.category_tree_id,
depth,
cat.name
HAVING (Count(parent.category_tree_id) - ( sub_tree.depth + 1 )) <= 1
ORDER BY node.lft