SQL - подавление дублирующихся * смежных * записей - PullRequest
2 голосов
/ 15 апреля 2010

Мне нужно запустить оператор Select (DB2 SQL), который не извлекает дубликаты соседних строк на основе определенного поля. В частности, я пытаюсь выяснить, когда данные изменяются , что затрудняется, поскольку они могут вернуться к своему первоначальному значению.

То есть, у меня есть таблица, которая немного напоминает приведенную ниже, отсортированную по письму, а затем по дате:

A, 5, 2009-01-01
A, 12, 2009-02-01
A, 12, 2009-03-01
A, 12, 2009-04-01
A, 9, 2009-05-01
A, 9, 2009-06-01
A, 5, 2009-07-01

И я хочу получить результаты:

A, 5, 2009-01-01
A, 12, 2009-02-01
A, 9, 2009-05-01
A, 5, 2009-07-01

отбрасывает соседние дубликаты, но сохраняет последний ряд (несмотря на то, что он имеет тот же номер, что и первый ряд). Очевидное:

Select Letter, Number, Min(Update_Date) from Table group by Letter, Number 

не работает - не включает последнюю строку.

Редактировать: Поскольку, кажется, была некоторая путаница, я уточнил столбец месяца в столбец даты. Он был задуман как понятная человеку краткая форма, а не как действительные достоверные данные.

Редактировать: последняя строка не важна, ПОТОМУ ЧТО это последняя строка, а потому что она имеет «новое значение», которое также является «старым значением». Группировка по NUMBER обернет его с первым рядом; оно должно оставаться отдельной сущностью.

Ответы [ 5 ]

3 голосов
/ 15 апреля 2010

В зависимости от того, в какой DB2 вы работаете, существуют аналитические функции, которые могут легко решить эту проблему. Пример в Oracle приведен ниже, но синтаксис select выглядит довольно схожим.

create table t1 (c1 char, c2 number, c3 date);

insert into t1 VALUES ('A', 5, DATE '2009-01-01');
insert into t1 VALUES ('A', 12, DATE '2009-02-01');
insert into t1 VALUES ('A', 12, DATE '2009-03-01');
insert into t1 VALUES ('A', 12, DATE '2009-04-01');
insert into t1 VALUES ('A', 9, DATE '2009-05-01');
insert into t1 VALUES ('A', 9, DATE '2009-06-01');
insert into t1 VALUES ('A', 5, DATE '2009-07-01');

SQL> l
  1  SELECT C1, C2, C3
  2    FROM (SELECT C1, C2, C3,
  3                 LAG(C2) OVER (PARTITION BY C1 ORDER BY C3) AS PRIOR_C2,
  4                 LEAD(C2) OVER (PARTITION BY C1 ORDER BY C3) AS NEXT_C2
  5            FROM T1
  6         )
  7   WHERE C2 <> PRIOR_C2
  8      OR PRIOR_C2 IS NULL -- to pick up the first value
  9   ORDER BY C1, C3
SQL> /

C         C2 C3
- ---------- -------------------
A          5 2009-01-01 00:00:00
A         12 2009-02-01 00:00:00
A          9 2009-05-01 00:00:00
A          5 2009-07-01 00:00:00
1 голос
/ 15 апреля 2010

Первое, что вам нужно сделать, это определить последовательность, в которой вы хотите просмотреть / рассмотреть данные. Значения «январь, февраль, мар» не помогают, потому что данные не в алфавитном порядке. А что происходит, когда вы переключаетесь с декабря на январь? Шаг 1: определите последовательность, которая однозначно определяет каждую строку с учетом вашей проблемы.

Далее, вы должны быть в состоянии сравнить элемент #x с элементом # x-1, чтобы увидеть, изменился ли он. Если изменено, включите; если не изменено, исключить. Тривиально при использовании процедурных циклов кода (курсоры в SQL), но вы бы хотели их использовать? Они, как правило, не очень хорошо работают.

Один из основанных на SQL способов сделать это состоит в том, чтобы соединить таблицу с самим собой, с предложением соединения «MyTable.SequenceVal = MyTable.SequenceVal - 1». Добавьте сравнение, убедитесь, что вы не бросили самый первый ряд набора (где нет x-1), и все готово. Обратите внимание, что производительность может ухудшиться, если «SequenceVal» не проиндексирован.

1 голос
/ 15 апреля 2010

Это невозможно с командами на основе набора (т. Е. Сгруппировать и т. Д.).

Вы можете сделать это с помощью курсоров.

Лично я бы помещал данные в свое клиентское приложение и выполнял там фильтрацию.

0 голосов
/ 16 апреля 2010

Использование предложения "EXCEPT" - один из способов сделать это. Смотрите решение ниже. Я включил все свои тестовые шаги здесь. Сначала я создал сеансовую таблицу (она исчезнет после отключения от базы данных).

CREATE TABLE session.sample (
   letter CHAR(1),
   number INT,
   update_date DATE
);   

Затем я импортировал ваши образцы данных:

IMPORT FROM sample.csv OF DEL INSERT INTO session.sample;

Подтверждено, что ваш образец информации в базе данных:

SELECT * FROM session.sample;

 LETTER NUMBER      UPDATE_DATE
 ------ ----------- -----------
 A                5 01/01/2009
 A               12 02/01/2009
 A               12 03/01/2009
 A               12 04/01/2009
 A                9 05/01/2009
 A                9 06/01/2009
 A                5 07/01/2009

   7 record(s) selected.

Я написал это с предложением EXCEPT и использовал «WITH», чтобы сделать его более понятным. По сути, я пытаюсь выбрать все строки, которые имеют предыдущую запись даты. Затем я исключаю все эти строки из выборки во всей таблице.

WITH rows_with_previous AS (
  SELECT s.*
  FROM session.sample s
  JOIN session.sample s2
    ON s.letter = s2.letter
      AND s.number = s2.number
      AND s.update_date = s2.update_date - 1 MONTH
)
SELECT *
FROM session.sample
EXCEPT ALL
SELECT *
FROM rows_with_previous;       

Вот результат:

 LETTER NUMBER      UPDATE_DATE
 ------ ----------- -----------
 A                5 01/01/2009
 A               12 04/01/2009
 A                9 06/01/2009
 A                5 07/01/2009

   4 record(s) selected.
0 голосов
/ 15 апреля 2010

отбрасывает соседние дубликаты, но сохраняя последний ряд.

почему вы хотите сохранить последний ряд? Какова цель?

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