Получить строки из таблицы, чья сумма меньше значения - PullRequest
2 голосов
/ 02 октября 2019

Предположим, у меня есть следующие строки в таблице

|  id |   user_id   |  amount |      date      |
------------------------------------------------
|  1  |     1       |  100    |   2019-09-30   |
------------------------------------------------
|  2  |     2       |  100    |   2019-09-30   |
------------------------------------------------
|  3  |     1       |  100    |   2019-09-30   |
------------------------------------------------
|  4  |     3       |  100    |   2019-10-01   |
------------------------------------------------
|  5  |     1       |  75     |   2019-10-01   |
------------------------------------------------
|  6  |     3       |  100    |   2019-10-01   |
------------------------------------------------
|  7  |     1       |  35     |   2019-10-01   |
------------------------------------------------

Я пытаюсь найти способ получить все строки с user_id = 1, где sum(amount) < 300 и date <= '2019-10-01'.

Я пытаюсь обрабатывать только те записи, которые соответствуют определенной пороговой сумме. Я не совсем уверен, с чего начать.

Ожидаемый результат

|  id |   user_id   |  amount |      date      |
------------------------------------------------
|  1  |     1       |  100    |   2019-09-30   |
------------------------------------------------
|  3  |     1       |  100    |   2019-09-30   |
------------------------------------------------
|  5  |     1       |   75    |   2019-10-01   |
------------------------------------------------

Вот что я пробовал до сих пор

SELECT id, SUM(amount) as total_sum 
   FROM table 
   WHERE date <= '2019-10-01' AND user_id = 1
     ORDER BY date ASC
   HAVING total_sum <= 300

Iне получить желаемый результат на основе вышеуказанного запроса.

Версия MySQL, в настоящее время использующая: 5.7.25

Я посмотрел на этот вопрос MySQL выбирает записи с суммой, превышающей порог , предполагая, что они пытаются сделать то же самое,но это не то, на что я смотрю

Ответы [ 2 ]

2 голосов
/ 02 октября 2019

Это проблема скользящей суммы. В MySQL 8.0.2 и выше вы можете решить эту проблему, используя оконные функции с фреймами . В более старых версиях мы можем сделать то же самое, используя пользовательские переменные сеанса.

  • Сначала мы вычисляем скользящую сумму, используя переменные сеанса .
  • Затем используемнабор результатов в Производная таблица и найдите идентификатор, где общая сумма пересекает «барьер» 300. Барьер достигается, когда Новая скользящая сумма больше 300. Мы устанавливаем значение барьера равным 1в этот момент 0 для строк перед ним и 2 и более для строк после.
  • Мы будем рассматривать только те строки, где барьер равен 0.

Try (работает длявсе версии MySQL):

Запрос № 1

SELECT dt.id, 
       dt.user_id, 
       dt.amount, 
       dt.date
FROM 
(
  SELECT 
    t.id, 
    t.user_id, 
    t.amount, 
    t.date, 
    @barrier := CASE
                  WHEN 
                       (@tot_qty := @tot_qty + t.amount) > 300
                  THEN (@barrier + 1)
                  ELSE 0
                END AS barrier 
  FROM 
    your_table AS t 
  CROSS JOIN (SELECT @tot_qty := 0, 
                     @barrier := 0) AS user_init
    WHERE t.user_id = 1 
        AND t.date <= '2019-10-01' 
  ORDER BY t.user_id, t.date, t.id
) AS dt 
WHERE dt.barrier = 0 
ORDER BY dt.user_id, dt.date, dt.id;

Результат

| id  | user_id | amount | date       |
| --- | ------- | ------ | ---------- |
| 1   | 1       | 100    | 2019-09-30 |
| 3   | 1       | 100    | 2019-09-30 |
| 5   | 1       | 75     | 2019-10-01 |

Просмотр БД Fiddle

1 голос
/ 02 октября 2019

Если вам не нравится использовать переменные сеанса (некоторым опытным пользователям SO они сильно не нравятся), вы можете использовать технику, основанную на «Self-Join», а затем использовать GROUP BY с HAVING для фильтрации.

Общая идея состоит в том, что мы оставили соединение, чтобы получить предыдущие строки для определенного user_id, а затем агрегировали, чтобы получить скользящую сумму, и затем отфильтровали с помощью предложения Have.

Запрос

SELECT 
  t1.* 
FROM 
  your_table AS t1 
LEFT JOIN your_table AS t2 
  ON t2.user_id = t1.user_id 
     AND t2.date <= t1.date 
     AND t2.id <= t1.id 
WHERE t1.user_id = 1 
  AND t1.date <= '2019-10-31'
GROUP BY t1.user_id, t1.date, t1.id, t1.amount
HAVING COALESCE(SUM(t2.amount),0) < 300;

Результат

| id  | user_id | amount | date       |
| --- | ------- | ------ | ---------- |
| 1   | 1       | 100    | 2019-09-30 |
| 3   | 1       | 100    | 2019-09-30 |
| 5   | 1       | 75     | 2019-10-01 |

Просмотр на БД Fiddle

Вы можете выполнить тестоба подхода и решить, какой из них подходит.

Для этого запроса вам потребуется составной индекс: (user_id, date)

...