没有CTE的阶层-取得直系子女

时间:2019-05-07 18:50:14

标签: sql sql-server

我有一张资产表:

id|name|parentId

我要为资产建立的视图是:

{
 'Id': ......,
 'Name': ....,
 'ChildrenIds': []
}

我需要一个查询,以选择前50个资产及其直接子资产(因此结果可能超过50个结果)。

我有一个可以使用的CTE,但是它很慢(5秒,对parentId和id进行了索引):

WITH MyCte as
(
    SELECT TOP 50 a.Id, a.Name, a.ParentAssetId
    FROM assets a
    UNION ALL
    SELECT a2.AssetId, a2.ParentAssetId
    FROM assets a2
    INNER JOIN MyCte cte ON cte.Id = a2.ParentAssetId
)
SELECT * From MyCte;

此联接查询完成了我想要的一半。

SELECT TOP 50 a.Id, a.Name, a.ParentAssetId
FROM assets a
LEFT JOIN assets a2 ON a2.ParentAssetId = a.Id

JOIN问题,它给了我50个结果,仅此而已。我需要后代信息来构建视图。我可以进行2次查询,但我不想这样做。

有什么建议吗?

也许我有更好的方法来建立这种观点?没有50 + N的要求? 您可以将GROUP BY与STRING_AGG一起使用,但我担心大小限制。

SAMPLE DATA:

1,Site1,NULL
2,Site2,1
3,Site3,1
4,Site4,2
5,Site5,NULL

TOP 3 ORDER BY ID DESC结果将返回

1,Site1,NULL
2,Site2,1
3,Site3,1
4,Site4,2

但是我想最好是这样的:

1,Site1,NULL|2,Site2,1|3,Site3,1
2,Site2,1|4,Site4,2
3,Site3,1

3 个答案:

答案 0 :(得分:0)

您可以使用临时表来实现所需的内容。

table(dplyr::lag(a),a)
   a
    1 2 3 4 5 6 7
  1 0 0 1 1 0 0 0
  2 0 0 1 0 0 1 0
  3 1 0 0 1 0 0 1
  4 0 1 0 0 0 0 0
  5 0 0 1 0 1 0 0
  6 0 0 0 0 1 0 0
  7 0 0 0 0 0 0 1

请注意,这不是确定性的,因为使用TOP时没有ORDER BY。

答案 1 :(得分:0)

您可以使用此CTE并从中进行查看:

WITH MyCte as
(
    SELECT TOP 50 a.Id, a.Name, a.ParentAssetId
    FROM assets a
)
SELECT cte.*, a1.Id as ChildId, a1.Name as ChildName
FROM MyCte cte
INNER JOIN assets a1
 ON a1.ParentAssetId=cte.Id

诚然,这将为您提供与问题中的UNION CTE不同的结果集,但是我假设您可以对用户应用程序进行简单的调整以处理它。这样,对于应用程序来说甚至可能更容易/更有效,因为这些关系存在于行中,而不必进行推断。

也就是说,如果您使用的是最新版本的SQL Server,则可能会查看内置的JSON函数,因为看起来这是您最终尝试生成的输出。

答案 2 :(得分:0)

根据您提供的内容,如果我了解,我认为您正在寻找

WITH CTE AS
(
  SELECT TOP 3 *
  FROM T
  ORDER BY ID DESC
)
SELECT *
FROM CTE
UNION
SELECT *
FROM T
WHERE ID IN (SELECT ParentId FROM CTE);

返回:

+----+-------+----------+
| ID | Name  | ParentId |
+----+-------+----------+
|  1 | Site1 |          |
|  2 | Site2 |        1 |
|  3 | Site3 |        1 |
|  4 | Site4 |        2 |
|  5 | Site5 |          |
+----+-------+----------+

这里是 db<>fiddle


更新:

由于您正在寻找一种传递INT值的方法,该值表示TOP中使用的行号,因此您可以将内联表值函数创建为

CREATE FUNCTION dbo.MyFunction (@Rows INT = 1)
RETURNS TABLE  
AS  
RETURN   
( 
  WITH CTE AS
  (
    SELECT TOP (@Rows) *
    FROM T
    ORDER BY ID DESC
  )
    SELECT *
    FROM CTE
    UNION
    SELECT *
    FROM T
    WHERE ID IN (SELECT ParentId FROM CTE)
);

并称其为

SELECT *
FROM dbo.MyFunction(2)

Demo