Запрос SQL для выбора, пока SUM (users_count) не достигнет 1000 - PullRequest
12 голосов
/ 17 августа 2011

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

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

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

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

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

Ответы [ 5 ]

14 голосов
/ 17 августа 2011

Это решение будет выполнять накопительную сумму, останавливаясь, когда сумма превышает 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 голосов
/ 10 июля 2012

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

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

SELECT users_count, @total := @total + users_count AS total
    FROM (messages_queue, (select @total := 0) t)
WHERE @total < 1000;
2 голосов
/ 17 августа 2011

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

SELECT *
FROM
   (SELECT 
        *
      , (select sum(users_count) from `messages_queue` where time <= mq.time) RunningTotal       
   FROM `messages_queue` mq) mq2
WHERE mq2.RunningTotal < 1000
1 голос
/ 17 августа 2011

Спасибо 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
0 голосов
/ 17 августа 2011

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

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

EDIT

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...