SQL для объединения первой записи в произвольном порядке сортировки - PullRequest
1 голос
/ 05 сентября 2011

Иногда я хочу присоединиться к записи, которую я могу легко определить как «отсортировать по тому и другому и выбрать первую запись».Интересно, какая здесь лучшая практика?

Например, в моей модели данных у меня есть службы и реализации.Сервис может иметь несколько реализаций.Реализации имеют номера версий;Может быть установлено ноль или более реализаций, и одна из них также может быть включена.Теперь я хочу выбрать «текущую реализацию» для каждой службы, используя следующие правила:

  • Если ничего не установлено, то текущая реализация является самой последней.
  • Если большечем один установлен, то текущим является либо тот, который включен, либо, если ничего не включено, снова самый последний.

Некоторые примеры:

Service  Version  Installed  Enabled
=======  =======  =========  =======
A        1        False      False
A        2        False      False   <- current for service A because most recent

B        1        True       False   <- current for service B because installed
B        2        False      False

C        1        True       False
C        2        True       False   <- current for service C because most recent
C        3        False      False      among installed

D        1        True       True    <- current for service D because enabled
D        2        True       False

IЯ подумал, что если я отсортирую по этим полям и выберу первую запись, это сработает.Т.е. я делаю что-то подобное:

SELECT s.service, <other service fields>, i.version, <other impl. fields>
  FROM service s, implementation i
 WHERE i.rowid == (
       SELECT rowid 
         FROM implementation o
        WHERE o.service == s.service
     ORDER BY installed DESC, enabled DESC, version ASC)

Обновление. Это довольно специфично для SQLite, я думаю.Во-первых, rowid - это внутренний идентификатор записи, который используется SQLite.Во-вторых, в SQLite выражение SELECT в этом контексте возвращает одно значение.Я предполагаю, что его можно переписать в более общий SQL, например:

  ...
  FROM service s, implementation i
 WHERE i.service == s.service
   AND i.version == (
       SELECT version 
         FROM ...
     ORDER BY ...
        LIMIT 1)

Он работает здесь, но иногда нет rowid или другого отдельного поля, которое может служить идентификатором.Есть ли другие способы получить такой же результат?Может быть, что-то более общее?(И для меня тоже есть что-то более конкретное, если оно специфично для SQLite:)

Ответы [ 2 ]

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

Вы можете использовать условие NOT EXISTS, чтобы исключить все, кроме желаемого соответствия. Соедините строки [service] и [creation], которые совпадают в столбце [service], при условии, что не существует другой строки [реализация], которая также соответствует столбцу [service], но предпочтительнее, поскольку она будет иметь более высокий ранг желаемый заказ. Вот идея.

SELECT s.service, <other service fields>, i.version, <other impl. fields>
FROM service s JOIN implementation i
ON i.service = s.service
AND NOT EXISTS ( -- where there's no "better" row from i to use
SELECT * FROM implementation AS i2
   WHERE i2.service = i.service
   AND (
     i2.installed > i.installed
     OR (i2.installed = i.installed AND i2.enabled > i.enabled)
     OR (i2.installed = i.installed AND i2.enabled = i.enabled AND i2.version < i.version)
   )
)

Используя Microsoft SQL Server или другой диалект SQL, который поддерживает оператор CROSS APPLY, это намного, намного проще:

SELECT s.service, <other service fields>, i.version, <other impl. fields>
FROM service s
CROSS APPLY (
  SELECT TOP 1 * FROM implementation
  WHERE implementation.service = s.service
  ORDER BY installed DESC, enabled DESC, version ASC
) AS i

(Ни одно из решений не было протестировано с образцами данных, поскольку операторы CREATE TABLE и INSERT не были опубликованы.)

Стив Касс

0 голосов
/ 05 сентября 2011

Я не мог придумать ничего лучше, чем это:

SELECT
  i.Service,
  i.Installed,
  i.Enabled,
  MAX(i.Version) AS Version
FROM implementation i
  INNER JOIN (
    SELECT
      i.Service,
      i.Installed,
      MAX(i.Enabled) AS Enabled
    FROM implementation i
      INNER JOIN (
        SELECT
          Service,
          MAX(Installed) AS Installed
        FROM implementation
        GROUP BY
          Service
      ) m ON i.Service = m.Service
    GROUP BY
      i.Service,
      i.Installed
  ) m ON i.Service = m.Service AND i.Installed = m.Installed
GROUP BY
  i.Service,
  i.Installed
  i.Enabled

По сути, этот скрипт находит максимальные значения Installed для каждого Service, затем присоединяет полученный список к исходной таблице (для фильтрации по найденным значениям) и находит максимумы Enabled, затем снова присоединяет полученную таблицу обратно к implementation, чтобы окончательно получить максимальные значения Version для отфильтрованных строк.

...