Сложность SQL JOIN - кажется, нужен способ ограничения строк в условии соединения - PullRequest
2 голосов
/ 12 ноября 2010

Я немного застрял, пытаясь понять, как разработать запрос Oracle, и, хотя здесь есть похожие вопросы, ни один из них, похоже, не решает проблемы, с которыми я сталкиваюсь.

У меня есть две таблицы, и я хочу к ним присоединиться:

PROJECT table
  PROJECT_ID   TITLE
  101          First project
  102          Second project
  103          Third project
  104          Fourth project
  105          Fifth project

EVENT table
  EVENT_ID  PROJECT_FK  EVENT_TYPE  EVENT_DATE  EVENT_DESC
  201       101         301         2010-01-01  First event
  202       101         301         2010-01-01  Second event
  203       101         302         2010-01-02  Third event
  204       102         301         2010-01-03  Fourth event
  205       102         301         2010-01-04  Fifth event
  206       104         301         2010-01-05  Sixth event
  207       105         302         2010-01-06  Seventh event

Я хотел бы получить список данных каждого проекта (из таблицы PROJECT) вместе с подробностями самого последнего события, но только события одного типа (все другие события должны игнорироваться). Одна строка и только одна строка, должна быть возвращена для каждого проекта (поэтому, если несколько совпадающих событий имеют одну и ту же дату, либо одно подходит, и если нет событий, в полях событий должны быть возвращены нули / пробелы.)

Вот как может выглядеть результат:

SELECT <???> WHERE PROJECT_ID IN (101, 102, 103, 105)   /* for event type 301 only */

  PROJECT_ID   TITLE           EVENT_DATE  EVENT_DESC
  101          First project   2010-01-01  First event
  102          Second project  2010-01-04  Fifth event
  103          Third project   NULL        NULL
  105          Fifth project   NULL        NULL

Я нахожу это довольно хитрым, так как единственные примеры, которые я могу найти, предполагают, что max (date) уникальна (но здесь при выборе будет возвращаться неправильная строка), или они предполагают, что есть много дубликатов, поэтому GROUP BY будет работать.

Ответы [ 2 ]

5 голосов
/ 12 ноября 2010

Oracle 9i +, используя ROW_NUMBER:

SELECT x.project_id,
       x.title,
       x.event_date,
       x.event_desc
  FROM (SELECT p.project_id,
               p.title,
               e.event_date,
               e.event_desc,
               ROW_NUMBER() OVER(PARTITION BY p.project_id
                                     ORDER BY e.event_date) AS rank
          FROM PROJECT p
     LEFT JOIN EVENT e ON e.project_fk = p.project_id
                      AND e.event_type = 301
         WHERE p.project_id IN (101,102,103)) x
 WHERE x.rank = 1

Oracle 9i +, используя WITH и ROW_NUMBER:

WITH example AS (
     SELECT p.project_id,
            p.title,
            e.event_date,
            e.event_desc,
            ROW_NUMBER() OVER(PARTITION BY p.project_id
                                  ORDER BY e.event_date) AS rank
       FROM PROJECT p
  LEFT JOIN EVENT e ON e.project_fk = p.project_id
                   AND e.event_type = 301
      WHERE p.project_id IN (101,102,103))
SELECT x.project_id,
       x.title,
       x.event_date,
       x.event_desc
  FROM example x
 WHERE x.rank = 1
0 голосов
/ 12 ноября 2010

Каждый раз, когда вы видите что-то вроде «только для типа события 301», вы должны ожидать того же ограничения либо в предложении where, либо в условии has («Имея» - это, в основном, предложение where для результатов операции «»group by ").

Итак, сказав, вы могли бы начать с чего-то, как показано ниже, и использовать обратные слова.WHERE EVENT_TYPE = 301;

Затем вы можете заполнить некоторые основы, например таблицу, в которой находится поле, например, SELECT * FROM EVERE, где EVENT_TYPE = 301;

Теперь вы можете начать думать о группировании похожихсобытия в соответствии с PROJECT_ID.Для простоты мы просто будем использовать PROJECT_FK, поскольку он эквивалентен PROJECT.PROJECT_ID.ВЫБЕРИТЕ PROJECT_FK ИЗ МЕРОПРИЯТИЯ, ГДЕ EVENT_TYPE = 301 GROUP BY PROJECT_FK;

Теперь все PROJECT_FK в таблице EVENT сгруппированы вместе, но у нас нет никакой информации.Мы ищем детали одного события, чтобы мы могли выбрать только один EVENT_ID, когда их несколько.MIN () и MAX () будут работать для этого.Таким образом, вы можете написать: SELECT PROJECT_FK, MIN (EVENT_ID) FROM EVENT WHERE EVENT_TYPE = 301 GROUP BY PROJECT_FK;

Приведенный выше запрос дал нам по одному событию для каждого PROJECT_FK в таблице EVENT, но ни одна из других подробностей события,Давайте объединим приведенный выше результат с таблицей EVENT, и мы находимся на пути к нашему ответу.Поэтому ВЫБЕРИТЕ table2. * ИЗ СОБЫТИЯ table1 ПРИСОЕДИНЯЙТЕСЬ (ВЫБЕРИТЕ PROJECT_FK, MIN (EVENT_ID) ОТ СОБЫТИЯ ГДЕ EVENT_TYPE = 301 GROUP BY PROJECT_FK) table2 ONтаблица ПРОЕКТ.Всякий раз, когда вы видите «пустые / пустые значения должны быть возвращены в полях событий», вы должны думать о внешнем соединении.Таким образом, вы можете просто продолжать объединять таблицы, чтобы получить желаемый результат.Например, SELECT * FROM PROJECT LEFT OUTER JOIN (SELECT table2. * FROM EVENT table1 JOIN (SELECT PROJECT_FK, MIN (EVENT_ID) FROM EVENT WHERE EVENT_TYPE = 301 GROUP BY PROJECT_FK) table2 ON table1.PROJECT_FK = table2.PROJECT_FK =) ONPROJECT_FK;

...