Иерархия без CTE - получите прямых детей - PullRequest
0 голосов
/ 07 мая 2019

У меня есть таблица для активов:

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 голосов
/ 07 мая 2019

Вы можете использовать этот 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, поскольку похоже, что это выход, который вы в конечном итоге пытаетесь сгенерировать.

0 голосов
/ 07 мая 2019

В зависимости от того, что вы предоставляете, и , если Я понимаю, я думаю, вы ищете

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


ОБНОВЛЕНИЕ:

Поскольку вы ищете способ передать *Значение 1021 * представляет номер строки, используемый в 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)

Демо

0 голосов
/ 07 мая 2019

Вы можете использовать временную таблицу, чтобы достичь того, что вам нужно.

SELECT TOP (50) a.Id, a.Name, a.ParentAssetId
INTO #Assets
FROM assets a;

INSERT INTO #Assets
SELECT a2.Id, a2.Name, a2.ParentAssetId
FROM #Assets a
JOIN assets a2 ON a2.ParentAssetId = a.Id;

SELECT *
FROM #Assets;

Обратите внимание, что это не является детерминированным, поскольку при использовании TOP нет ORDER BY.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...