Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como criar vários um para um


Você está usando a herança (também conhecida na modelagem entidade-relacionamento como "subclasse" ou "categoria"). Em geral, existem 3 maneiras de representá-lo no banco de dados:
  1. "Todas as classes em uma tabela": Tenha apenas uma tabela "cobrindo" o pai e todas as classes filho (ou seja, com todas as colunas pai e filho), com uma restrição CHECK para garantir que o subconjunto correto de campos não seja NULL (ou seja, dois filhos diferentes não "misturam").
  2. "Classe concreta por tabela": Tenha uma tabela diferente para cada filho, mas nenhuma tabela pai. Isso exige que os relacionamentos dos pais (no seu caso, Inventário <- Armazenamento) sejam repetidos em todos os filhos.
  3. "Classe por tabela": Ter uma tabela pai e uma tabela separada para cada filho, que é o que você está tentando fazer. Isso é mais limpo, mas pode custar algum desempenho (principalmente ao modificar dados, não tanto ao consultar, porque você pode ingressar diretamente do filho e pular o pai).

Eu geralmente prefiro a terceira abordagem, mas reforce tanto a presença e a exclusividade de uma criança no nível de aplicação. Impor ambos no nível do banco de dados é um pouco complicado, mas pode ser feito se o DBMS suportar restrições adiadas. Por exemplo:


CHECK (
    (
        (VAN_ID IS NOT NULL AND VAN_ID = STORAGE_ID)
        AND WAREHOUSE_ID IS NULL
    )
    OR (
        VAN_ID IS NULL
        AND (WAREHOUSE_ID IS NOT NULL AND WAREHOUSE_ID = STORAGE_ID)
    )
)

Isso aplicará a exclusividade (devido ao CHECK ) e a presença (devido à combinação de CHECK e FK1 /FK2 ) da criança.

Infelizmente, o MS SQL Server não oferece suporte a restrições adiadas, mas você pode "esconder" toda a operação por trás de procedimentos armazenados e proibir os clientes de modificar as tabelas diretamente.

Apenas a exclusividade pode ser aplicada sem restrições adiadas:



O STORAGE_TYPE é um discriminador de tipo, geralmente um inteiro para economizar espaço (no exemplo acima, 0 e 1 são "conhecidos" pelo seu aplicativo e interpretados de acordo).

O VAN.STORAGE_TYPE e WAREHOUSE.STORAGE_TYPE podem ser computadas (também conhecidas como colunas "calculadas") para economizar armazenamento e evitar a necessidade do CHECK s.

--- EDITAR ---

As colunas computadas funcionariam no SQL Server assim:
CREATE TABLE STORAGE (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE tinyint NOT NULL,
    UNIQUE (STORAGE_ID, STORAGE_TYPE)
);

CREATE TABLE VAN (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE AS CAST(0 as tinyint) PERSISTED,
    FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);

CREATE TABLE WAREHOUSE (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE AS CAST(1 as tinyint) PERSISTED,
    FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);

-- We can make a new van.
INSERT INTO STORAGE VALUES (100, 0);
INSERT INTO VAN VALUES (100);

-- But we cannot make it a warehouse too.
INSERT INTO WAREHOUSE VALUES (100);
-- Msg 547, Level 16, State 0, Line 24
-- The INSERT statement conflicted with the FOREIGN KEY constraint "FK__WAREHOUSE__695C9DA1". The conflict occurred in database "master", table "dbo.STORAGE".

Infelizmente, o SQL Server requer uma coluna computada que é usada em um externo chave a ser PERSISTIDA. Outros bancos de dados podem não ter essa limitação (por exemplo, colunas virtuais do Oracle), o que pode economizar algum espaço de armazenamento.