MySQL: самостоятельное объединение для создания пар дат - PullRequest
1 голос
/ 09 сентября 2011

У меня есть таблица с записями для элементов как «потерянные» и «найденные».В каждой строке есть дата для события.Я надеюсь построить запрос с соответствующими парами 'itemid', 'потерянная дата', 'найденная дата', присоединив таблицу к себе.

Это работает до некоторой степени: к сожалению, если есть несколько потерянных и найденныхпары для данного элемента, каждая «потерянная дата» будет объединена со всеми «найденными датами», которые следуют за ней.

Все еще со мной?

Запрос выглядит примерно так:

select c0.ItemId, c0.ChangeDate, c1.ChangeDate from Changes c0
join Changes c1 on 
   c0.ItemId = c1.ItemId and c1.ChangeDate >= c0.ChangeDate
where c0.ChangeType = 9 (lost) and c1.ChangeType = 10 (found);

То, что я надеюсь достичь, это некоторая форма заданной «потерянной даты» в паре со следующей «найденной датой» в последовательности (или NULL, если «найденной даты» не существует).Я (довольно) уверен, что это возможно, но я не вижу пути.

Мне было интересно, чтобы в первом объединении был выбран дополнительный выбор и использование LIMIT 1 для получения только одной записи, но я не вижу, как соединить это с соответствующей строкой в ​​основной части выбора,MySQL говорит мне, что его не существует.Справедливо.

Ответы [ 2 ]

3 голосов
/ 09 сентября 2011

Хитрость здесь в том, чтобы оговорить «а между потерянными и найденными датами нет другой потерянной или найденной даты» или, в SQL:

SELECT c0.ItemId, c0.ChangeDate, c1.ChangeDate
  FROM Changes AS c0
  JOIN Changes AS c1 ON c0.ItemId = c1.ItemId AND c1.ChangeDate >= c0.ChangeDate
 WHERE c0.ChangeType = 9   -- Lost
   AND c1.ChangeType = 10  -- Found
   AND NOT EXISTS(SELECT *
                    FROM Changes AS c2
                   WHERE c2.ItemId = c1.ItemID
                     AND c2.ChangeType IN (9, 10) -- Lost or Found
                     AND c2.ChangeDate BETWEEN c0.ChangeDate AND c1.ChangeDate
                     AND (c2.ChangeDate != c0.ChangeDate AND c2.ChangeDate != c1.ChangeDate)
                 );

Поскольку это коррелированный подзапрос,он имеет тенденцию замедлять запрос, но должен давать правильные строки.

Существует важное предостережение о том, как я удалил строки c0 и c1, указав, что ChangeDate для строки в c2 долженотличаться от потерянной даты или найденной даты.Однако основной запрос, по-видимому, позволяет найти элемент в тот же день, когда он был утерян.Там может быть какой-то другой столбец, например столбец ChangeId, который не указан в запросе, но который можно использовать вместо этого:

AND c2.ChangeID NOT IN (c0.ChangeID, c1.ChangeID)

Вам нужно подумать о том, что произойдет, если элемент потерянскажем, 2011-06-07, и снова проиграл 2011-06-14, и был найден только 2011-06-21.А что, если он также найден на 2011-06-28?Такие проблемы должны быть предотвращены обработкой ввода данных, поэтому в приведенном выше запросе предполагается, что таких проблем не будет.

3 голосов
/ 09 сентября 2011

Обычно при работе с парами дат (например, начало / конец для планирования) совет не размещайте их в отдельных строках.Поместите их в две колонки одного ряда.См. Стиль программирования Джо Селко для SQL .

Но, тем не менее, вы можете решить эту проблему с помощью вашей текущей схемы, выполнив поиск другого самостоятельного соединения для поиска ChangeDate между ними.Если ничего не найдено (то есть, если c2. * Равно нулю из-за внешнего соединения), то c0 и c1 являются «смежными».

select c0.ItemId, c0.ChangeDate, c1.ChangeDate 
from Changes c0
inner join Changes c1 on 
   c0.ItemId = c1.ItemId and c1.ChangeDate > c0.ChangeDate
left outer join Changes c2 on
   c0.ItemId = c2.ItemId and c2.ChangeDate > c0.ChangeDate 
                         and c2.ChangeDate < c1.ChangeDate 
                         and c2.ChangeType IN (9,10) -- edit
where c0.ChangeType = 9 (lost) and c1.ChangeType = 10 (found)
   and c2.ItemId IS NULL;

В приведенном выше примере я предположил, что ChangeDateявляется уникальным, и я изменил> = на>.Если ChangeDate не является уникальным, вам придется придумать какое-то другое выражение для проверки c2 «между» c0 и c1.

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