SQL-запрос для выбора, пока SUM (users_count) не достигнет 1000

Мне нужен sql-запрос для выбора строк из очереди сообщений, пока SUM (users_count) не достигнет не более 1000. НО нет проблем, если будет только одна строка, и эта строка users_count более 1000.

Мне нужно что-то вроде: (я добавил свои собственные ключевые слова)

SELECT * FROM `messages_queue` UNTIL SUM(users_count) < 1000 AT LEAST 1 ROW

Это моя структура таблицы:

messages_queue
  - msg_id
  - msg_body
  - users_count (количество повторителей сообщений)
  - время (время вставки)

Ответ 1

Это решение будет выполнять кумулятивную сумму, останавливаясь, когда сумма превышает 1000:

SELECT NULL AS users_count, NULL AS total
  FROM dual
 WHERE (@total := 0)
 UNION
SELECT users_count, @total := @total + users_count AS total
  FROM messages_queue
 WHERE @total < 1000;

Это означает, что если у вас есть два значения, скажем, 800, сумма будет равна 1600. Первый SELECT должен просто инициализировать переменную @total.

Если вы хотите, чтобы сумма не превышала 1000, кроме случаев, когда одна строка имеет значение больше 1000, то я думаю, что это работает, хотя вам нужно подвергнуть ее строгому тестированию:

SELECT NULL AS users_count, NULL AS total, NULL AS found
  FROM dual
 WHERE (@total := 0 OR @found := 0)
 UNION
SELECT users_count, @total AS total, @found := 1 AS found
  FROM messages_queue
 WHERE (@total := @total + users_count)
   AND @total < 1000
 UNION
SELECT users_count, users_count AS total, 0 AS found
  FROM messages_queue
 WHERE IF(@found = 0, @found := 1, 0);

Ответ 2

Я думаю, вы хотите сделать что-то вроде этого:

SELECT *
FROM
   (SELECT 
        *
      , (select sum(users_count) from `messages_queue` where time <= mq.time) RunningTotal       
   FROM `messages_queue` mq) mq2
WHERE mq2.RunningTotal < 1000

Ответ 3

Престижность Aducci для чистого SQL-решения, но, как сказал Томас Бергер, это может оказаться очень дорогостоящим запросом. В зависимости от размера вашей таблицы хранимая процедура может быть лучшим способом:

CREATE PROCEDURE messages_to_send
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE oldest_date DATETIME;
  DECLARE cur_count INT;
  DECLARE que_size INT DEFAULT 0;
  DECLARE curs CURSOR FOR SELECT users_count, time FROM messages_que ORDER BY time;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

  OPEN curs;

  read_loop: LOOP
    FETCH curs INTO cur_count, oldest_date;
    IF done THEN
      LEAVE read_loop;
    END IF;
    que_size = que_size + cur_count
    IF que_size >= 1000
      LEAVE read_loop;
    END IF;
  END LOOP;

  CLOSE curs

  SELECT * FROM messages_que WHERE time < oldest_date;
END

CALL messages_to_send(); --> returns a result set of messages to send with a total user_count of 1000 or less

Ответ 4

Я попытался добавить это как комментарий Майку, однако это проблематично с знаками @для переменных.

Чтобы нарисовать ответ Майка, запрос действительно мог быть сокращен путем инициализации переменной в предложении FROM, например:

SELECT users_count, @total := @total + users_count AS total
    FROM (messages_queue, (select @total := 0) t)
WHERE @total < 1000;

Ответ 5

Я не думаю, что вы могли бы с этим простым MySQL Query.

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

ИЗМЕНИТЬ

Я не MySQL Guru (мог только код хранимых процедур в oracle и postgres), но вы могли бы начать здесь: http://www.mysqltutorial.org/sql-cursor-in-stored-procedures.aspx.

Более общая информация о синтаксисе находится здесь: http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html