重用查询的不同部分之间的连接

时间:2016-10-09 13:29:44

标签: 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。每个layerSound都附加一个声音。

我想编写一个可以选择特定序列的查询(返回JSON) 通过它的ID,然后加入:

  • 此序列使用的图层数组
  • 按层分组的layerSounds聚合数组
  • 此序列使用的一系列独特声音(跨所有layerSounds)

例如:

{
  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.sounds 数组只有声音ID。所以声音数据不会重复。

到目前为止,我的方法是选择序列,然后分别聚合其他表。我按顺序ID对每个进行分组,然后针对外部查询加入一次。

虽然这有效,但我注意到我必须在每个JOIN查询中重复连接才能始终按sequenceId进行分组。

因此,要通过sequenceId对layerSounds进行分组,我将图层连接到layerSound以发声。然后,我再次进行完全相同的连接,以计算此序列使用的所有声音。我在下面提到了这个问题。

我的问题是,有没有办法改善这个查询?这种方法有问题吗?或者正在重新使用这样的连接?

感谢您的时间。

查询:

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 个答案:

答案 0 :(得分: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数组合并到一个数组中(可能使用Postgres数组作为中介)。但这将最后一个列表作为一个单独的子查询。