SQLite: как объединить строки, которые совпадают в чередующихся столбцах? - PullRequest
1 голос
/ 26 августа 2011

Итак, давайте предположим, что мы работаем с таблицей sqlite, которая выглядит (примерно) так:

   id       date1     date2
+-------+----------+----------+
|  foo  |10/01/2010|01/01/2011|
+-------+----------+----------+
|  bar  |07/01/2010|10/01/2010|
+-------+----------+----------+
...      ...        ...

и т. Д. ... Я пытаюсь каким-то образом объединить эти строки с одинаковым идентификатором и комбинацией значений date1 и date2, которые указывают диапазон, который в противном случае был бы непрерывным, если бы он не разбросан по нескольким строкам. Другими словами, это:

   id       date1     date2
+-------+----------+----------+
|  foo  |07/01/2010|10/01/2010|
+-------+----------+----------+
|  foo  |10/01/2010|01/01/2011|
+-------+----------+----------+

станет:

   id       date1     date2
+-------+----------+----------+
|  foo  |07/01/2010|01/01/2011|
+-------+----------+----------+

и т. Д. Для случаев, когда у вас есть 3 (или более) бара, каждый из которых сопоставлен с тремя (или более) разными, хотя и непрерывными, диапазонами. Как выглядит такой запрос? До сих пор мне не удавалось найти какие-либо разумные решения, хотя я сам не большой SQLista.

Ответы [ 2 ]

0 голосов
/ 24 декабря 2011

Вам специально нужно сделать это с помощью (одного) SQL-запроса?Если нет, то я советую выбрать язык и написать одноразовый скрипт для преобразования ваших данных.

0 голосов
/ 24 декабря 2011

Я понимаю, что sqlite не поддерживает аналитические функции, но ... Вот потенциальное решение SQL, которое использует аналитические функции.Я запустил это в Postgresql.

CREATE TABLE test(id VARCHAR(16), date1 DATE, date2 DATE);

INSERT INTO test VALUES('foo', '2011-01-01', '2011-01-15');
INSERT INTO test VALUES('bar', '2011-01-02', '2011-01-04');
INSERT INTO test VALUES('bar', '2011-01-05', '2011-01-10'); -- not contiguous
INSERT INTO test VALUES('foo', '2011-01-25', '2011-01-30');
INSERT INTO test VALUES('foo', '2011-01-15', '2011-01-18'); -- contiguous
INSERT INTO test VALUES('foo', '2011-01-28', '2011-01-31'); -- overlap
INSERT INTO test VALUES('bar', '2011-01-07', '2011-01-08'); -- subset chopped

postgres=# SELECT * FROM test ORDER BY id, date1;
 id  |   date1    |   date2
-----+------------+------------
 bar | 2011-01-02 | 2011-01-04
 bar | 2011-01-05 | 2011-01-10
 bar | 2011-01-07 | 2011-01-08
 foo | 2011-01-01 | 2011-01-15
 foo | 2011-01-15 | 2011-01-18
 foo | 2011-01-25 | 2011-01-30
 foo | 2011-01-28 | 2011-01-31
(7 rows)

SELECT id
      ,MIN(date1) AS date1
      ,MAX(date2) AS date2
  FROM ( SELECT id, date1, date2, previous_date1, previous_date2
               ,SUM( CASE WHEN date1 > previous_date2 THEN 1 ELSE 0 END ) OVER(PARTITION BY id ORDER BY id, date1) AS group_id
           FROM ( SELECT id, date1, date2
                        ,COALESCE( LAG(date1) OVER (PARTITION BY id ORDER BY id, date1), date1 )  previous_date1
                        ,COALESCE( LAG(date2) OVER (PARTITION BY id ORDER BY id, date1), date2 )  previous_date2
                    FROM test
                    ORDER BY id, date1, date2
                ) AS x
       ) AS y
  GROUP BY id, group_id
  ORDER BY 1,2;

 id  |   date1    |   date2
-----+------------+------------
 bar | 2011-01-02 | 2011-01-04
 bar | 2011-01-05 | 2011-01-10
 foo | 2011-01-01 | 2011-01-18
 foo | 2011-01-25 | 2011-01-31
(4 rows)

ОБЪЯСНЕНИЕ

Работая изнутри, сначала отсортируйте строки по идентификатору и дате, и добавьте два дополнительных столбца в каждую строку, чтобы указать предыдущую строкузначения date1 и date2.

 id  |   date1    |   date2    | previous_date1 | previous_date2
-----+------------+------------+----------------+----------------
 bar | 2011-01-02 | 2011-01-04 | 2011-01-02     | 2011-01-04
 bar | 2011-01-05 | 2011-01-10 | 2011-01-02     | 2011-01-04
 bar | 2011-01-07 | 2011-01-08 | 2011-01-05     | 2011-01-10
 foo | 2011-01-01 | 2011-01-15 | 2011-01-01     | 2011-01-15
 foo | 2011-01-15 | 2011-01-18 | 2011-01-01     | 2011-01-15
 foo | 2011-01-25 | 2011-01-30 | 2011-01-15     | 2011-01-18
 foo | 2011-01-28 | 2011-01-31 | 2011-01-25     | 2011-01-30
(7 rows)

Затем пометьте каждую строку, имеющую перекрытие (между date1 и previous_date1), суммируя эти флаги в группе «id», мы получим подгруппу идентификаторов.

 id  |   date1    |   date2    | previous_date1 | previous_date2 | flag | group_id
-----+------------+------------+----------------+----------------+------+----------
 bar | 2011-01-02 | 2011-01-04 | 2011-01-02     | 2011-01-04     |    0 |        0
 bar | 2011-01-05 | 2011-01-10 | 2011-01-02     | 2011-01-04     |    1 |        1
 bar | 2011-01-07 | 2011-01-08 | 2011-01-05     | 2011-01-10     |    0 |        1
 foo | 2011-01-01 | 2011-01-15 | 2011-01-01     | 2011-01-15     |    0 |        0
 foo | 2011-01-15 | 2011-01-18 | 2011-01-01     | 2011-01-15     |    0 |        0
 foo | 2011-01-25 | 2011-01-30 | 2011-01-15     | 2011-01-18     |    1 |        1
 foo | 2011-01-28 | 2011-01-31 | 2011-01-25     | 2011-01-30     |    0 |        1
(7 rows)

Теперь мы можем группировать по идентификатору и сгенерированному «group_id».

Может быть, это безумие.Я не уверен, что на самом деле хотел бы использовать такого рода решения, потому что может быть сложно тестировать, проверять, документировать, и , особенно , выдержать несколько лет спустя.Но я все еще думаю, что это просто, что можно сделать с помощью sql.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...