A melhor forma de aplicar recursão nível único em uma tabela SQL?
-
05-07-2019 - |
Pergunta
Vamos dizer que você tem uma mesa para filiais em sua organização. Alguns deles são "principais" ramos, e outros são escritórios satélites que rolam até um ramo principal. Outros que esta distinção, que só afeta algumas coisas no sistema, os ramos são todas iguais e têm os mesmos atributos (endereço, etc.). Uma maneira de modelar isto é, em uma tabela como:
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)
)
Onde is_satellite_office
= 1 sse este registro é um satélite para outro ramo, e satellite_to_branch_id
refere-se a que ramo você é um satélite de, se houver.
É fácil o suficiente para colocar uma restrição sobre a mesa para que essas duas colunas concordar em qualquer registro:
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)
)
No entanto, o que eu realmente quero é uma maneira de garantir que esta recursão só vai um nível profundo ... isto é, que se eu apontar para um ramo como o meu pai, ele não deve ter a própria mãe, e seu valor para is_satellite_office
deve ser 0. em outras palavras, eu realmente não quero uma estrutura de árvore totalmente recursiva, eu só quero limitá-lo a uma única relação pai / filho. É assim que eu vou escrever o código, e se há uma maneira de aplicá-la no banco de dados que não irá executar um lixo total, o que eu gostaria de.
Todas as idéias? Eu estou trabalhando em MSSQL 2005, mas soluções gerais (não específicos de fornecedor) são os preferidos. E há gatilhos precisa de aplicar, a menos que há realmente nenhuma outra maneira de fazê-lo.
EDIT: Para ser claro, satellite_to_branch_id
é o ponteiro recursiva para outro registro na mesma tabela Branch. Eu sei que eu poderia remover o is_satellite_office BIT
e confiar em IsNull(satellite_to_branch_id)
para me dar a mesma informação, mas eu acho que é um pouco mais claro para ser explícito e, além disso, que, isso não é a essência da questão. Estou realmente procurando uma maneira pura SQL-restrição para evitar recursão profundidade superior a 1.
Solução
Você pode vincular uma restrição de verificação para o valor de retorno de uma UDF. Criar um UDF que leva as colunas envolvidas como parâmetros de entrada e, em seguida, verificar o seu estado desejado usando uma escolha na UDF.
Outras dicas
Parece-me que uma restrição de negócios, difícil de aplicar no nível de definição de dados. Eu não acredito que a álgebra relacional tem qualquer apoio para determinar um limite para auto referências profundidade.
Você não está autorizado a referir-se a um procedimento armazenado no seu constrangimento? Você pode em PostgreSQL, então eu ficaria surpreso se 2005 não permitem isso.
E sobre esta estrutura um pouco diferente?
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 simplesmente apontar para o BRANCH_ID de outro registro na mesma tabela. Se for nulo, então você sabe que não tem pais.
Então, para obter um nível de recursão, você pode simplesmente juntar-se à mesa para si uma vez, como este:
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
Se você deseja impor um nível de profundidade em sua árvore, fazer um on-disparador de inserção / atualização que irá levantar uma exceção se esta consulta retorna nada.
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;