我正在使用MS SqlServer 2008.我有一个表'用户'。该表具有bigint的关键字段ID。还有一个字段为varchar的字段,它编码所有用户父ID的链。 例如:
用户表:
ID | Parents
1 | null
2 | ..
3 | ..
4 | 3,2,1
此处,用户1没有父母,用户4具有父母链3-> 2-> 1。我创建了一个函数,它解析用户的Parents字段并返回结果表,其中包含bigint的用户ID。
现在我需要一个查询,它将选择和加入一些被请求用户及其父母的ID(用户和他们的父母的顺序并不重要)。我不是SQL专家,所以我能想到的就是以下内容:
WITH CTE AS(
SELECT
ID,
Parents
FROM
[Users]
WHERE
(
[Users].Name = 'John'
)
UNION ALL
SELECT
[Users].Id,
[Users].Parents
FROM [Users], CTE
WHERE
(
[Users].ID in (SELECT * FROM GetUserParents(CTE.ID, CTE.Parents) )
))
SELECT * FROM CTE
基本上它起作用了。但是这个查询的性能非常差。我相信WHERE .. IN ..这里的表达是一个瓶颈。据我了解 - 它不是仅仅加入CTE的第一个子查询(已发现用户的ID)和GetUserParents(ID用户父母的ID)的结果,而是必须枚举Users表中的所有用户并检查是否它们中的每一个都是函数结果的一部分(并且判断执行计划 - Sql Server执行结果的不同顺序以提高WHERE ... IN ..语句的性能 - 这本身就是合乎逻辑的,但总的来说我的目标不是必需的。但是这个不同的顺序需要70%的查询执行时间)。所以我想知道如何改进这个查询,或者有人可能会提出另一种方法来解决这个问题呢?
感谢您的帮助!
答案 0 :(得分:1)
问题中的递归查询看起来多余,因为您已经在GetUserParents
中形成了所需的ID列表。也许可以使用SELECT
/ Users
将此更改为GetUserParents()
WHERE
和JOIN
。
select Users.*
from Users join
(select ParentId
from (SELECT * FROM Users where Users.Name='John') as U
cross apply [GetDocumentParents](U.ID, U.Family, U.Parents))
as gup
on Users.ID = gup.ParentId
由于GetDocumentParents
期望标量和select... where
生成一个表格,我们需要将该函数应用于表格的每一行(即使我们已经"知道"有" s只有一个)。这是apply
的作用。
我使用缩进来强调查询的概念部分。 (select...) as gup
是实体Users
join
' d; (select...) as U cross apply fn()
是FROM
的参数。
理解此查询的关键知识是了解cross apply
的工作原理:
FROM
子句的一部分(非常出乎意料;因此语法位于FROM (Transact-SQL))FROM
的参数(我用缩进强调了这一点)转换是:对于每一行,它
ParentId
的列
这是cross apply
,因此只有在函数返回任何内容时才会添加行。如果这是另一种风格outer apply
,则无论如何都会添加一行,如果没有返回任何内容,则会在函数列中添加NULL
。
答案 1 :(得分:0)
这"解析"事情甚至违反了1NF。使Parents
字段仅包含直接父项(最好是外键),然后可以使用递归查询检索整个子树。