0

I have a complicated query. I have a bunch of users. I have a ledger of debits and credits. I want to filter a user's subscribers by how long the subscribers have been negative. To do that I find negative subscribers for a given user, get each subscriber's transactions in reverse order, and work backwards until the subscriber's balance is ≥ 0 to find how long ago that was.

The query works only if I take the list of subscriber IDs the query returns and add them into the WHERE clause.

The Query
-- Return just the info we want
SELECT subscriber_id,
       balance,
       days_negative,
       DATE_SUB(CURRENT_DATE, INTERVAL days_negative DAY) AS days_since
FROM (
    -- Reset @balanced for each subscriber
    SELECT r.subscriber_id,
           balance,
           (@balanced := FALSE),
           (
               -- get date of last transaction before balance >= 0
               SELECT DATEDIFF(CURRENT_DATE, MIN(transaction_time))
               FROM (
                   -- work backwards until balance >= 0
                   SELECT *,
                          ROUND(balance, 2),
                          ROUND(balance - running_total, 2),
                          (@balanced := @balanced OR balance - running_total + amount >= 0) AS balance_reached
                   FROM (
                       -- get transactions with running total
                       SELECT t.transaction_id,
                              t.transaction_time,
                              t.subscriber_id,
                              t.amount,
                              t.category,
                              SUM(t.amount) OVER w AS running_total
                       FROM transactions t
                       WHERE t.subscriber_id = r.subscriber_id
                         AND `t`.`void` = 0
                       WINDOW w AS (ORDER BY transaction_time DESC, transaction_id DESC)
                   ) with_running_total
               ) with_balance_reached
               WHERE balance_reached = 0
           ) AS days_negative
    FROM subscribers r
             INNER JOIN user_subscriber_linker usl USING (subscriber_id)
             INNER JOIN subscriber_balance b USING (subscriber_id)
    WHERE usl.user_id = 12345
      AND r.subscriber_id IN (987, 654, 321)
      AND r.void = 0
      AND b.balance < 0
    GROUP BY subscriber_id
) with_each;

This query gives me a result like this:

renter_id balance days_negative negative_since
987 -5998.90 98 2024-08-01
654 -2000.00 62 2024-09-06
321 -3774.20 79 2024-08-20

When I remove the line AND r.subscriber_id IN (987, 654, 321), I get this:

renter_id balance days_negative negative_since
987 -5998.90 98 2024-08-01
654 -2000.00 null null
321 -3774.20 null null

The fact that the first row is correct leads me to believe @balanced isn't being set for each row. I tried using joins (JOIN (SELECT @balanced := FALSE)) to set the var but that hasn't worked either.

How do I get the query to work?

3

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.