Предположим, нам требуется сформировать выборку, в которой вместе с информацией об образце указывается глубина отбора следующего (по глубине) образца.
Построим запрос, который выполняет следующие действия.
Все пары образцов можно получить используя соединение таблицы sample с самой собой. Например, с помощью CROSS JOIN:
SELECT *
FROM sample CROSS JOIN sample AS second_sample
Нас интересуют такие пары, которые были отобраны из одной скважины. Это условие может быть указано либо в выражении WHERE, либо в условии соединения [INNER] JOIN. Воспользуемся вторым вариантом. Заметим, что CROSS JOIN был заменен на JOIN с условием. Также нам нужно условие на сравненеие глубин: второй образец отобран с большей глубины. Будем выбирать из ьаблицы только идентификаторы и глубины отбора образцов. Получаем следующий запрос.
SELECT sample.sample_id AS id,
sample.sample_depth AS depth,
second_sample.sample_id AS id2,
second_sample.sample_depth AS depth2
FROM sample JOIN sample AS second_sample ON sample.borehole_id = second_sample.borehole_id
WHERE second_sample.sample_depth > sample.sample_depth
Таким образом мы получили пары образцов.
Можно заметить, что для образца с id=1 в получившейся выборке есть несколько записей, по одной для каждого образца из этой скважины, но с большей глубиной отбора. Нам нужно оставить записи, соответсвующие "ближайшему" образцу.
Вместо удаления лишних записей мы будем вычислять нужное значение. Для этого разделим записи на группы по значению id1 и в каждой группе вычислим минимальное значение depth2.
Для наглядности, вместо усложнения предыдущего запроса или использования вложенных запросов, будем использовать выражение WITH, которое позволит обращаться к данным предыдущего запроса по имени deeper_sample. Также добавим в выборку идентификатор скважины и отсорируем результат по скважине и глубине отбора первого образца из пары.
WITH deeper_sample AS (
SELECT sample.sample_id AS id1,
sample.sample_depth AS depth1,
sample.borehole_id,
second_sample.sample_id AS id2,
second_sample.sample_depth AS depth2
FROM sample JOIN sample AS second_sample ON sample.borehole_id = second_sample.borehole_id
WHERE second_sample.sample_depth > sample.sample_depth
)
SELECT id1,
MIN(depth1) AS depth1,
MIN(depth2) AS next_deeper
FROM deeper_sample
GROUP BY id1
ORDER BY borehole_id, depth1
Упражнение: перепишите приведённый запрос без использования конструкции WITH.
Последняя версия нашего запроса содержит один изъян: образец с максимальной глубиной отбора отсутствует в выборке. Это связано с тем, что для него нет парной записи.
Для включения в выпорку пар образцов записей, соответствующих образцам с максимальной глубиной отбора, необходимо заменить JOIN на LEFT JOIN. Однако, если мы просто добавим ключевое слово LEFT результат выполнения запроса не изменится.
SELECT sample.sample_id AS id,
sample.sample_depth AS depth,
second_sample.sample_id AS id2,
second_sample.sample_depth AS depth2
FROM sample
LEFT JOIN /* <-------- Замена JOIN на LEFT JOIN */
sample AS second_sample ON sample.borehole_id = second_sample.borehole_id
WHERE second_sample.sample_depth > sample.sample_depth
Проблема в том, что условие WHERE second_sample.sample_depth ... требует, чтобы запись в табице second_sample существовала.
Исправить сиуацию можно перемещением условия из выражения WHERE в выражение LEFT JOIN.
SELECT sample.sample_id AS id,
sample.sample_depth AS depth,
second_sample.sample_id AS id2,
second_sample.sample_depth AS depth2
FROM sample
LEFT JOIN
sample AS second_sample
ON sample.borehole_id = second_sample.borehole_id
AND second_sample.sample_depth > sample.sample_depth
WITH deeper_sample AS (
SELECT sample.sample_id AS id1,
sample.sample_depth AS depth1,
sample.borehole_id,
second_sample.sample_id AS id2,
second_sample.sample_depth AS depth2
FROM sample
LEFT JOIN
sample AS second_sample
ON sample.borehole_id = second_sample.borehole_id
AND second_sample.sample_depth > sample.sample_depth
)
SELECT id1,
MIN(depth1) AS depth1,
MIN(depth2) AS next_deeper
FROM deeper_sample
GROUP BY id1
-- HAVING MIN(depth2) IS NULL /* Для показа только самых глубоких образцов */
ORDER BY borehole_id, depth1
Чтобы увидеть записи с максимальной глубиной отбора первого образца можно либо использовать для поля depth1 сортировку по убыванию, либо раскомментировать выражение HAVING, которое отбирает только те группы, для которых глубина depth2 не определена.