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 / 4794
Accepted
emaynard
emaynard
Asked: 2011-08-19 15:44:20 +0800 CST2011-08-19 15:44:20 +0800 CST 2011-08-19 15:44:20 +0800 CST

Como forço um registro a ter um valor verdadeiro para uma coluna booleana e todos os outros um valor falso?

  • 772

Desejo impor que apenas um registro em uma tabela seja considerado o valor "padrão" para outras consultas ou exibições que possam acessar essa tabela.

Basicamente, quero garantir que essa consulta sempre retornará exatamente uma linha:

SELECT ID, Zip 
FROM PostalCodes 
WHERE isDefault=True

Como eu faria isso no SQL?

mysql database-design
  • 7 7 respostas
  • 30138 Views

7 respostas

  • Voted
  1. Nick Chammas
    2011-08-19T16:05:57+08:002011-08-19T16:05:57+08:00

    Nota: Esta resposta foi dada antes de ficar claro que o solicitante queria algo específico do MySQL. Esta resposta é tendenciosa para o SQL Server.

    Aplique uma restrição CHECK à tabela que chama uma UDF e garante que seu valor de retorno seja <= 1. A UDF pode apenas contar as linhas na tabela WHERE isDefault = TRUE. Isso garantirá que não haja mais de 1 linha padrão na tabela.

    Você deve adicionar um bitmap ou índice filtradoisDefault na coluna (dependendo da sua plataforma) para garantir que essa UDF seja executada muito rapidamente.

    Ressalvas

    • As restrições de verificação têm certas limitações . Ou seja, eles são invocados para cada linha modificada, mesmo que essas linhas ainda não tenham sido confirmadas em uma transação. Portanto, se você iniciar uma transação, definir uma nova linha como padrão e, em seguida, desmarcar a linha antiga, sua restrição de verificação reclamará. Isso porque ele será executado depois que você tornar a nova linha o padrão, descobrir que agora existem dois padrões e falhar. A solução, então, é certificar-se de que você desconfigurou a linha padrão antes de definir um novo padrão, mesmo se estiver fazendo esse trabalho em uma transação.
    • Como gbn apontou , UDFs podem retornar resultados inconsistentes se você estiver usando o isolamento de instantâneo no SQL Server. No entanto, uma UDF inline não se depara com esse problema e, como uma UDF que apenas faz uma contagem é compatível com inline, isso não é um problema aqui.
    • A força do poder de processamento de um mecanismo de banco de dados está em sua capacidade de trabalhar em conjuntos de dados de uma só vez. Com uma restrição de verificação baseada em UDF, o mecanismo será limitado a aplicar essa UDF serialmente a cada linha modificada. Em seu caso de uso, é improvável que você execute atualizações em massa na PostalCodestabela, no entanto, isso continua sendo uma preocupação de desempenho para as situações em que a atividade baseada em conjunto é provável.

    Dadas todas essas advertências, recomendo usar a sugestão do gbn de um índice exclusivo filtrado em vez de uma restrição de verificação.

    • 10
  2. Best Answer
    gbn
    2011-08-20T00:22:25+08:002011-08-20T00:22:25+08:00

    Edit: isso é voltado para o SQL Server antes de conhecermos o MySQL

    Existem 4 5 maneiras de fazer isso: em ordem do mais desejado para o menos desejado

    Não sabemos para que RDBMS isso se aplica, embora nem todos possam se aplicar

    1. A melhor maneira é um índice filtrado. Isso usa DRI para manter a exclusividade.

    2. Coluna computada com exclusividade (veja a resposta de Jack Douglas) (adicionada pela edição 2)

    3. Uma visão indexada/materializada que é como um índice filtrado usando DRI

    4. Gatilho (conforme outras respostas)

    5. Verifique a restrição com uma UDF. Isso não é seguro para simultaneidade e isolamento de instantâneo. Veja Um Dois Três Quatro

    Observe que o requisito de "um padrão" está na tabela, não no procedimento armazenado. O procedimento armazenado terá os mesmos problemas de simultaneidade que uma restrição de verificação com um udf

    Nota: perguntado em TANTAS vezes:

    • https://stackoverflow.com/questions/4810615/sql-server-bit-column-constraint-1-row-1-all-others-0/4810654#4810654
    • https://stackoverflow.com/questions/6088361/the-proper-way-to-implement-unique-constraint-that-allows-multiple-null-values-in/6088397#6088397
    • 9
  3. RolandoMySQLDBA
    2011-08-19T18:35:21+08:002011-08-19T18:35:21+08:00

    Aqui está um procedimento armazenado (dialeto do MySQL):

    DELIMITER $$
    DROP PROCEDURE IF EXISTS SetDefaultForZip;
    CREATE PROCEDURE SetDefaultForZip (NEWID INT)
    BEGIN
        DECLARE FOUND_TRUE,OLDID INT;
    
        SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
        IF FOUND_TRUE = 1 THEN
            SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
            IF NEWID <> OLDID THEN
                UPDATE PostalCode SET isDefault = FALSE WHERE ID = OLDID;
                UPDATE PostalCode SET isDefault = TRUE  WHERE ID = NEWID;
            END IF;
        ELSE
            UPDATE PostalCode SET isDefault = TRUE WHERE ID = NEWID;
        END IF;
    END;
    $$
    DELIMITER ;
    

    Para garantir que sua tabela esteja limpa e que o procedimento armazenado esteja funcionando, supondo que o ID 200 seja o padrão, execute estas etapas:

    ALTER TABLE PostalCode DROP INDEX isDefault_ndx;
    UPDATE PostalCodes SET isDefault = FALSE;
    ALTER TABLE PostalCode ADD INDEX isDefault_ndx (isDefault);
    CALL SetDefaultForZip(200);
    SELECT ID FROM PostalCodes WHERE isDefault = TRUE;
    

    Em vez de um procedimento armazenado, que tal um Trigger?

    DELIMITER $$
    CREATE TRIGGER postalcodes_bu BEFORE UPDATE ON PostalCodes FOR EACH ROW
    BEGIN
        DECLARE FOUND_TRUE,OLDID INT;
        IF NEW.isDefault = TRUE THEN
            SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
            IF FOUND_TRUE = 1 THEN
                SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
                UPDATE PostalCodes SET isDefault = FALSE WHERE ID = OLDID;
            END IF;
        END IF;
    END;
    $$
    DELIMITER ;
    

    Para garantir que sua tabela esteja limpa e que o gatilho esteja funcionando, supondo que o ID 200 seja o padrão, execute estas etapas:

    DROP TRIGGER postalcodes_bu;
    ALTER TABLE PostalCode DROP INDEX isDefault_ndx;
    UPDATE PostalCodes SET isDefault = FALSE;
    ALTER TABLE PostalCode ADD INDEX isDefault_ndx (isDefault);
    DELIMITER $$
    CREATE TRIGGER postalcodes_bu BEFORE UPDATE ON PostalCodes FOR EACH ROW
    BEGIN
        DECLARE FOUND_TRUE,OLDID INT;
        IF NEW.isDefault = TRUE THEN
            SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
            IF FOUND_TRUE = 1 THEN
                SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
                UPDATE PostalCodes SET isDefault = FALSE WHERE ID = OLDID;
            END IF;
        END IF;
    END;
    $$
    DELIMITER ;
    UPDATE PostalCodes SET isDefault = TRUE WHERE ID = 200;
    SELECT ID FROM PostalCodes WHERE isDefault = TRUE;
    
    • 4
  4. bobs
    2011-08-19T16:17:42+08:002011-08-19T16:17:42+08:00

    Você pode usar um gatilho para aplicar as regras. Quando uma instrução UPDATE ou INSERT define isDefault como True, o SQL no gatilho pode definir todas as outras linhas como False.

    Você terá que considerar outras situações ao aplicar isso. Por exemplo, o que deve acontecer se mais de uma linha e UPDATE ou INSERT definir isDefault como True? Que regras serão aplicadas para evitar quebrar a regra?

    Além disso, é possível a condição em que não há padrão? O que acontecerá quando uma atualização definir o isDefault de True para False.

    Depois de definir as regras, você pode criá-las no gatilho.

    Em seguida, conforme indicado em outra resposta, você deseja aplicar uma restrição de verificação para ajudar a aplicar as regras.

    • 3
  5. Mark Storey-Smith
    2011-08-19T17:35:54+08:002011-08-19T17:35:54+08:00

    A seguir configura uma tabela e dados de exemplo:

    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U'))
    DROP TABLE [dbo].[MyTable]
    GO
    
    CREATE TABLE dbo.MyTable
    (
        [id] INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
        , [IsDefault] BIT DEFAULT 0
    )
    GO
    
    INSERT dbo.MyTable DEFAULT VALUES
    GO 100
    

    Se você criar um procedimento armazenado para definir o sinalizador IsDefault e remover permissões para alterar a tabela base, poderá impor isso com a consulta abaixo. Isso exigiria uma verificação de tabela a cada vez.

    DECLARE @Id INT
    SET @Id = 10
    
    UPDATE
        dbo.MyTable
    SET
        IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END 
    GO
    

    Dependendo do tamanho da tabela, um índice em IsDefault (DESC) pode resultar em uma ou ambas as consultas abaixo, evitando uma verificação completa.

    DECLARE @Id INT
    SET @Id = 10
    
    UPDATE
        dbo.MyTable
    SET
        IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END
    FROM
        dbo.MyTable
    WHERE
        [id] = @Id
    OR  IsDefault = 1
    
    UPDATE
        dbo.MyTable
    SET
        IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END
    FROM
        dbo.MyTable
    WHERE
        [id] = @Id
    OR  ([id] != @Id AND IsDefault = 1)
    

    Se você não puder remover permissões da tabela base, precisar impor a integridade por outros meios e estiver usando o SQL2008, poderá usar um índice exclusivo filtrado:

    CREATE UNIQUE INDEX IX_MyTable_IsDefault  ON dbo.MyTable (IsDefault) WHERE IsDefault = 1
    
    • 3
  6. djfm
    2015-08-17T10:18:07+08:002015-08-17T10:18:07+08:00

    Para o MySQL que não possui índices filtrados, você pode fazer algo como o seguinte. Ele explora o fato de que o MySQL permite vários NULLs em índices únicos e usa uma tabela dedicada e uma chave estrangeira para simular um tipo de dados que só pode ter NULL e 1 como valores.

    Com esta solução, em vez de verdadeiro/falso, você usaria apenas 1/NULL.

    CREATE TABLE DefaultFlag (id TINYINT UNSIGNED NOT NULL PRIMARY KEY);
    INSERT INTO DefaultFlag (id) VALUES (1);
    
    CREATE TABLE PostalCodes (
        ID INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
        Zip VARCHAR (32) NOT NULL,
        isDefault TINYINT UNSIGNED NULL DEFAULT NULL,
        CONSTRAINT FOREIGN KEY (isDefault) REFERENCES DefaultFlag (id),
        UNIQUE KEY (isDefault)
    );
    
    INSERT INTO PostalCodes (Zip, isDefault) VALUES ('123', 1   );
    /* Only one row can be default */
    INSERT INTO PostalCodes (Zip, isDefault) VALUES ('abc', 1   );
    /* ERROR 1062 (23000): Duplicate entry '1' for key 'isDefault' */
    
    /* MySQL allows multiple NULLs in unique indexes */
    INSERT INTO PostalCodes (Zip, isDefault) VALUES ('456', NULL);
    INSERT INTO PostalCodes (Zip, isDefault) VALUES ('789', NULL);
    
    /* only NULL and 1 are admitted in isDefault thanks to foreign key */
    INSERT INTO PostalCodes (Zip, isDefault) VALUES ('789', 2   );
    /* ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`PostalCodes`, CONSTRAINT `PostalCodes_ibfk_1` FOREIGN KEY (`isDefault`) REFERENCES `DefaultFlag` (`id`))*/
    

    A única coisa que pode dar errado, eu acho, é se alguém adicionar uma linha à DefaultFlagtabela. Acho que isso não é uma grande preocupação e você sempre pode corrigi-lo com permissões se realmente quiser estar seguro.

    • 1
  7. Konchog
    2015-07-10T06:45:01+08:002015-07-10T06:45:01+08:00
    SELECT ID, Zip FROM PostalCodes WHERE isDefault=True 
    

    Isso estava essencialmente causando problemas porque você colocava o campo no lugar errado e isso é tudo.

    Tenha uma tabela 'Defaults', com PostalCode nela, que contenha o id que você está procurando. Em geral, também é bom nomear suas tabelas de acordo com um registro, então o nome da sua tabela inicial seria melhor chamado 'PostalCode'. Os padrões serão uma tabela de linha única, até que você precise especializar seus padrões em relação a alguma outra configuração (como históricos).

    A consulta final que você deseja é a seguinte:

    select Zip from PostalCode p, Defaults d where p.ID=d.PostalCode;
    
    • 0

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

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

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

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

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

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 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
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +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