Как лучше всего применять одноуровневую рекурсию в таблице SQL? - PullRequest
1 голос
/ 08 января 2009

Допустим, у вас есть таблица для филиалов в вашей организации. Некоторые из них являются «основными» филиалами, а другие - спутниковыми офисами, которые сворачиваются в основной филиал. Помимо этого различия, которое влияет только на несколько вещей в системе, все ветви являются одноранговыми и имеют одинаковые атрибуты (адрес и т. Д.). Один из способов смоделировать это в виде таблицы:

CREATE TABLE Branch (
    branch_id INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    branch_name VARCHAR(80) NOT NULL,
    street VARCHAR(80) NULL,
    city VARCHAR(30) NULL,
    state CHAR(2) NULL,
    zip CHAR(5) NULL,
    is_satellite_office BIT NOT NULL DEFAULT(0),
    satellite_to_branch_id INT NULL REFERENCES Branch(branch_id)
)

Где is_satellite_office = 1, если эта запись является спутником для другой ветви, а satellite_to_branch_id указывает, к какой ветви вы относитесь, если таковая имеется.

Достаточно просто наложить ограничение на таблицу, чтобы эти два столбца согласовали любую запись:

CONSTRAINT [CK_Branch] CHECK 
  (
    (is_satellite_office = 0 AND satellite_to_branch_id IS NULL) 
    OR (is_satellite_office = 1 AND satellite_to_branch_id IS NOT NULL)
  )

Однако то, что я действительно хочу, это способ гарантировать, что эта рекурсия пойдет только на один уровень глубины ... то есть, если я указываю на ветвь как своего родителя, она не должна сам родитель, и его значение для is_satellite_office должно быть 0. Другими словами, я не хочу полностью рекурсивную древовидную структуру, я просто хочу ограничить ее одним родителем / дочерним отношением. Вот как я собираюсь написать код, и если есть способ применить его в базе данных, который не будет работать как полная чушь, я бы хотел.

Есть идеи? Я работаю над MSSQL 2005, но предпочтительны общие (не зависящие от поставщика) решения. И никакие триггеры не должны применяться, если действительно нет другого способа сделать это.

EDIT: для ясности, satellite_to_branch_id - рекурсивный указатель на другую запись в той же таблице ветвления. Я знаю, что мог бы удалить is_satellite_office BIT и полагаться на IsNull(satellite_to_branch_id), чтобы дать мне ту же информацию, но я нахожу, что это немного яснее, чтобы быть явным, и, кроме того, это не суть вопроса. Я действительно ищу чистый способ ограничения SQL, чтобы предотвратить глубину рекурсии больше 1.

Ответы [ 4 ]

1 голос
/ 08 января 2009

Вы можете связать проверочное ограничение с возвращаемым значением UDF. Создайте UDF, который принимает включенные столбцы в качестве входных параметров, а затем проверьте желаемое состояние с помощью выбора в UDF.

1 голос
/ 08 января 2009

Разрешено ли вам ссылаться на хранимую процедуру в вашем ограничении? Вы можете в PostgreSQL, так что я был бы удивлен, если бы 2005 не допустил этого.

1 голос
/ 08 января 2009

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

0 голосов
/ 08 января 2009

А как насчет этой немного другой структуры?

CREATE TABLE Branch (
    branch_id INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    branch_name VARCHAR(80) NOT NULL,
    street VARCHAR(80) NULL,
    city VARCHAR(30) NULL,
    state CHAR(2) NULL,
    zip CHAR(5) NULL,
    parent_id int NULL
)

PARENT_ID будет просто указывать на BRANCH_ID другой записи в той же таблице. Если он нулевой, то вы знаете, что у него нет родителей.

Затем, чтобы получить один уровень рекурсии, вы можете просто присоединить таблицу к себе один раз, например:

SELECT
  PARENT.BRANCH_NAME AS PARENT_BRANCH
 ,CHILD.BRANCH_NAME AS CHILD_BRANCH
FROM
  BRANCH PARENT
 ,BRANCH CHILD
WHERE CHILD.PARENT_ID PARENT.BRANCH_ID

Если вы хотите применить один уровень глубины в вашем дереве, создайте триггер при вставке / обновлении, который вызовет исключение, если этот запрос что-либо возвращает.

SELECT *
FROM
  BRANCH B1
 ,BRANCH B2
 ,BRANCH B3
WHERE B1.PARENT_ID = :NEW.NEW_PARENT_ID
  AND B2.PARENT_ID = B1.BRANCH_ID
  AND B2.PARENT_ID = B3.BRANCH_ID;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...