AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 213924
Accepted
Ross Presser
Ross Presser
Asked: 2018-08-03 08:04:13 +0800 CST2018-08-03 08:04:13 +0800 CST 2018-08-03 08:04:13 +0800 CST

Impedir a exclusão do último filho

  • 772
CREATE TABLE Parent ( ParentID int NOT NULL PRIMARY KEY,
                      ParentName varchar(50) NULL )
CREATE TABLE Child  ( ChildID int NOT NULL PRIMARY KEY,
                      ParentID int NOT NULL REFERENCES Parent (ParentID),
                      ChildName varchar(50) NULL,
                      IsFavorite BIT NOT NULL )

O primeiro desejo é que eu gostaria de evitar a situação de pais sem filhos. Obviamente, quando o par é criado pela primeira vez, o Pai não terá filho, e eu não quero impedir a inserção em si; mas quero proibir totalmente a exclusão do último filho. (Por motivos comerciais, os Pais nunca serão excluídos.)

Como segundo desejo, preciso evitar mais de um Child com IsFavorite=1, o que sei que posso fazer com um índice exclusivo filtrado. Eu também gostaria de desencorajar não ter filhos com IsFavorite=1 -- ou seja, não permitir a configuração IsFavorite=0 se este for o único filho de seu pai. (Do lado do aplicativo, as inserções normalmente devem ser feitas corretamente com IsFavorite=1 para o primeiro filho; mas não quero que o banco de dados interfira nisso.)

Tenho certeza de que não sou o primeiro com esses desejos, mas não consegui encontrar uma pergunta correspondente no dba ou no stackoverflow. Se isso é um engano, por favor marque-o como um idiota e me aponte onde eu preciso estar.

sql-server foreign-key
  • 2 2 respostas
  • 282 Views

2 respostas

  • Voted
  1. Best Answer
    David Browne - Microsoft
    2018-08-03T08:17:23+08:002018-08-03T08:17:23+08:00

    Você pode fazer (ParentId,FavoriteChildId) um FK em Parent e obter a maior parte disso. No entanto, no SQL Server, não há uma maneira prática de tornar o relacionamento necessário, portanto, você deve permitir que um pai não tenha filho favorito, ou então não poderá inserir um novo pai sem desabilitar o FK.

    Você pode adicionar um gatilho para evitar definir FavoriteChildId como nulo, mas deve permitir isso pelo menos no caso de um pai ter um único filho, caso contrário não haveria como excluir esse filho. Então não tenho certeza se vale a pena se preocupar com o Trigger. POR EXEMPLO:

    --alter table Parent 
    --drop constraint fk_Parent_favorite_child
    --go
    --drop table if exists Child
    --drop table if exists Parent
    
    
    go
    
    CREATE TABLE Parent 
    ( 
       ParentID int NOT NULL PRIMARY KEY,
       ParentName varchar(50) NULL,
       FavoriteChildId int null
    )
    
    CREATE TABLE Child  
    ( 
      ParentID int NOT NULL REFERENCES Parent (ParentID),
      ChildID int UNIQUE NOT NULL,
      constraint pk_child primary key (ParentID, ChildID),
      ChildName varchar(50) NULL
    )
    
    alter table Parent 
    add constraint fk_Parent_favorite_child
    foreign key (ParentId,FavoriteChildId) 
      references Child (ParentId,ChildId)
    on delete set null
    
    go
    create or alter trigger tg_ensure_favorite_child 
      on parent after update 
    as
    begin
       if exists 
       (
           select * 
           from inserted i
           where i.FavoriteChildId is null
             and (select count(*) from child where ParentID = i.ParentId  ) > 1
        )
        begin
          throw 50001, 'Cannot set ChildID null for Parent with more than one Child.', 10
        end
    end
    
    go
    
    insert into Parent(ParentID,ParentName) values (1,'Parent1')
    insert into Child(ParentId, ChildId, ChildName) values (1,1,'Child1')
    insert into Child(ParentId, ChildId, ChildName) values (1,2,'Child2')
    update Parent set FavoriteChildId = 2 where ParentId = 1
    
    go
    
    update parent set FavoriteChildId = null where ParentId = 1 -- Fails
    --Msg 50001, Level 16, State 10, Procedure tg_ensure_favorite_child, Line 13 [Batch Start Line 45]
    --cannot set ChildID null for Parent with Children
    
    select FavoriteChildId from Parent where ParentID = 1
    --FavoriteChildId
    -----------------
    --2
    
    --(1 row affected)
    go
    
    delete from Child where ParentId = 1 and ChildID = 1
    
    go
    
    update parent set FavoriteChildId = null where ParentId = 1 -- Succeeds
    
    • 2
  2. Paul White
    2018-08-05T09:24:30+08:002018-08-05T09:24:30+08:00

    Talvez seja mais fácil implementar esses requisitos (um tanto incomuns) com um índice exclusivo filtrado e alguns AFTERgatilhos:

    Tabelas e índices

    CREATE TABLE dbo.Parent
    ( 
        ParentID int NOT NULL,
        ParentName varchar(50) NULL,
    
        CONSTRAINT [PK dbo.Parent ParentID]
            PRIMARY KEY CLUSTERED (ParentID)
    );
    
    CREATE TABLE dbo.Child
    ( 
        ChildID int NOT NULL,
        ParentID int NOT NULL,
        ChildName varchar(50) NULL,
        IsFavourite bit NOT NULL,
    
        CONSTRAINT [PK dbo.Child ChildID]
            PRIMARY KEY CLUSTERED (ChildID),
    
        CONSTRAINT [FK dbo.Child dbo.Parent (ParentID)]
            FOREIGN KEY (ParentID)
            REFERENCES dbo.Parent (ParentID)
    );
    GO
    -- No more than one favourite child per parent
    CREATE UNIQUE INDEX [FUQ dbo.Child ParentID (IsFavourite = true)]
    ON dbo.Child (ParentID)
    WHERE IsFavourite = CONVERT(bit, 'true');
    
    -- Useful index for trigger logic and FK
    CREATE INDEX [IX dbo.Child ParentID]
    ON dbo.Child (ParentID);
    

    Excluir gatilho

    CREATE TRIGGER dbo.Child_AD
    ON dbo.Child
    AFTER DELETE AS
    BEGIN
        IF @@ROWCOUNT = 0 RETURN;
        SET XACT_ABORT, NOCOUNT ON;
        SET ROWCOUNT 0;
    
        IF EXISTS
        (
            SELECT DEL.ParentID
            FROM Deleted AS DEL
            EXCEPT
            SELECT C.ParentID
            FROM dbo.Child AS C
        )
        BEGIN
            RAISERROR ('Cannot delete the only child of a parent', 16, 1);
            ROLLBACK TRANSACTION;
            RETURN;
        END;
    END;
    

    Atualizar acionador

    CREATE TRIGGER dbo.Child_AU
    ON dbo.Child
    AFTER UPDATE AS
    BEGIN
        IF @@ROWCOUNT = 0 OR NOT UPDATE(IsFavourite) RETURN;
        SET XACT_ABORT, NOCOUNT ON;
        SET ROWCOUNT 0;
    
        IF EXISTS
        (
            SELECT 1 
            FROM Deleted AS DEL
            JOIN Inserted AS INS
                ON INS.ChildID = DEL.ChildID
            WHERE 
                DEL.IsFavourite = CONVERT(bit, 'true')
                AND INS.IsFavourite = CONVERT(bit, 'false')
                AND NOT EXISTS
                (
                    SELECT 1
                    FROM dbo.Child AS C
                    WHERE C.ParentID = INS.ParentID
                    AND C.ChildID <> INS.ChildID
                )
        )
        BEGIN
            RAISERROR ('Cannot change IsFavourite to false for the only child of a parent', 16, 1);
            ROLLBACK TRANSACTION;
            RETURN;
        END;
    END;
    

    Como de costume, o gatilho de atualização só lidará com todas as atualizações de várias linhas corretamente se a chave primária for imutável. A maneira mais fácil de conseguir isso seria criar ChildIDuma coluna de identidade.

    • 2

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve