В запросе с объединениями и несколькими таблицами / полями ORDER BY, как задать смещение LIMIT для запуска из конкретной строки, идентифицируемой уникальным полем id? - PullRequest
1 голос
/ 21 апреля 2020

Предположим, у меня есть четыре таблицы: tbl1 ... tbl4. Каждый имеет уникальное числовое поле id. tbl1, tbl2 и tbl3 каждый имеет поле внешнего ключа для следующей таблицы в последовательности. Например, tbl1 имеет поле внешнего ключа tbl2_id и т. Д. В каждой таблице также есть поле order (и другие поля, не относящиеся к вопросу).

Нетрудно объединить все четыре таблицы, чтобы получить все строки таблицы tbl1 вместе с соответствующими полями из трех других полей. Также легко упорядочить этот набор результатов с помощью определенной комбинации c ORDER BY полей order. Также легко вернуть только строку, которая соответствует некоторому конкретному id в tbl1, например, WHERE tbl1.id = 7777.

ВОПРОС: какой запрос наиболее эффективно возвращает (например, 100 строк), начиная со строки соответствует id=7777, в порядке, определяемом заданной комбинацией c полей order ?

Использование ROW_NUMBER или (эмуляция этого в MySQL версии <8), чтобы получить положение строки id = 7777, а затем использование этого в новой версии того же запроса, чтобы установить смещение в предложении <code>LIMIT будет одним из подходов. (С блокировкой чтения между ними.) Но можно ли это сделать за один запрос?

# FIRST QUERY: get row number of result row where tbl1.id = 7777

SELECT x.row_number 
FROM 
   (SELECT @row_number:=@row_number+1 AS row_number, tbl1.id AS id
    FROM (SELECT @row_number:=0) AS t, tbl1
    INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
    INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
    INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
    WHERE <some conditions>
    ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
   ) AS x 
WHERE id=7777;

Сохранить номер строки из вышеприведенного запроса и использовать его для привязки :offset в следующем запросе.

# SECOND QUERY : Get 100 rows starting from the one with id=7777

SELECT x.field1, x.field2, <etc.> 
FROM 
  (SELECT @row_number:=@row_number+1 AS row_number, field1, field2
   FROM (SELECT @row_number:=0) AS t, tbl1
   INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
   INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
   INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
   WHERE <same conditions as before>
   ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
  ) AS x 
LIMIT :offset, 100;

1 Ответ

1 голос
/ 30 апреля 2020

Уточняющий вопрос

В общем случае вы не будете спрашивать WHERE id1 > 7777. Вместо этого у вас есть кортеж (11,22,33,44), и вы хотите «продолжить с того места, на котором остановились».

Два обсуждения, с

Это грязно, но не невозможно. См. Перебор составного ключа . Иг дает пример сделать это с 2 столбцами; 4 столбца, взятые из 4 таблиц, являются продолжением таких.

Вариант

Вот еще одно обсуждение такого: https://dba.stackexchange.com/questions/164428/should-i-store-data-pre-ordered-rather-than-ordering-on-the-fly/164755#164755

Реализуя такое, я обнаружил, что позволить «100» (LIMIT) быть гибким можно легче обдумать. Идея такова: достичь 100 строк вперед (с LIMIT 100,1). Допустим, вы получите (111,222,333,444). Если вы сейчас находитесь на (111, ...), то разберитесь с id2 / 3/4. Если это, скажем, (113, ...), тогда выполните WHERE id1 < 113 и пропустите все спецификации id2 / 3/4. Это означает, что нужно выбрать менее 100 строк, но вам просто не нужно начинать с id1 = 113.

То есть он включает в себя построение предложения WHERE с условиями от 1 до 4.

Во всех случаях ваш запрос говорит ORDER BY id1, id2, id3, id4. И единственное использование для LIMIT - в датчике, чтобы выяснить, насколько далеко впереди сотая строка (с LIMIT 100,1).

Я думаю, что могу выкопать какой-то старый Perl код для этого.

...