2016-11-11 2 views
1

У меня есть служба REST, которая возвращает строки из таблицы базы данных в зависимости от текущей страницы и результатов на странице.mySQL - разбиение страниц на отфильтрованные строки

Не фильтруя результаты, это довольно легко сделать, я просто делаю SELECT WHERE id> = (страница - 1) * perPage + 1 и LIMIT для perPage.

Проблема заключается в попытке использовать разбиение на страницы на отфильтрованные результаты, например. если я хочу фильтровать только строки WHERE type = someType.

В этом случае первое совпадение первой страницы может начинаться с идентификатора 7, а последнее может быть в идентификаторе 5046. Тогда первое совпадение второй страницы может начинаться с 7302 и заканчиваться на этапе 12430 и т. Д. ,

Для первой страницы отфильтрованных результатов я мог бы просто начать с id 1 и LIMIT до perPage, но для второй страницы и т. Д. Мне нужно знать индекс последней совпадающей строки в предыдущем страницы или даже лучше - первая совпадающая строка на текущей странице или другое указание.

Как это сделать эффективно? Мне нужно иметь возможность делать это на таблицах с миллионами строк, поэтому, очевидно, выборка всех строк и их оттуда не является вариантом.

Идея что-то вроде этого:

SELECT ... FROM ... WHERE filterKey = filterValue AND id >= id_of_first_match_in_current_page

с id_of_first_match_in_current_page быть тайной.

+1

Это кажется странным способом сделать это. Стандартом для небольших/средних наборов данных в моей экспирации является ORDER BY по строкам, поэтому вы получаете согласованный набор данных (так что вы можете использовать все, что есть), затем используйте LIMIT и, как я подозреваю, вы, возможно, пропустили, предложение OFFSET для сообщите MySQL, чтобы они возвращали строки из позиции X. –

+0

Насколько велик размер отфильтрованных данных? – Strawberry

+0

Джон - Я понимаю, я не был знаком с СМЕЩЕНИЕМ, спасибо. Клубника - я хотел бы получить что-то вроде 200 строк за раз, от фильтрованных результатов, которые могут доходить до сотен тысяч. –

ответ

2

Вы не можете знать, что такое первый идентификатор на данной странице, потому что номера идентификаторов не обязательно являются последовательными. Другими словами, в последовательности могут быть пробелы, поэтому строки на пятой странице из 100 строк не обязательно начинаются с идентификатора 500. Например, он может начинаться с id 527. Это невозможно узнать.

Указанный еще один способ: id - это значение, а не номер строки.

Одно из возможных решений, если ваш клиент продвигается через страницу в порядке возрастания, что каждый запрос REST извлекают данные, отмечает наибольшее значения идентификатора на этой странице, а затем использует, что в следующем запросе покоятся поэтому запросы значения id больше.

SELECT ... FROM ... WHERE filterKey = filterValue 
AND id > id_of_last_match_of_previous_page 

Но если ваш запрос REST может получить любую случайную страницу, это решение не будет работать. Это зависит от того, что вы уже сделали предварительную страницу.

Другим решением является использование синтаксиса LIMIT <x> OFFSET <y>. Это позволяет запросить любую произвольную страницу. LIMIT <y>, <x> работает одинаково, но по какой-то причине x и y обращаются в двух разных синтаксических формах, поэтому имейте это в виду.

Использование LIMIT...OFFSET не очень эффективно, когда вы запрашиваете страницу с большим количеством страниц в результате. Скажем, вы запрашиваете 5000-ю страницу. MySQL должен сгенерировать результат на стороне сервера из 5000 страниц, затем отбросить 4 999 из них и вернуть последнюю страницу в результате. Извините, но так оно и работает.


Re ваш комментарий:

Вы должны понимать, что WHERE применяет условия на значений в строках, но страницы определяются позиции строк. Это два разных способа определения строк!

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

Но значения первичного ключа могут меняться и не быть последовательными, например, если вы обновляете или удаляете строки или откатываете некоторые транзакции и т. Д. Перенумерация значений первичного ключа является плохой идеей, потому что другие таблицы или внешние данные могут ссылаться на значения первичного ключа.

Таким образом, вы можете добавить еще один столбец , а не первичный ключ, но только номер строки.

ALTER TABLE MyTable ADD COLUMN row_number BIGINT UNSIGNED, ADD KEY (row_number); 

Затем заполните значения, когда вам нужно изменить нумерацию строк.

SET @row := 0; 
UPDATE MyTable SET row_number = (@row := @row + 1) ORDER BY id; 

Вам нужно будет повторно указать строки, если вы когда-нибудь удалите их, например. Это не эффективно делать это часто, в зависимости от размера таблицы.

Кроме того, новые вставки не могут создавать правильные значения числа строк без блокировки таблицы. Это необходимо для предотвращения условий гонки.

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

SELECT * FROM MyTable WHERE row_number BETWEEN 401 AND 500; 

По крайней мере, до следующего раза последовательность номеров строк ставится под сомнение удалением или новыми вставками.

+0

Спасибо за подробный ответ. Есть ли эффективный подход, который позволяет мне запрашивать любую произвольную страницу? В наши дни со всеми достижениями в больших данных кто-то должен был что-то придумать. –

+0

Re your edit - Отличный ответ. Большое спасибо. –

1

Вы используете колонку ID для неправильной цели. Идентификатором является идентификатор записи, а не порядковый номер записи для любого заданного набора результатов.

Ключевое слово LIMIT распространяется на базовое разбиение на страницы. Если вы просто хотели первые 10 записей, вы могли бы сделать что-то вроде:

LIMIT 10 

Для постраничного, если вы хотели, вторых 10 записей, вы могли бы сделать:

LIMIT 10,10 

10- после чего:

LIMIT 20,10 

И так далее.

Статья LIMIT не зависит от положения WHERE. Используйте WHERE, чтобы отфильтровать результаты, используйте LIMIT, чтобы разбивать их на страницы.

Смежные вопросы