Повторное использование соединений между различными частями запроса

#sql #postgresql

#sql #postgresql

Вопрос:

У меня есть следующие таблицы:

 CREATE TABLE sequence (
  id serial PRIMARY KEY
  -- ...other sequence data
)

CREATE TABLE sound (
  id serial PRIMARY KEY
  -- ...other sound data
)

CREATE TABLE layer (
  id serial PRIMARY KEY,
  index smallint NOT NULL,
  sequence integer NOT NULL REFERENCES sequence (id)
)

CREATE TABLE layerSound (
  id serial PRIMARY KEY,
  index smallint NOT NULL,
  layer integer NOT NULL REFERENCES layer (id),
  sound integer NOT NULL REFERENCES sound (id)
)
  

Итак, у меня есть последовательности. Каждая последовательность состоит из множества слоев. Каждый слой
имеет много layerSounds. К каждому слою звука прикреплен один звук.

Я хочу написать запрос (возвращающий JSON), который может выбрать определенную последовательность по ее идентификатору, а затем также присоединиться:

  • массив слоев, используемых этой последовательностью
  • агрегированный массив звуков слоев, сгруппированных по слоям
  • массив уникальных звуков, используемых в этой последовательности (на всех уровнях звука)

Например:

 {
  sequence: 3,
  layers: [1, 2],
  layerSounds: [
    { layer: 1, sounds: [1, 2] },
    { layer: 2, sounds: [2, 3] }
  ],
  sounds: [
    { id: 1, foo: "bar" },
    { id: 2, foo: "baz" },
    { id: 3, foo: "blah" }
  ]
}
  

Таким образом, цель состоит в том, чтобы записать отдельные звуковые данные полностью только один раз, а затем layerSounds.
массив звуков содержит только идентификаторы звуков. Таким образом, звуковые данные не дублируются.

Мой подход до сих пор заключается в том, чтобы ВЫБРАТЬ последовательность, а затем объединить другие таблицы по отдельности. Я группирую каждый из них по идентификатору последовательности, а затем объединяю один раз с внешним запросом.

Пока это работает, я замечаю, что мне приходится повторять объединения в каждом запросе объединения, чтобы всегда группировать по sequenceId.

Поэтому, чтобы сгруппировать layerSounds по sequenceId, я объединяю layerSound в layerSound в sound . Затем я снова выполняю то же самое соединение, чтобы вычислить все звуки, используемые этой последовательностью. Я включил запрос ниже.

Мой вопрос в том, есть ли способ улучшить этот запрос? Что-то не так с этим подходом? Или повторное использование соединений, подобных этому, является нормальным?

Спасибо за ваше время.

Запрос:

 SELECT
  sequence.id,
  layers.ids AS layers,
  layerSounds.ids AS layerSounds,
  sounds.ids AS sounds
FROM sequence
JOIN (
  SELECT
    sequence,
    json_agg(id) AS ids
  FROM layer
  GROUP BY sequence
) layers ON layers.sequence = sequence.id
JOIN (
  SELECT
    sequence,
    json_agg(layerSounds) AS ids
  FROM layer
  JOIN (
    SELECT
      layerSound.layer,
      json_agg(sound.id) AS ids
    FROM layerSound
    JOIN sound
    ON sound.id = layerSound.sound
    GROUP BY layerSound.layer
  ) layerSounds ON layerSounds.layer = layer.id
  GROUP BY sequence
) layerSounds ON layerSounds.sequence = sequence.id
JOIN (
  SELECT
    sequence,
    json_agg(DISTINCT sound.id) AS ids
  FROM layer
  JOIN layerSound
    ON layerSound.layer = layer.id
  JOIN sound
    ON sound.id = layerSound.sound
  GROUP BY sequence
) sounds ON sounds.sequence = sequence.id
  

Ответ №1:

Вы определенно можете упростить запрос. Я думаю, что это одно упрощение:

 SELECT s.id, l.ids AS layers, ls.ids AS layerSounds,
       so.ids AS sounds
FROM sequence s JOIN
     (SELECT l.sequence, json_agg(l.id) AS ids,
             json_agg(ls)
      FROM layer l JOIN
           (SELECT ls.layer, json_agg(ls.sound) AS ids
            FROM layerSound ls 
            GROUP BY ls.layer
           ) ls
           ON ls.layer = l.id
      GROUP BY l.sequence
     ) l
     ON l.sequence = s.id JOIN
     (SELECT l.sequence,
             json_agg(DISTINCT ls.sound) AS ids
      FROM layer l JOIN
           layerSound ls
           ON ls.layer = l.id
      GROUP BY l.sequence
     ) so
     ON so.sequence = s.id;
  

Ключевое замечание заключается в том, что вам не нужно присоединяться к sounds , потому что информация находится внутри layerSound .

Первый подзапрос объединяет первые два подзапроса в вашей версии. В Postgres может быть способ объединить массивы JSON layer sound в один массив (возможно, используя массивы Postgres в качестве посредника). Но это делает последний список отдельным подзапросом.

Комментарии:

1. Спасибо, это очень помогло. Я собираюсь использовать array_agg для ls.sound и преобразовать в JSON в другом месте — тогда я могу использовать unnest в массиве, чтобы получить различные идентификаторы звука на всех уровнях.