Выберите всех родителей или детей в одном и том же контексте таблицы SQL Server

SQL-разработчики, у меня есть плохо спланированная база данных как задача, чтобы много узнать о SQL Server 2012.

SO, есть таблица Elem:

+-----------+----+---+----------+------------+
|VERSION(PK)|NAME|KEY|PARENT_KEY|DIST_KEY(FK)|
+-----------+----+---+----------+------------+
|1          |a   |12 |NULL      |1           |
+-----------+----+---+----------+------------+
|2          |b   |13 |12        |1           |
+-----------+----+---+----------+------------+
|3          |c   |14 |13        |1           |
+-----------+----+---+----------+------------+
|4          |d   |15 |12        |1           |
+-----------+----+---+----------+------------+
|5          |e   |16 |NULL      |1           |
+-----------+----+---+----------+------------+
|6          |e   |17 |NULL      |2           |
+-----------+----+---+----------+------------+

После обновления строки мне нужно проверить родительский ключ элемента, чтобы он не позволял элементу быть самоучка или что-то в этом роде.

И когда я удаляю строку, мне нужно удалить все дочерние и дочерние элементы детей и т.д.

Вопросы:

  • Как я могу выбрать все "родитель + бабушка и т.д." одного элемента DIST?

  • Как я могу выбрать всех "сыновей + внуков + и т.д." одного элемента DIST?

Я читал о решениях с CTE, но у меня нет корня элементов, и я даже не могу понять, как я могу использовать CTE.

Пожалуйста, помогите!

Спасибо.

Ответы

Ответ 1

Я столкнулся с этой проблемой, я решил проблему таким образом

 --all  "parent + grandparent + etc" @childID Replaced with the ID you need

with tbParent as
(
   select * from Elem where [KEY][email protected]
   union all
   select Elem.* from Elem  join tbParent  on Elem.[KEY]=tbParent.PARENT_KEY
)
 SELECT * FROM  tbParent
 --all "sons + grandsons + etc" @parentID Replaced with the ID you need

with tbsons as
(
  select * from Elem where [KEY][email protected]
  union all
  select Elem.* from Elem  join tbsons  on Elem.PARENT_KEY=tbsons.[KEY]
)
SELECT * FROM tbsons

PS.My Английский не очень хорошо.

Ответ 2

вот рекурсивный запрос, дающий вам обоих всех предков и всех потомков элемента. Используйте их вместе или отдельно в зависимости от ситуации. Замените предложения where, чтобы получить желаемую запись. В этом примере я ищу ключ 13 (это элемент с именем = b) и найти его предка 12/a и его потомка 14/c.

with all_ancestors(relation, version, name, elem_key, parent_key, dist_key)
as 
(
  -- the record itself
  select 'self      ' as relation, self.version, self.name, self.elem_key, self.parent_key, self.dist_key
  from elem self
  where elem_key = 13
  union all
  -- all its ancestors found recursively
  select 'ancestor  ' as relation, parent.version, parent.name, parent.elem_key, parent.parent_key, parent.dist_key
  from elem parent
  join all_ancestors child on parent.elem_key = child.parent_key
)
, all_descendants(relation, version, name, elem_key, parent_key, dist_key)
as 
(
  -- the record itself
  select 'self      ' as relation, self.version, self.name, self.elem_key, self.parent_key, self.dist_key
  from elem self
  where elem_key = 13
  union all
  -- all its descendants found recursively
  select 'descendant' as relation, child.version, child.name, child.elem_key, child.parent_key, child.dist_key
  from elem child
  join all_descendants parent on parent.elem_key = child.parent_key
)
select * from all_ancestors
union
select * from all_descendants
order by elem_key
;

Вот скрипт SQL: http://sqlfiddle.com/#!6/617ee/28.

Ответ 3

Я создал функцию для поиска родителей определенного ребенка, где вам нужно передать идентификатор дочернего элемента.

Это вернет список родителей в виде строки, разделенной запятой. Попробуйте, если это сработает для вас.

Я предполагаю, что parent_key with null value является root.

CREATE FUNCTION checkParent(@childId INT)
RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @parentId VARCHAR(MAX) = NULL
    DECLARE @parentKey INT = null
    SET @parentId = (SELECT parent_key FROM Elem WHERE [KEY] = @childId)

    WHILE(@parentKey IS NOT NULL)
    begin
        SET @parentId = @parentId +  ', ' + (SELECT parent_key FROM Elem WHERE [KEY] = @parentId)
        SET @parentKey = (SELECT parent_key FROM Elem WHERE [KEY] = @parentId)
    END
    RETURN @parentId
END
GO

Ответ 4

Я не думаю, что это можно сделать в одном выборе в любом случае, чтобы вы могли выбрать всех родителей, дедушек и бабушек,.... Один из способов сделать это - присоединиться к элем таблице для себя, и это зависит от того, сколько уровней вы присоединитесь, этот уровень детей, внуков вы получите.

Решение может быть примерно таким (для второго случая)

это выберет всех родителей, детей и внуков

Select 
parent.key as parent_key,
child.key as child_key,
grandchild.key as grandchild_key 
from elem parent 
join elem child on (elem.key=child.parentkey)
join elem grandchild on (child.key=grandchild.parentkey)
where parent.parentkey is null; -- this make you sure that first level will be parents

решение для первого случая - это просто, что вы подключите таблицы не в стиле "key = parentkey", а oposite "parentkey = key".