Escribir un esquema bancario simple: ¿Cómo debo mantener mis saldos sincronizados con su historial de transacciones?


57

Estoy escribiendo el esquema para una base de datos bancaria simple. Aquí están las especificaciones básicas:

  • La base de datos almacenará transacciones contra un usuario y moneda.
  • Cada usuario tiene un saldo por moneda, por lo que cada saldo es simplemente la suma de todas las transacciones contra un usuario y moneda determinados.
  • Un saldo no puede ser negativo.

La aplicación bancaria se comunicará con su base de datos exclusivamente a través de procedimientos almacenados.

Espero que esta base de datos acepte cientos de miles de transacciones nuevas por día, así como consultas de saldo en un orden de magnitud más alto. Para servir saldos muy rápidamente, necesito agregarlos previamente. Al mismo tiempo, necesito garantizar que un saldo nunca contradiga su historial de transacciones.

Mis opciones son:

  1. Tenga una balancesmesa separada y realice una de las siguientes acciones:

    1. Aplicar transacciones a las tablas transactionsy balances. Usar la TRANSACTIONlógica en mi capa de procedimiento almacenado para garantizar que los saldos y las transacciones estén siempre sincronizados. (Apoyado por Jack )

    2. Aplique transacciones a la transactionstabla y tenga un activador que actualice la balancestabla para mí con el monto de la transacción.

    3. Aplique transacciones a la balancestabla y tenga un activador que agregue una nueva entrada en la transactionstabla para mí con el monto de la transacción.

    Tengo que confiar en enfoques basados ​​en la seguridad para asegurarme de que no se puedan realizar cambios fuera de los procedimientos almacenados. De lo contrario, por ejemplo, algún proceso podría insertar directamente una transacción en la transactionstabla y, según el esquema, 1.3el saldo relevante no estaría sincronizado.

  2. Tener una balancesvista indizada que agregue las transacciones de manera adecuada. El motor de almacenamiento garantiza que los saldos estén sincronizados con sus transacciones, por lo que no necesito confiar en enfoques basados ​​en la seguridad para garantizar esto. Por otro lado, ya no puedo exigir que los saldos ya no sean negativos, ya que las vistas, incluso las vistas indexadas, no pueden tener CHECKrestricciones. (Apoyado por Denny .)

  3. Tenga solo una transactionstabla pero con una columna adicional para almacenar el saldo efectivo inmediatamente después de la transacción ejecutada. Por lo tanto, el último registro de transacción para un usuario y moneda también contiene su saldo actual. (Sugerido a continuación por Andrew ; variante propuesta por garik .)

Cuando abordé este problema por primera vez, leí estas dos discusiones y decidí la opción 2. Como referencia, puede ver una implementación básica aquí .

  • ¿Ha diseñado o administrado una base de datos como esta con un perfil de alta carga? ¿Cuál fue su solución a este problema?

  • ¿Crees que he tomado la decisión de diseño correcta? ¿Hay algo que deba tener en cuenta?

    Por ejemplo, sé que los cambios de esquema en la transactionstabla requerirán que reconstruya la balancesvista. Incluso si estoy archivando transacciones para mantener la base de datos pequeña (por ejemplo, moviéndolas a otro lugar y reemplazándolas con transacciones sumarias), tener que reconstruir la vista de decenas de millones de transacciones con cada actualización de esquema probablemente significará significativamente más tiempo de inactividad por implementación.

  • Si la vista indexada es el camino a seguir, ¿cómo puedo garantizar que ningún saldo sea negativo?


Archivado de transacciones:

Permítanme elaborar un poco sobre el archivo de transacciones y las "transacciones resumidas" que mencioné anteriormente. Primero, el archivado regular será una necesidad en un sistema de alta carga como este. Quiero mantener la coherencia entre los saldos y sus historiales de transacciones al tiempo que permitir que las transacciones antiguas se trasladen a otro lugar. Para hacer esto, reemplazaré cada lote de transacciones archivadas con un resumen de sus montos por usuario y moneda.

Entonces, por ejemplo, esta lista de transacciones:

user_id    currency_id      amount    is_summary
------------------------------------------------
      3              1       10.60             0
      3              1      -55.00             0
      3              1      -12.12             0

se archiva y se reemplaza con esto:

user_id    currency_id      amount    is_summary
------------------------------------------------
      3              1      -56.52             1

De esta manera, un saldo con transacciones archivadas mantiene un historial de transacciones completo y consistente.


1
Si elige la opción 2 (que creo que es más limpia), eche un vistazo a pgcon.org/2008/schedule/attachments/… cómo implementar "vistas materializadas" de manera eficiente. Para la opción 1, el capítulo 11 de Matemáticas aplicadas de Haan y Koppelaars para profesionales de bases de datos (no se preocupe por el título) sería útil para tener una idea de cómo implementar "restricciones de transición" de manera eficiente. El primer enlace es para PostgreSQL y el segundo para Oracle, pero las técnicas deberían funcionar para cualquier sistema de base de datos razonable.
jp

En teoría, quieres hacer el # 3. La forma correcta de hacer un "saldo corriente" es asignar un saldo a cada transacción. Asegúrese de que puede ordenar las transacciones definitivamente con una ID de serie (preferida) o una marca de tiempo. Realmente no se supone que "calcules" un saldo corriente.
pbreitenbach

Respuestas:


15

No estoy familiarizado con la contabilidad, pero resolví algunos problemas similares en entornos de tipo inventario. Almacene totales acumulados en la misma fila con la transacción. Estoy usando restricciones, por lo que mis datos nunca están equivocados, incluso bajo alta concurrencia. En 2009 escribí la siguiente solución :

El cálculo de los totales acumulados es notoriamente lento, ya sea que lo haga con un cursor o con una unión triangular. Es muy tentador desnormalizar, almacenar totales acumulados en una columna, especialmente si lo selecciona con frecuencia. Sin embargo, como es habitual cuando se desnormaliza, debe garantizar la integridad de sus datos desnormalizados. Afortunadamente, puede garantizar la integridad de los totales acumulados con restricciones: siempre y cuando todas sus restricciones sean confiables, todos sus totales correctos son correctos. También de esta manera puede asegurarse fácilmente de que el saldo actual (totales acumulados) nunca sea negativo: la aplicación por otros métodos también puede ser muy lenta. El siguiente guión demuestra la 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)
  )
);

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

14

No permitir que los clientes tengan un saldo inferior a 0 es una regla comercial (que cambiaría rápidamente ya que las comisiones por cosas como el giro bancario son la forma en que los bancos obtienen la mayor parte de su dinero). Querrá manejar esto en el procesamiento de la aplicación cuando se inserten filas en el historial de transacciones. Especialmente porque puede terminar con algunos clientes que tienen protección contra sobregiros y algunos cobran tarifas y algunos no permiten ingresar cantidades negativas.

Hasta ahora me gusta a dónde vas con esto, pero si esto es para un proyecto real (no para la escuela), es necesario pensar mucho en las reglas de negocios, etc. Una vez que tienes un sistema bancario en funcionamiento y en funcionamiento no hay mucho espacio para el rediseño, ya que hay leyes muy específicas sobre las personas que tienen acceso a su dinero.


1
Puedo ver por qué la restricción de saldos debería ser una regla de negocios. La base de datos solo proporciona un servicio de transacciones, y depende del usuario decidir qué hacer con ella.
Nick Chammas

¿Qué opina de los comentarios de Jack de que el uso de las dos tablas ofrece a los desarrolladores más flexibilidad para cambiar o implementar la lógica empresarial? Además, ¿tiene alguna experiencia directa con vistas indexadas que valide o desafíe estas preocupaciones ?
Nick Chammas

1
No diría que tener dos tablas le brinde flexibilidad de movimiento es implementar la lógica empresarial. Le brinda más flexibilidad para archivar datos. Sin embargo, como banco (al menos en los EE. UU.), Tiene leyes que indican la cantidad de datos que necesita conservar. Deberá probar cómo se ve el rendimiento con la vista en la parte superior, así como tener en cuenta que si tiene una vista indexada no puede cambiar el esquema de las tablas subyacentes. Solo otra cosa en que pensar.
mrdenny

Todos los elementos mencionados en el artículo son preocupaciones válidas cuando se usa una vista indizada.
mrdenny

1
Para aclarar: IMO, una API transaccional brinda más flexibilidad para implementar la lógica de negocios (no tener dos tablas). En este caso, también estaría a favor de dos tablas (al menos dada la información que tenemos hasta ahora) debido a las compensaciones propuestas con el enfoque de vista indizada (por ejemplo, no puede usar DRI para hacer cumplir el balance> 0 negocios regla)
Jack Douglas

13

Un enfoque ligeramente diferente (similar a su segunda opción) a considerar es tener solo la tabla de transacciones, con una definición de:

CREATE TABLE Transaction (
      UserID              INT
    , CurrencyID          INT 
    , TransactionDate     DATETIME  
    , OpeningBalance      MONEY
    , TransactionAmount   MONEY
);

También es posible que desee un ID / pedido de transacción, para que pueda manejar dos transacciones con la misma fecha y mejorar su consulta de recuperación.

Para obtener el saldo actual, todo lo que necesita es el último registro.

Métodos para obtener el último registro :

/* For a single User/Currency */
Select TOP 1 *
FROM dbo.Transaction
WHERE UserID = 3 and CurrencyID = 1
ORDER By TransactionDate desc

/* For multiple records ie: to put into a view (which you might want to index) */
SELECT
    C.*
FROM
    (SELECT 
        *, 
        ROW_NUMBER() OVER (
           PARTITION BY UserID, CurrencyID 
           ORDER BY TransactionDate DESC
        ) AS rnBalance 
    FROM Transaction) C
WHERE
    C.rnBalance = 1
ORDER BY
    C.UserID, C.CurrencyID

Contras:

  • Al insertar una transacción fuera de secuencia (es decir, para corregir una emisión / saldo inicial incorrecto), es posible que deba actualizar en cascada todas las transacciones posteriores.
  • Las transacciones para el Usuario / Moneda deberían ser serializadas para mantener un saldo exacto.

    -- Example of getting the current balance and locking the 
    -- last record for that User/Currency.
    -- This lock will be freed after the Stored Procedure completes.
    SELECT TOP 1 @OldBalance = OpeningBalance + TransactionAmount  
    FROM dbo.Transaction with (rowlock, xlock)   
    WHERE UserID = 3 and CurrencyID = 1  
    ORDER By TransactionDate DESC;

Pros:

  • Ya no tiene que mantener dos tablas separadas ...
  • Puede validar fácilmente el saldo, y cuando el saldo se desincroniza, puede identificar exactamente cuándo salió de control cuando el historial de transacciones se auto documenta.

Editar: Algunas consultas de muestra sobre la recuperación del saldo actual y resaltar estafa (Gracias @Jack Douglas)


3
El SELECT TOP (1) ... ORDER BY TransactionDate DESCva a ser muy difícil de poner en práctica de tal manera que SQL Server no analiza constantemente la tabla de transacciones. Alex Kuznetsov publicó una solución aquí para un problema de diseño similar que complementa perfectamente esta respuesta.
Nick Chammas

2
+1 Estoy usando un enfoque similar. Por cierto, debemos ser muy cuidadosos y asegurarnos de que nuestro código funcione correctamente bajo una carga de trabajo concurrente.
AK

12

Después de leer estas dos discusiones, decidí la opción 2

Después de leer esas discusiones también, no estoy seguro de por qué decidió la solución DRI sobre la más sensata de las otras opciones que describe:

Aplique transacciones a las tablas de transacciones y saldos. Usar la lógica de TRANSACCIÓN en mi capa de procedimiento almacenado para garantizar que los saldos y las transacciones estén siempre sincronizados.

Este tipo de solución tiene inmensos beneficios prácticos si tiene el lujo de restringir todo el acceso a los datos a través de su API transaccional. Pierde el beneficio muy importante de DRI, que es que la integridad está garantizada por la base de datos, pero en cualquier modelo de suficiente complejidad habrá algunas reglas comerciales que DRI no puede hacer cumplir .

Aconsejaría usar DRI cuando sea posible para hacer cumplir las reglas de negocio sin doblar demasiado su modelo para que eso sea posible:

Incluso si estoy archivando transacciones (por ejemplo, moviéndolas a otro lugar y reemplazándolas con transacciones resumidas)

Tan pronto como empiece a considerar contaminar su modelo de esta manera, creo que se está mudando al área donde el beneficio de DRI se ve compensado por las dificultades que está presentando. Considere, por ejemplo, que un error en su proceso de archivado podría, en teoría, hacer que su regla de oro (que los saldos siempre sean iguales a la suma de las transacciones) se rompa en silencio con una solución DRI .

Aquí hay un resumen de las ventajas del enfoque transaccional tal como las veo:

  • Deberíamos estar haciendo esto de todos modos si es posible. Cualquiera sea la solución que elija para este problema en particular, le brinda más flexibilidad de diseño y control sobre sus datos. Todo acceso se convierte en "transaccional" en términos de lógica empresarial, en lugar de solo en términos de lógica de base de datos.
  • Puedes mantener tu modelo ordenado
  • Puede "imponer" un rango y una complejidad mucho más amplia de las reglas comerciales (observando que el concepto de "imponer" es más flexible que con DRI)
  • Todavía puede usar DRI siempre que sea práctico para darle al modelo una integridad subyacente más sólida, y esto puede actuar como un control de su lógica transaccional
  • La mayoría de los problemas de rendimiento que te preocupan desaparecerán
  • Introducir nuevos requisitos puede ser mucho más fácil, por ejemplo: las reglas complejas para transacciones en disputa pueden obligarlo a alejarse de un enfoque DRI puro más adelante, lo que significa un gran esfuerzo desperdiciado
  • Particionar o archivar datos históricos se vuelve mucho menos arriesgado y doloroso

--editar

Para permitir el archivado sin agregar complejidad o riesgo, puede optar por mantener las filas de resumen en una tabla de resumen separada, generada continuamente (tomando prestado de @Andrew y @Garik)

Por ejemplo, si los resúmenes son mensuales:

  • cada vez que hay una transacción (a través de su API) hay una actualización o inserción correspondiente en la tabla de resumen
  • la tabla de resumen nunca se archiva, pero el archivado de transacciones se vuelve tan simple como eliminar (¿o eliminar partición?)
  • cada fila en la tabla de resumen incluye 'saldo inicial' y 'monto'
  • las restricciones de verificación como 'saldo inicial' + 'cantidad'> 0 y 'saldo inicial'> 0 se pueden aplicar a la tabla de resumen
  • se podrían insertar filas de resumen en un lote mensual para facilitar el bloqueo de la última fila de resumen (siempre habrá una fila para el mes actual)

Con respecto a su edición: ¿Entonces propone tener esta tabla de resumen junto a la tabla de saldos principales? ¿La tabla de saldos se convierte efectivamente en una tabla de resumen que solo tiene los registros del mes actual (ya que ambos almacenarán el mismo tipo de datos)? Si lo he entendido correctamente, ¿por qué no simplemente reemplazar la tabla de saldos con la partición apropiada en la tabla de resumen?
Nick Chammas

Lo siento, tiene razón, eso no está claro: me refería a dispensar con la tabla de saldos, ya que siempre sería una búsqueda clave en la tabla de resumen para obtener el saldo actual (no es cierto con la sugerencia de Andrews AFAIK). La ventaja es que calcular saldos en momentos anteriores se vuelve más fácil y hay un rastro de auditoría más claro para los saldos si salen mal.
Jack Douglas

6

Mella.

La idea principal es almacenar el saldo y los registros de transacciones en la misma tabla. Sucedió históricamente, pensé. Entonces, en este caso, podemos obtener el equilibrio simplemente ubicando el último registro de resumen.

 id   user_id    currency_id      amount    is_summary (or record_type)
----------------------------------------------------
  1       3              1       10.60             0
  2       3              1       10.60             1    -- summary after transaction 1
  3       3              1      -55.00             0
  4       3              1      -44.40             1    -- summary after transactions 1 and 3
  5       3              1      -12.12             0
  6       3              1      -56.52             1    -- summary after transactions 1, 3 and 5 

Una mejor variante es la disminución del número de registros de resumen. Podemos tener un registro de saldo al final (y / o comienzo) del día. Como saben, cada banco tiene operational dayque abrirlo y luego cerrarlo para hacer algunas operaciones de resumen para este día. Nos permite calcular fácilmente los intereses utilizando el registro de saldo diario, por ejemplo:

user_id    currency_id      amount    is_summary    oper_date
--------------------------------------------------------------
      3              1       10.60             0    01/01/2011 
      3              1      -55.00             0    01/01/2011
      3              1      -44.40             1    01/01/2011 -- summary at the end of day (01/01/2011)
      3              1      -12.12             0    01/02/2011
      3              1      -56.52             1    01/02/2011 -- summary at the end of day (01/02/2011)

Suerte.


4

Según sus requisitos, la opción 1 sería la mejor. Aunque tendría mi diseño para permitir solo inserciones en la tabla de transacciones. Y tenga el activador en la tabla de transacciones, para actualizar la tabla de saldo en tiempo real. Puede usar los permisos de la base de datos para controlar el acceso a estas tablas.

En este enfoque, se garantiza que el saldo en tiempo real esté sincronizado con la tabla de transacciones. Y no importa si se utilizan procedimientos almacenados o psql o jdbc. Puede tener su cheque de saldo negativo si es necesario. El rendimiento no será un problema. Para obtener el saldo en tiempo real, es una consulta única.

El archivado no afectará este enfoque. Puede tener una tabla de resumen semanal, mensual, anual también si es necesario para cosas como informes.


3

En Oracle, puede hacer esto usando solo la tabla de transacciones con una Vista materializada que se puede actualizar rápidamente y que hace la agregación para formar el saldo. Usted define el disparador en la Vista materializada. Si la Vista materializada se define con 'EN COMPROMISO', efectivamente evita agregar / modificar datos en las tablas base. El activador detecta los datos [en] válidos y genera una excepción, donde revierte la transacción. Un buen ejemplo está aquí http://www.sqlsnippets.com/en/topic-12896.html

No conozco sqlserver, pero ¿tal vez tiene una opción similar?


2
Las vistas materializadas en Oracle son similares a la "vista indizada" de SQL Server, pero se actualizan automáticamente en lugar de una forma explícitamente administrada, como el comportamiento 'ON COMMIT' de Oracle. Ver social.msdn.microsoft.com/Forums/fi-FI/transactsql/thread/… y techembassy.blogspot.com/2007/01/…
GregW
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.