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 / 23475
Accepted
Chris Travers
Chris Travers
Asked: 2012-08-31 17:27:40 +0800 CST2012-08-31 17:27:40 +0800 CST 2012-08-31 17:27:40 +0800 CST

Restrições de modelagem em agregados de subconjunto?

  • 772

Estou usando o PostgreSQL, mas acho que a maioria dos bancos de dados de ponta deve ter alguns recursos semelhantes e, além disso, que as soluções para eles podem inspirar soluções para mim, portanto, não considere isso específico do PostgreSQL.

Sei que não sou o primeiro a tentar resolver esse problema, então acho que vale a pena perguntar aqui, mas estou tentando avaliar os custos de modelar dados contábeis de forma que cada transação seja fundamentalmente equilibrada. Os dados contábeis são apenas anexos. A restrição geral (escrita em pseudo-código) aqui pode ser mais ou menos assim:

CREATE TABLE journal_entry (
    id bigserial not null unique, --artificial candidate key
    journal_type_id int references  journal_type(id),
    reference text, -- source document identifier, unique per journal
    date_posted date not null,
    PRIMARY KEY (journal_type_id, reference)
);

CREATE TABLE journal_line (
    entry_id bigint references journal_entry(id),
    account_id int not null references account(id),
    amount numeric not null,
    line_id bigserial not null unique,
    CHECK ((sum(amount) over (partition by entry_id) = 0) -- this won't work
);

Obviamente, tal restrição de verificação nunca funcionará. Ele opera por linha e pode verificar todo o banco de dados. Portanto, sempre falhará e será lento ao fazê-lo.

Então, minha pergunta é qual é a melhor maneira de modelar essa restrição? Eu basicamente olhei para duas ideias até agora. Querendo saber se estes são os únicos, ou se alguém tem uma maneira melhor (além de deixá-lo no nível do aplicativo ou em um procedimento armazenado).

  1. Eu poderia pegar emprestado uma página do conceito do mundo da contabilidade sobre a diferença entre um livro de entrada original e um livro de entrada final (diário geral versus razão geral). A esse respeito, eu poderia modelar isso como uma matriz de linhas de diário anexadas à entrada do diário, impor a restrição na matriz (em termos do PostgreSQL, selecione sum(amount) = 0 de unnest(je.line_items). Um gatilho pode expandir e salve-os em uma tabela de itens de linha, onde as restrições de colunas individuais podem ser aplicadas com mais facilidade e onde os índices, etc., podem ser mais úteis. Essa é a direção em que estou inclinado.
  2. Eu poderia tentar codificar um gatilho de restrição que aplicaria isso por transação com a ideia de que a soma de uma série de 0s sempre será 0.

Estou pesando isso contra a abordagem atual de impor a lógica em um procedimento armazenado. O custo da complexidade está sendo pesado contra a ideia de que a prova matemática de restrições é superior aos testes de unidade. A principal desvantagem do nº 1 acima é que os tipos como tuplas são uma daquelas áreas no PostgreSQL em que se depara com comportamento inconsistente e mudanças nas suposições regularmente e, portanto, espero que o comportamento nessa área mude com o tempo. Projetar uma futura versão segura não é tão fácil.

Existem outras maneiras de resolver esse problema que serão dimensionados para milhões de registros em cada tabela? Estou esquecendo de algo? Existe uma troca que eu perdi?

Em resposta ao ponto de Craig abaixo sobre as versões, no mínimo, isso terá que ser executado no PostgreSQL 9.2 e superior (talvez 9.1 e superior, mas provavelmente podemos usar o 9.2 direto).

database-design postgresql
  • 2 2 respostas
  • 2555 Views

2 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2012-09-01T08:59:57+08:002012-09-01T08:59:57+08:00

    Como temos que abranger várias linhas, isso não podeCHECK ser implementado com uma restrição simples .

    Também podemos descartar restrições de exclusão . Isso abrangeria várias linhas, mas apenas verificaria a desigualdade. Operações complexas como uma soma em várias linhas não são possíveis.

    A ferramenta que parece melhor se adequar ao seu caso é CONSTRAINT TRIGGER(ou até mesmo simples TRIGGER- a única diferença na implementação atual é que você pode ajustar o tempo do gatilho com SET CONSTRAINTS.

    Então essa é a sua opção 2 .

    Uma vez que podemos contar com a restrição sendo aplicada o tempo todo, não precisamos mais verificar a tabela inteira. Verificar apenas as linhas inseridas na transação atual - ao final da transação - é suficiente. O desempenho deve ser ok.

    Tambem como

    Os dados contábeis são apenas anexos.

    ... só precisamos nos preocupar com as linhas recém- inserida . (Assumindo UPDATEou DELETEnão são possíveis.)

    Eu uso a coluna do sistema xide comparo com a função txid_current()- que retorna o xidda transação atual. Para comparar os tipos, é necessário lançar ... Isso deve ser razoavelmente seguro. Considere esta resposta posterior relacionada com um método mais seguro:

    • Como visualizar as tuplas alteradas em uma transação do PostgreSQL?

    Demonstração

    CREATE TABLE journal_line(amount int); -- simplistic table for demo
    
    CREATE OR REPLACE FUNCTION trg_insaft_check_balance()
        RETURNS trigger AS
    $func$
    BEGIN
       IF sum(amount) <> 0
          FROM journal_line 
          WHERE xmin::text::bigint = txid_current()  -- consider link above
             THEN
          RAISE EXCEPTION 'Entries not balanced!';
       END IF;
    
       RETURN NULL;  -- RETURN value of AFTER trigger is ignored anyway
    END;
    $func$ LANGUAGE plpgsql;
    
    CREATE CONSTRAINT TRIGGER insaft_check_balance
        AFTER INSERT ON journal_line
        DEFERRABLE INITIALLY DEFERRED
        FOR EACH ROW
        EXECUTE PROCEDURE trg_insaft_check_balance();
    

    Deferred , por isso só é verificado no final da transação.

    testes

    INSERT INTO journal_line(amount) VALUES (1), (-1);
    

    Funciona.

    INSERT INTO journal_line(amount) VALUES (1);
    

    Falha:

    ERRO: Entradas não balanceadas!

    BEGIN;
    INSERT INTO journal_line(amount) VALUES (7), (-5);
    -- do other stuff
    SELECT * FROM journal_line;
    INSERT INTO journal_line(amount) VALUES (-2);
    -- INSERT INTO journal_line(amount) VALUES (-1); -- make it fail
    COMMIT;
    

    Funciona. :)

    Se você precisar impor sua restrição antes do final da transação, poderá fazê-lo em qualquer ponto da transação, mesmo no início:

    SET CONSTRAINTS insaft_check_balance IMMEDIATE;
    

    Mais rápido com gatilho simples

    Se você operar com várias linhas INSERT, é mais eficaz acionar por instrução - o que não é possível com acionadores de restrição :

    Os gatilhos de restrição só podem ser especificados FOR EACH ROW.

    Em vez disso, use um gatilho simples e atire FOR EACH STATEMENTpara...

    • perder a opção de SET CONSTRAINTS.
    • ganhar desempenho.

    EXCLUIR possível

    Em resposta ao seu comentário: Se DELETEfor possível, você pode adicionar um gatilho semelhante fazendo uma verificação de saldo de toda a tabela após um DELETE ter ocorrido. Isso seria muito mais caro, mas não importa muito, pois raramente acontece.

    • 13
  2. A-K
    2012-09-01T11:19:24+08:002012-09-01T11:19:24+08:00

    A solução do SQL Server a seguir usa apenas restrições. Estou usando abordagens semelhantes em vários lugares do meu sistema.

    CREATE TABLE dbo.Lines
      (
        EntryID INT NOT NULL ,
        LineNumber SMALLINT NOT NULL ,
        CONSTRAINT PK_Lines PRIMARY KEY ( EntryID, LineNumber ) ,
        PreviousLineNumber SMALLINT NOT NULL ,
        CONSTRAINT UNQ_Lines UNIQUE ( EntryID, PreviousLineNumber ) ,
        CONSTRAINT CHK_Lines_PreviousLineNumber_Valid CHECK ( ( LineNumber > 0
                AND PreviousLineNumber = LineNumber - 1
              )
              OR ( LineNumber = 0 ) ) ,
        Amount INT NOT NULL ,
        RunningTotal INT NOT NULL ,
        CONSTRAINT UNQ_Lines_FkTarget UNIQUE ( EntryID, LineNumber, RunningTotal ) ,
        PreviousRunningTotal INT NOT NULL ,
        CONSTRAINT CHK_Lines_PreviousRunningTotal_Valid CHECK 
            ( PreviousRunningTotal + Amount = RunningTotal ) ,
        CONSTRAINT CHK_Lines_TotalAmount_Zero CHECK ( 
                ( LineNumber = 0
                    AND PreviousRunningTotal = 0
                  )
                  OR ( LineNumber > 0 ) ),
        CONSTRAINT FK_Lines_PreviousLine 
            FOREIGN KEY ( EntryID, PreviousLineNumber, PreviousRunningTotal )
            REFERENCES dbo.Lines ( EntryID, LineNumber, RunningTotal )
      ) ;
    GO
    
    -- valid subset inserts
    INSERT INTO dbo.Lines(EntryID ,
            LineNumber ,
            PreviousLineNumber ,
            Amount ,
            RunningTotal ,
            PreviousRunningTotal )
    VALUES(1, 0, 2, 10, 10, 0),
    (1, 1, 0, -5, 5, 10),
    (1, 2, 1, -5, 0, 5);
    
    -- invalid subset fails
    INSERT INTO dbo.Lines(EntryID ,
            LineNumber ,
            PreviousLineNumber ,
            Amount ,
            RunningTotal ,
            PreviousRunningTotal )
    VALUES(2, 0, 1, 10, 10, 5),
    (2, 1, 0, -5, 5, 10) ;
    
    • 4

relate perguntas

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Quais são algumas maneiras de implementar um relacionamento muitos-para-muitos em um data warehouse?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

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

    Como ver a lista de bancos de dados no Oracle?

    • 8 respostas
  • Marko Smith

    Quão grande deve ser o mysql innodb_buffer_pool_size?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    restaurar a tabela do arquivo .frm e .ibd?

    • 10 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

    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

    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
    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
    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
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +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