Смежные последовательные числа в MySQL - PullRequest
2 голосов
/ 09 сентября 2011

У меня есть следующая таблица и данные.

CREATE TABLE county_zip_code (
  id_county INT UNSIGNED,
  from_zip_code INT UNSIGNED,
  to_zip_code INT UNSIGNED
);

INSERT INTO county_zip_code 
(id_county, from_zip_code, to_zip_code)
    VALUES
(12,          2580288,          0),
(12,          2580289,          0),
(12,          2580290,          0),
(12,          2580291,          0),
(12,          2580292,          0),
(15,          8670418,          0),
(15,          8670420,          0),
(15,          8670430,          0),
(16,          7600070,          0),
(16,          7600071,          0),
(16,          7600072,          0),
(16,          7600073,          0)
;

Я хотел бы создать новую таблицу с именем "county_zip_code1" с заменой смежных последовательностей их конечными точками:

id_county | from_zip_code | to_zip_code
    12      2580288         2580292
    15      8670418         0
    15      8670420         0
    15      8670430         0
    16      7600070         7600073

Как я могу это сделать?

Я прочитал некоторые ответы на вопрос SO " Оценить последовательные пары строк в SQLite ", но этот вид SQL слишком сложен для меня.

Ответы [ 2 ]

4 голосов
/ 13 сентября 2011

Это немного грязно в 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
0 голосов
/ 10 сентября 2011

Попробуйте это:

CREATE TABLE county_zip_code1 AS
SELECT id_county, MIN(from_zip_code) AS from_zip_code, MAX(from_zip_code) to_zip_code
  FROM county_zip_code
GROUP BY id_county
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...