Итераторы базы данных в сочетании с пейджингом

мой вопрос носит концептуальный характер, но очень важен для меня:

При использовании баз данных вы обычно получаете итератор из запроса, содержащего набор результатов. Однако: когда вы хотите выполнять пейджинг, вы хотите иметь возможность перемещать этот набор результатов туда и обратно. однако это невозможно с использованием итератора. поэтому наивной идеей здесь было бы использовать преобразование в список. но это снова занимает время O (n), что может вызвать серьезные проблемы с производительностью.

Я уверен, что должно быть решение этой проблемы (кроме использования elasticsearch: D). Каков наилучший метод решения этой проблемы?

с наилучшими пожеланиями,

Стефан


person Stefan Kunze    schedule 16.10.2013    source источник


Ответы (1)


Вы хотите разбиение по страницам, но не хотите выполнять O(n) загрузку невыгружаемого набора результатов в память. Достаточно справедливо - логически это подразумевает, что база данных должна передавать вам выгружаемые фрагменты. Я думаю, что в большинстве баз данных RDMS есть что-то вроде «LIMIT» и «OFFSET» SQL:

select id, name from foo where date > ? LIMIT $start, $page_size;

Если бы вы имели дело с MySQL и писали сырой SQL, то это было бы примерно так. Но с такими библиотеками, как Slick, вы могли бы

val query = for { 
  d <- Parameter[Date]
  f <- foo if f.date > d
} yield (f.id, f.name)

Итак, чтобы получить все строки без страниц, вы делаете

query(yesterday).list
// Select id, name from foo

И если вы хотите пейджинг, это просто:

query(yesterday).drop(20).take(5).list
// Select id, name from foo limit 20, 5 ; %% whatever; I suck at SQL and can't remember syntac

                                          %% but you get the point.

Который вернет список из (Id, Name) из 5 элементов, если вы хотите только 5 на страницу. Это означает, что эта подпоследовательность будет 5-й страницей результатов.

Не то чтобы это то, что вы, вероятно, сделали бы, если бы вместо query(yesterday) у вас было List результатов в памяти: SLICK предоставляет вам абстракцию для запросов, тип Query, который содержит множество полезных методов, обычно встречающихся в коллекциях. Метод .list на самом деле выполняет окончательный запрос, чтобы получить List[T] (в этом примере List[(Int, String)]), но перед его вызовом вы можете «пролистнуть» свои результаты (путем вызова .take, drop и т. д., которые создают исходный запрос) и в этом например, SQL выполняет подкачку за вас, а SLICK генерирует этот SQL, поэтому вы просто делаете .take, .drop или что-то еще.

Это помогает, если уровень вашей модели использует возможности компоновки SLICK: вы определяете базовые запросы в SLICK вместо того, чтобы писать необработанный SQL, и эти запросы можно использовать в качестве строительных блоков для других запросов.

person Faiz    schedule 16.10.2013
comment
Slick не всегда генерирует оптимальные запросы, особенно для нескольких таблиц. - person Ashalynd; 16.10.2013
comment
Ограничение и смещение не обязательно работают хорошо, особенно если курсор очень большой. Насколько я понимаю, так что брось и возьми, я просто переведу на это, я полагаю? а также метод .list делает именно то, что я хотел предотвратить? - person Stefan Kunze; 16.10.2013