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 / 33737
Accepted
Mithir
Mithir
Asked: 2013-01-30 05:27:02 +0800 CST2013-01-30 05:27:02 +0800 CST 2013-01-30 05:27:02 +0800 CST

Tudo bem manter um valor que é atualizado em uma tabela?

  • 772

Estamos desenvolvendo uma plataforma para cartões pré-pagos, que basicamente contém dados sobre os cartões e seu saldo, pagamentos, etc.

Até agora tínhamos uma entidade Cartão que possui uma coleção de entidade Conta, e cada Conta possui um Valor, que atualiza a cada Depósito/Retirada.

Há um debate agora na equipe; alguém nos disse que isso quebra as 12 regras de Codd e que atualizar seu valor a cada pagamento é um problema.

Isto é realmente um problema?

Se for, como podemos corrigir isso?

database-design rdbms
  • 5 5 respostas
  • 3009 Views

5 respostas

  • Voted
  1. Best Answer
    db2
    2013-01-30T06:08:28+08:002013-01-30T06:08:28+08:00

    Sim, isso não é normalizado, mas ocasionalmente projetos não normalizados vencem por motivos de desempenho.

    No entanto, eu provavelmente abordaria isso de maneira um pouco diferente, por razões de segurança. (Isenção de responsabilidade: atualmente não trabalho nem nunca trabalhei no setor financeiro. Estou apenas jogando isso fora.)

    Tenha uma tabela de saldos lançados nos cartões. Isso teria uma linha inserida para cada conta, indicando o saldo lançado no fechamento de cada período (dia, semana, mês ou o que for apropriado). Indexe esta tabela por número de conta e data.

    Use outra tabela para armazenar transações pendentes, que são inseridas na hora. Ao final de cada período, execute uma rotina que adicione as transações não contabilizadas ao último saldo final da conta para calcular o novo saldo. Marque as transações pendentes como lançadas ou observe as datas para determinar o que ainda está pendente.

    Dessa forma, você tem um meio de calcular o saldo do cartão na hora, sem precisar somar todo o histórico da conta, e colocando o recálculo do saldo em uma rotina de lançamento dedicada, você pode garantir que a segurança da transação desse recálculo seja limitada a um único local (e também limitar a segurança na tabela de saldos para que apenas a rotina de lançamento possa gravar nela).

    Em seguida, mantenha o máximo de dados históricos necessários para auditoria, atendimento ao cliente e requisitos de desempenho.

    • 30
  2. Chris Travers
    2013-01-30T06:02:25+08:002013-01-30T06:02:25+08:00

    Por outro lado, há um problema que encontramos com frequência no software de contabilidade. Parafraseado:

    Preciso mesmo agregar dez anos de dados para saber quanto dinheiro tem na conta corrente?

    A resposta, claro, é não, você não. Existem algumas abordagens aqui. Um é armazenar o valor calculado. Não recomendo essa abordagem porque os bugs de software que causam valores incorretos são muito difíceis de rastrear e, portanto, evitaria essa abordagem.

    Uma maneira melhor de fazer isso é o que chamo de abordagem agregada de instantâneo de log. Nessa abordagem nossos pagamentos e utilizações são inserções e nunca atualizamos esses valores. Periodicamente, agregamos os dados durante um período de tempo e inserimos um registro instantâneo calculado que representa os dados no momento em que o instantâneo se tornou válido (geralmente um período de tempo antes do presente).

    Agora, isso não quebra as regras de Codd porque, com o tempo, os instantâneos podem ser menos do que perfeitamente dependentes dos dados de pagamento/uso inseridos. Se tivermos instantâneos de trabalho, podemos decidir limpar dados de 10 anos sem afetar nossa capacidade de calcular saldos atuais sob demanda.

    • 17
  3. A-K
    2013-01-30T06:26:16+08:002013-01-30T06:26:16+08:00

    Por motivos de desempenho, na maioria dos casos, devemos armazenar o saldo atual - caso contrário, calculá-lo na hora pode se tornar proibitivamente lento.

    Nós armazenamos totais pré-calculados em nosso sistema. Para garantir que os números estejam sempre corretos, usamos restrições. A seguinte solução foi copiada do meu blog. Ele descreve um inventário, que é essencialmente o mesmo problema:

    Calcular totais em execução é notoriamente lento, seja com um cursor ou com uma junção triangular. É muito tentador desnormalizar, armazenar totais correntes em uma coluna, especialmente se você a selecionar com frequência. No entanto, como sempre, quando você desnormaliza, precisa garantir a integridade de seus dados desnormalizados. Felizmente, você pode garantir a integridade dos totais em execução com restrições – desde que todas as suas restrições sejam confiáveis, todos os seus totais em execução estão corretos. Além disso, dessa forma, você pode garantir facilmente que o saldo atual (totais acumulados) nunca seja negativo - a aplicação por outros métodos também pode ser muito lenta. O script a seguir demonstra a técnica.

    CREATE TABLE Data.Inventory(InventoryID INT NOT NULL IDENTITY,
      ItemID INT NOT NULL,
      ChangeDate DATETIME NOT NULL,
      ChangeQty INT NOT NULL,
      TotalQty INT NOT NULL,
      PreviousChangeDate DATETIME NULL,
      PreviousTotalQty INT NULL,
      CONSTRAINT PK_Inventory PRIMARY KEY(ItemID, ChangeDate),
      CONSTRAINT UNQ_Inventory UNIQUE(ItemID, ChangeDate, TotalQty),
      CONSTRAINT UNQ_Inventory_Previous_Columns UNIQUE(ItemID, PreviousChangeDate, PreviousTotalQty),
      CONSTRAINT FK_Inventory_Self FOREIGN KEY(ItemID, PreviousChangeDate, PreviousTotalQty)
        REFERENCES Data.Inventory(ItemID, ChangeDate, TotalQty),
      CONSTRAINT CHK_Inventory_Valid_TotalQty CHECK(TotalQty >= 0 AND (TotalQty = COALESCE(PreviousTotalQty, 0) + ChangeQty)),
      CONSTRAINT CHK_Inventory_Valid_Dates_Sequence CHECK(PreviousChangeDate < ChangeDate),
      CONSTRAINT CHK_Inventory_Valid_Previous_Columns CHECK((PreviousChangeDate IS NULL AND PreviousTotalQty IS NULL)
                OR (PreviousChangeDate IS NOT NULL AND PreviousTotalQty IS NOT NULL))
    );
    GO
    -- beginning of inventory for item 1
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    VALUES(1, '20090101', 10, 10, NULL, NULL);
    -- cannot begin the inventory for the second time for the same item 1
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    VALUES(1, '20090102', 10, 10, NULL, NULL);
    
    Msg 2627, Level 14, State 1, Line 10
    Violation of UNIQUE KEY constraint 'UNQ_Inventory_Previous_Columns'. Cannot insert duplicate key in object 'Data.Inventory'.
    The statement has been terminated.
    
    -- add more
    DECLARE @ChangeQty INT;
    SET @ChangeQty = 5;
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    SELECT TOP 1 ItemID, '20090103', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
      FROM Data.Inventory
      WHERE ItemID = 1
      ORDER BY ChangeDate DESC;
    
    SET @ChangeQty = 3;
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    SELECT TOP 1 ItemID, '20090104', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
      FROM Data.Inventory
      WHERE ItemID = 1
      ORDER BY ChangeDate DESC;
    
    SET @ChangeQty = -4;
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    SELECT TOP 1 ItemID, '20090105', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
      FROM Data.Inventory
      WHERE ItemID = 1
      ORDER BY ChangeDate DESC;
    
    -- try to violate chronological order
    
    SET @ChangeQty = 5;
    INSERT INTO Data.Inventory(ItemID,
      ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty)
    SELECT TOP 1 ItemID, '20081231', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
      FROM Data.Inventory
      WHERE ItemID = 1
      ORDER BY ChangeDate DESC;
    
    Msg 547, Level 16, State 0, Line 4
    The INSERT statement conflicted with the CHECK constraint "CHK_Inventory_Valid_Dates_Sequence". The conflict occurred in database "Test", table "Data.Inventory".
    The statement has been terminated.
    
    
    SELECT ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty
    FROM Data.Inventory ORDER BY ChangeDate;
    
    ChangeDate              ChangeQty   TotalQty    PreviousChangeDate      PreviousTotalQty
    ----------------------- ----------- ----------- ----------------------- -----
    2009-01-01 00:00:00.000 10          10          NULL                    NULL
    2009-01-03 00:00:00.000 5           15          2009-01-01 00:00:00.000 10
    2009-01-04 00:00:00.000 3           18          2009-01-03 00:00:00.000 15
    2009-01-05 00:00:00.000 -4          14          2009-01-04 00:00:00.000 18
    
    
    -- try to change a single row, all updates must fail
    UPDATE Data.Inventory SET ChangeQty = ChangeQty + 2 WHERE InventoryID = 3;
    UPDATE Data.Inventory SET TotalQty = TotalQty + 2 WHERE InventoryID = 3;
    -- try to delete not the last row, all deletes must fail
    DELETE FROM Data.Inventory WHERE InventoryID = 1;
    DELETE FROM Data.Inventory WHERE InventoryID = 3;
    
    -- the right way to update
    
    DECLARE @IncreaseQty INT;
    SET @IncreaseQty = 2;
    UPDATE Data.Inventory SET ChangeQty = ChangeQty + CASE WHEN ItemID = 1 AND ChangeDate = '20090103' THEN @IncreaseQty ELSE 0 END,
      TotalQty = TotalQty + @IncreaseQty,
      PreviousTotalQty = PreviousTotalQty + CASE WHEN ItemID = 1 AND ChangeDate = '20090103' THEN 0 ELSE @IncreaseQty END
    WHERE ItemID = 1 AND ChangeDate >= '20090103';
    
    SELECT ChangeDate,
      ChangeQty,
      TotalQty,
      PreviousChangeDate,
      PreviousTotalQty
    FROM Data.Inventory ORDER BY ChangeDate;
    
    ChangeDate              ChangeQty   TotalQty    PreviousChangeDate      PreviousTotalQty
    ----------------------- ----------- ----------- ----------------------- ----------------
    2009-01-01 00:00:00.000 10          10          NULL                    NULL
    2009-01-03 00:00:00.000 7           17          2009-01-01 00:00:00.000 10
    2009-01-04 00:00:00.000 3           20          2009-01-03 00:00:00.000 17
    2009-01-05 00:00:00.000 -4          16          2009-01-04 00:00:00.000 20
    
    • 7
  4. Philᵀᴹ
    2013-01-30T06:51:43+08:002013-01-30T06:51:43+08:00

    Esta é uma pergunta muito boa.

    Supondo que você tenha uma tabela de transações que armazene cada débito/crédito, não há nada de errado com seu design. Na verdade, trabalhei com sistemas de telecomunicações pré-pagos que funcionaram exatamente dessa maneira.

    A principal coisa que você precisa fazer é garantir que está fazendo uma revisão SELECT ... FOR UPDATEdo saldo enquanto faz INSERTo débito/crédito. Isso garantirá o equilíbrio correto se algo der errado (porque toda a transação será revertida).

    Como outros apontaram, você precisará de um instantâneo dos saldos em períodos específicos de tempo para verificar se todas as transações em um determinado período são somadas com os saldos de início/fim do período corretamente. Grave uma tarefa em lote que seja executada à meia-noite no final do período (mês/semana/dia) para fazer isso.

    • 6
  5. Stephen Senkomago Musoke
    2013-01-30T05:34:32+08:002013-01-30T05:34:32+08:00

    O saldo é um valor calculado com base em certas regras de negócios; portanto, sim, você não deseja manter o saldo, mas sim calculá-lo a partir das transações no cartão e, portanto, na conta.

    Você deseja acompanhar todas as transações no cartão para auditoria e relatórios de extratos e até mesmo dados de diferentes sistemas posteriormente.

    Resumindo - calcule todos os valores que precisam ser calculados como e quando você precisar

    • 4

relate perguntas

  • É melhor armazenar os valores calculados ou recalculá-los a pedido? [duplicado]

  • Armazenar vs calcular valores agregados

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

  • O que significa "índice" em RDBMSs? [fechado]

  • Quais são as diferenças entre o NoSQL e um RDBMS tradicional?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • 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

    Conceder acesso a todas as tabelas para um usuário

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

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