Sum credit and debit transactions where credits have expiries
Estoy construyendo un sistema de limitación que podemos usar para controlar cuántos correos electrónicos puede enviar un usuario desde nuestra plataforma. Se conceden automáticamente 10.000 emails al mes en su fecha de facturación. Y tienen la opción de comprar más en 5.000 paquetes de correo electrónico. Mi idea para esto era añadir el crédito límite y los débitos a una tabla, y luego utilizar SUM(amount)
para calcular cuál es su uso en cualquier momento dado.
Esto es complicado por algunos requisitos:
- El usuario recibe automáticamente 10.000 correos electrónicos por mes. Cuando calculamos el límite del usuario tal como está ahora mismo, tiene que mirar desde su última fecha de facturación en adelante.
- Los addons que un usuario puede comprar para aumentar su límite tienen una fecha de vencimiento, si no han utilizado la cantidad total acreditada por el addon por vencimiento, pierden la cantidad restante.
- Los addons deben persistir para siempre hasta que se utilicen, pero los 10.000 créditos por mes se reinician cada mes.
Actualmente, tengo esta estructura de tabla hecha para una prueba de concepto (la columna de info agregada sólo para explicar lo que está pasando):
Y esta consulta para calcular el límite actual del usuario en el momento de ejecutar la consulta:
SELECT SUM(amount) FROM (
SELECT SUM(credits.amount) as amount
FROM limit_transactions credits
WHERE date >= (
SELECT date FROM limit_transactions WHERE limit_transactions.billing_reset=1 ORDER BY limit_transactions.date DESC -- to start from where the billing cycle reset
)
AND credits.amount > 0 -- to only get credits
AND (credits.expiry IS NULL OR credits.expiry <= NOW()) -- to exclude expired credits
UNION ALL
SELECT SUM(debits.amount) as amount
FROM limit_transactions debits
WHERE date >= (
SELECT date FROM limit_transactions WHERE limit_transactions.billing_reset=1 ORDER BY limit_transactions.date DESC -- to start from where the billing cycle reset
)
AND debits.amount < 0 -- to only get debits
) as amount
Creo que esta consulta va en la dirección correcta, pero está dando un resultado incorrecto. Con el conjunto de datos dado vuelve 2000
, cuando debe regresar -3000
. Esto se debe a que esta parte de la consulta:
AND (credits.expiry IS NULL OR credits.expiry <= NOW()) -- to exclude expired credits
está eliminando completamente los addons que han caducado del cálculo general, en lugar de ser consciente de lo que los débitos han ocurrido mientras que ese addon no fue explorado.
¿Alguien ha implementado una solución como esta antes - o puede sugerir una mejor manera de modelar esta estructura de datos? La otra forma en que podía ir es almacenar créditos en esta tabla, y añadir un used
columna que rastrearía cuánto de ese crédito se ha utilizado. Si un usuario envía un correo electrónico, aumentaría el used
columna.
Pregunta hecha hace 3 años, 4 meses, 29 días - Por bytebard
2 Respuestas:
-
Para resolver este problema y calcular correctamente el límite actual del usuario teniendo en cuenta los créditos con fecha de vencimiento, se puede ajustar la consulta SQL de la siguiente manera:
sql SELECT SUM(sub.amount) as total_amount FROM ( SELECT SUM(credits.amount) as amount FROM limit_transactions credits WHERE credits.date >= ( SELECT date FROM limit_transactions WHERE billing_reset=1 ORDER BY date DESC LIMIT 1 ) AND credits.amount > 0 AND (credits.expiry IS NULL OR credits.expiry > NOW() OR credits.id IN ( SELECT DISTINCT credits.id FROM limit_transactions credits LEFT JOIN limit_transactions debits ON credits.id = debits.id WHERE credits.expiry <= NOW() AND debits.date >= credits.date )) UNION ALL SELECT SUM(debits.amount) as amount FROM limit_transactions debits WHERE debits.date >= ( SELECT date FROM limit_transactions WHERE billing_reset=1 ORDER BY date DESC LIMIT 1 ) AND debits.amount < 0 ) as sub;
En esta consulta, se ha ajustado la condición
AND (credits.expiry IS NULL OR credits.expiry <= NOW())
para incluir también los créditos con fecha de vencimiento que no han sido utilizados antes de expirar. Se ha añadido una subconsulta para verificar si hay debitos asociados a un crédito vencido que aún no se han restado de la cantidad total.Prueba esta consulta en tu base de datos para ver si ahora obtienes el resultado correcto. Si necesitas más ayuda, ¡házmelo saber!
Respondida el Dec 17, 2020 a las 19:06 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Solución con la tabla existente:
Para resolver este problema utilizando la estructura de tabla existente, puedes modificar la consulta para excluir solo los créditos caducados que tienen un débito posterior que utiliza parte de su cantidad:
sql SELECT SUM(amount) FROM ( SELECT SUM(credits.amount) AS amount FROM limit_transactions credits WHERE date >= ( SELECT date FROM limit_transactions WHERE limit_transactions.billing_reset = 1 ORDER BY limit_transactions.date DESC ) AND credits.amount > 0 AND ( credits.expiry IS NULL OR credits.expiry <= NOW() OR EXISTS( SELECT 1 FROM limit_transactions debits WHERE debits.date > credits.expiry AND debits.amount < 0 AND credits.transaction_id = debits.transaction_id ) ) UNION ALL SELECT SUM(debits.amount) AS amount FROM limit_transactions debits WHERE date >= ( SELECT date FROM limit_transactions WHERE limit_transactions.billing_reset = 1 ORDER BY limit_transactions.date DESC ) AND debits.amount < 0 ) AS amount
Esta consulta tiene en cuenta que un crédito caducado puede seguir siendo válido si se ha producido un débito después de su fecha de caducidad. Si ese es el caso, el crédito se incluye en la suma.
Solución alternativa con una columna "utilizada"
Si la estructura de la tabla anterior no es adecuada, puedes considerar una solución alternativa que utilice una columna "utilizada":
Agrega una columna
used
a la tablalimit_transactions
para rastrear cuánto de cada crédito se ha utilizado.Realiza un seguimiento de la cantidad utilizada cada vez que se envía un correo electrónico.
En cualquier momento, para calcular el límite actual, puedes consultar la suma de
amount
para créditos conamount > used
yexpiry IS NULL
oexpiry > NOW()
.Esta solución tiene la ventaja de eliminar cualquier ambigüedad sobre qué créditos están disponibles para su uso. Sin embargo, requiere que mantengas la columna
used
actualizada con precisión.Respondida el Dec 17, 2020 a las 19:09 - por Gemini
Votos positivos: 0 | Votos negativos: 0