【问题标题】:Reusing joins between different parts of a query在查询的不同部分之间重用连接
【发布时间】:2016-10-09 13:29:44
【问题描述】:

我有以下表格:

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 进行分组,我将 layer 与 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

【问题讨论】:

    标签: sql postgresql


    【解决方案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 数组作为中介)。但这会将最后一个列表作为单独的子查询。

    【讨论】:

    • 谢谢,帮了大忙。我将为 ls.sound 使用 array_agg 并在其他地方转换为 JSON - 然后我可以在数组上使用 unnest 来获取所有层的不同声音 ID。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    相关资源
    最近更新 更多