Это немного грязно в SQL.Возможно, лучше справиться с программой.
Отойдите на секунду от SQL и реляционных баз данных.Чтобы упростить задачу, начните с набора чисел S .Скажем, вы хотите найти все смежные последовательности в S ;как бы вы пошли об этом?Если бы вы могли сортировать и зацикливать (например, в программе), вы бы проверяли, отличаются ли соседние значения в отсортированной последовательности на 1. Если вы ограничены операциями с множествами, вы можете выполнить аналогичную задачу, взяв соседние пары изперекрестное произведение:
C = {(n,n+1) : n ∈ S ∧ n+1 ∈ S}
Чтобы получить конечные точки, вы можете взять транзитивное замыкание C .Однако транзитивные замыкания в SQL не элегантны;они требуют процедурного подхода, а не декларативного подхода, который обычно позволяет SQL.
Чтобы найти непрерывную последовательность в SQL, вы можете выполнить самосоединение для столбца со следующим значением в последовательности.Внутреннее объединение отфильтровывает отдельные элементы, поскольку они не будут иметь следующего значения.MIN
и MAX
получат конечные точки последовательности, что также требует группировки по идентификатору округа.
SELECT czc.id_county,
MIN(czc.from_zip_code) AS from_zip_code,
MAX(czc_n.from_zip_code) AS to_zip_code
FROM county_zip_code AS czc
JOIN county_zip_code AS czc_n
ON czc.id_county = czc_n.id_county
AND czc.from_zip_code = czc_n.from_zip_code-1
GROUP BY czc.id_county
Обратите внимание, что это решение не охватывает все случаи.Если для округа существуют непересекающиеся последовательности, это объединит их.Добавьте к образцу данных следующее:
INSERT INTO county_zip_code
VALUES
(15, 8670424, 0),
(15, 8670425, 0),
(15, 8670426, 0),
(15, 8670450, 0),
(15, 8670451, 0),
;
, и запрос приведет к:
+-----------+---------------+-------------+
| id_county | from_zip_code | to_zip_code |
+-----------+---------------+-------------+
| 12 | 2580288 | 2580292 |
| 15 | 8670424 | 8670453 |
| 16 | 7600070 | 7600073 |
+-----------+---------------+-------------+
Получение отдельных предметов немного сложнее.Из набора S вы хотите, чтобы элементы не имели предыдущего или следующего значения:
{(n-1, n, n+1) : n ∈ S ∧ n-1 ∉ S ∧ n+1 ∉ S}
В SQL снова вы используете самообъединение, но выбираете элементы, у которых нет предыдущегоили следующее значение.Здесь вам нужны частичные (левые или правые) объединения для получения этих элементов («a ∉ A» может быть преобразовано в нулевое значение в объединенных таблицах).
SELECT czc.id_county,
czc.from_zip_code AS from_zip_code,
NULL AS to_zip_code
FROM county_zip_code AS czc
LEFT JOIN county_zip_code AS czc_p
ON czc.id_county = czc_p.id_county
AND czc.from_zip_code = czc_p.from_zip_code+1
LEFT JOIN county_zip_code AS czc_n
ON czc.id_county = czc_n.id_county
AND czc.from_zip_code = czc_n.from_zip_code-1
WHERE czc_p.from_zip_code IS NULL AND czc_n.from_zip_code IS NULL
Возьмите объединение двух запросов и(при желании) сортировка.
(
SELECT czc.id_county,
MIN(czc.from_zip_code) AS from_zip_code,
MAX(czc_n.from_zip_code) AS to_zip_code
FROM county_zip_code AS czc
JOIN county_zip_code AS czc_n
ON czc.id_county = czc_n.id_county
AND czc.from_zip_code = czc_n.from_zip_code-1
GROUP BY czc.id_county
) UNION (
SELECT czc.id_county,
czc.from_zip_code AS from_zip_code,
NULL AS to_zip_code
FROM county_zip_code AS czc
LEFT JOIN county_zip_code AS czc_p
ON czc.id_county = czc_p.id_county
AND czc.from_zip_code = czc_p.from_zip_code+1
LEFT JOIN county_zip_code AS czc_n
ON czc.id_county = czc_n.id_county
AND czc.from_zip_code = czc_n.from_zip_code-1
WHERE czc_p.from_zip_code IS NULL AND czc_n.from_zip_code IS NULL
)
ORDER BY id_county, from_zip_code