Основной неэффективный способ сделать это состоит в том, чтобы получить user_id для каждой строки в ORDERS, где дата находится в целевом диапазоне И количество строк в ORDERS с тем же user_id и более низкой датой составляет ровно 24.
Хотя это может быть очень уродливо, очень быстро.
Если вы вызываете это из кода, которым вы управляете, вы не можете сделать это из кода?
Если нет, должен быть способ назначить каждой строке индекс, описывающий ее ранг среди заказов для его определенного user_id, и выбрать из этого все user_id из строк с индексом 25 и правильной датой.Это даст вам возможность выбора из выбора, но это должно быть намного быстрее.Сложность заключается в том, чтобы контролировать порядок строк, поэтому я представляю следующие селекты:
- Выбрать все строки, упорядочить по user_id asc, по дате asc, объединить до нуля из созданной таблицыиз двух переменных, которые вы инициализируете в 0.
- , выберите все при обновлении переменной, чтобы узнать, совпадает ли user_id строки с последней, и добавьте поле, которое будет сообщать об этом (так для каждогоuser_id первая строка в порядке будет иметь определенное значение, например 0, в то время как другие строки для того же user_id будут иметь 1)
- , выберите все, плюс равное себе поле плюс одно в случае добавления первогополе равно 1, иначе 0
- , из этого выберите user_id из строк, где второе добавленное поле равно 25, а дата находится в диапазоне.
Объединение необходимо толькоесли вам нужно сделать все это в одном запросе (вам нужно инициализировать их с более низким выбором, чем тот, в котором они используются).
Редактировать: Ну, если вам нужна дата, вы можете просто выбрать еевместес user_id, но вычисление количества дней в sql будет проблемой.Просто присоедините таблицу результатов к таблице пользователей и получите и дату 25-го заказа, и дату их регистрации, и вы наверняка сможете изменить код.Я попытаюсь составить реальный запрос, однако, если вы хотите действительно понять, что вам нужно для этого, вы должны прочитать о переменных, объединениях и условных выражениях mysql.
"Выглядит слишком сложно. Я уверен, чточто это можно сделать с помощью текущей структуры БД и 1-2 запросов.Ну, да.Используйте запрос COUNT, он будет легким и медленным до чертиков.
Сложный ответ см. http://www.sqlfiddle.com/#!9/998358/21
Поскольку вы можете использовать несколько запросов, вы можете сначала инициализировать переменные,Это на самом деле не сложно, вам просто нужно понять, как конкретно выразить то, что вы подразумеваете под «25-й командой пользователя», для механизма SQL.
См. http://www.sqlfiddle.com/#!9/998358/24 разницу в днях, оказывается, есть метод для этого.
Редактировать 5: кажется, вы идете с методом COUNT.Я буду молиться, чтобы ваша БД была маленькой.
Редактировать 6: Для потомков: метод подсчета займет годы на очень больших базах данных.Поскольку OP не вернулся, я предполагаю, что его достаточно мало, чтобы пропустить скорость запроса.Если это не ваш случай и, скажем, прошло 10 лет, а ссылки sqlfiddle не работают;Вот решение для двух запросов:
SET @PREV_USR:=0;
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
@RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE @RANK_USR+1 END) AS RANK FROM (
SELECT orders.*, CASE WHEN @PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
@PREV_USR:=user_id AS IGNORE_USR FROM
orders
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
Просто измените RANK =?и условия, соответствующие вашим потребностям.Если вы хотите полностью понять это, начните с самого внутреннего SELECT, затем продвигайтесь высоко;эта версия объединяет пункты 1 и 2. моего объяснения.
Теперь иногда вам придется использовать API или что-то еще, и это не позволит вам хранить значения переменных в памяти, если вы не передадите это или какое-то другое ограничение, и вынужно будет сделать это одним запросом.Для этого вы ставите инициализацию на один шаг ниже и делаете так, чтобы она не влияла на высшие операторы.IMO лучший способ сделать это в UNION с поддельной таблицей, где исключена только строка.Вы избежите хлопот JOIN, и это просто лучше в целом.
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
@RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE @RANK_USR+1 END) AS RANK FROM (
SELECT DERIVED_4.*, CASE WHEN @PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
@PREV_USR:=user_id AS IGNORE_USR FROM
(SELECT * FROM orders
UNION
SELECT * FROM (
SELECT (@PREV_USR:=0) AS INIT_PREV_USR, 0 AS COL_2, 0 AS COL_3
) AS DERIVED_3
WHERE INIT_PREV_USR <> 0
) AS DERIVED_4
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
При использовании этого метода нужно следить за количеством и типом столбцов в вашей базовой таблице.Здесь первое поле ордеров - int, поэтому сначала я помещаю INIT_PREV_USR, затем есть еще два поля, поэтому я просто добавляю два нуля с именами и называю это день.Большинство типов работают, так как объединение на самом деле ничего не делает, но я бы не стал это делать, когда вашим первым полем является BLOB-объект (в худшем случае вы можете использовать JOIN).
Вы заметитеэто получено из метода нумерации страниц в MySQL.Если вы хотите применить это к другим движкам, просто проверьте их лучшие вызовы нумерации страниц, и вы сможете уметь продумывать.