【问题标题】:More Efficient Way to Join Three Tables Together in Postgres在 Postgres 中将三个表连接在一起的更有效方法
【发布时间】:2017-04-14 12:14:21
【问题描述】:

我试图在 postgres 中将三个表链接在一起。

所有三个表都是从子查询生成的。第一个表通过变量 call_sign 作为 FULL JOIN 链接到第二个表(因为我想要两个表中条目的超集)。第三个表有一个 INNER JOIN,第二个表也在 call_sign 上(但理论上可以链接到第一个表)

查询运行但很慢,我觉得随着我添加更多数据会变得更慢。我意识到我可以做一些事情来加快速度——比如不要在子查询中提取不必要的数据,也不要即时将文本转换为数字。但是有没有更好的方法来构建这三个表之间的 JOIN?

任何建议都将不胜感激,因为我是 postgres 的新手。

代码如下:

select 
(CASE 
    WHEN tmp1.frequency_assigned is NULL 
    THEN tmp2.lower_frequency
    ELSE tmp1.frequency_assigned END) as master_frequency,
(CASE 
    WHEN tmp1.call_sign is NULL 
    THEN tmp2.call_sign
    ELSE tmp1.call_sign END) as master_call_sign,
(CASE 
    WHEN tmp1.entity_type is NULL 
    THEN tmp2.entity_type
    ELSE tmp1.entity_type END) as master_entity_type,
(CASE 
    WHEN tmp1.licensee_id is NULL 
    THEN tmp2.licensee_id
    ELSE tmp1.licensee_id END) as master_licensee_id,
(CASE 
    WHEN tmp1.entity_name is NULL 
    THEN tmp2.entity_name
    ELSE tmp1.entity_name END) as master_entity_name,
tmp3.market_name    
FROM
    (select cast(replace(frequency_assigned, ',','.') as decimal) AS frequency_assigned, 
    frequency_upper_band,
    f.uls_file_number,
    f.call_sign,
    entity_type,
    licensee_id,
    entity_name
    from combo_fr f INNER JOIN combo_en e 
    ON f.call_sign=e.call_sign
    ORDER BY frequency_assigned DESC) tmp1
FULL JOIN
    (select cast(replace(lower_frequency, ',','.') as decimal) AS lower_frequency, 
    upper_frequency,
    e.uls_file_number,
    mf.call_sign,
    entity_type,
    licensee_id,
    entity_name
    FROM market_mf mf INNER JOIN combo_en e
    ON mf.call_sign=e.call_sign
    ORDER BY lower_frequency DESC) tmp2
ON tmp1.call_sign=tmp2.call_sign
INNER JOIN 
    (select en.call_sign,
    mk.market_name
    FROM combo_mk mk
    INNER JOIN combo_en en
    ON mk.call_sign=en.call_sign) tmp3
ON tmp2.call_sign=tmp3.call_sign 
ORDER BY master_frequency DESC;

【问题讨论】:

  • 首先你可以用COALESCE代替CASE WHEN … IS NULL
  • 其次,你应该删除所有子选择中的ORDER BY,在这种情况下完全没用。
  • call_sign 在所涉及的任何表中是否可以为空或唯一?其他字段可以为空吗?换句话说,那些CASE WHEN... 是否会返回来自同一个表的每一行的值,或者答案中的某些行是来自combo_frmarket_mf 的值的混合(忽略@987654330 @)?
  • 谢谢@kennytm。 COALESCE 似乎将性能提高了一点点,但代码看起来仍然更好。
  • 也感谢@Dario。删除子查询中的 ORDER BY 将性能提高了 13%。 call_sign 永远不会为空,但可能是 call_sign 存在于第一个表 (combo_fr) 或第二个表 (market_mf) 中,我想同时提取两者。这有帮助吗?

标签: postgresql join processing-efficiency


【解决方案1】:

如果可以的话,您会希望展开这些查询并在一次连接中完成所有操作。有点像:

select <whatever you need>
from combo_fr f
JOIN combo_en e ON f.call_sign=e.call_sign
JOIN market_mf mf mf ON mf.call_sign=e.call_sign
JOIN combo_mk mk ON mk.call_sign=en.call_sign

我无法完全理解您在做什么,但某些连接子句可能必须成为 LEFT JOIN 才能处理呼号出现或不出现的地方。

【讨论】:

    【解决方案2】:

    call_sign 上为所有四个相关表创建索引后,试试这个:

    WITH nodup AS (
      SELECT call_sign FROM market_mf 
      EXCEPT SELECT call_sign FROM combo_fr
    ) SELECT     
          CAST(REPLACE(u.master_frequency_string, ',','.') AS DECIMAL) 
                  AS master_frequency,
          u.call_sign AS master_call_sign,
          u.entity_type AS master_entity_type,
          u.licensee_id AS master_licensee_id,
          u.entity_name AS master_entity_name,
          combo_mk.market_name
        FROM (SELECT frequency_assigned AS master_frequency_string, call_sign, 
                     entity_type, licensee_id, entity_name
                FROM combo_fr
              UNION ALL SELECT lower_frequency, call_sign, 
                     entity_type, licensee_id, entity_name
                FROM market_mf INNER JOIN nodup USING (call_sign)
          ) AS u
        INNER JOIN combo_en USING (call_sign)
        INNER JOIN combo_mk USING (call_sign)
        ORDER BY 1 DESC;
    

    我发布这个是因为这是了解您需要什么的最简单方法。

    • 如果call_sign 没有出现在market_mfcombo_frWITH nodup ...INNER JOIN nodup ... 可以省略。
    • 我假设call_signcombo_frmarket_mf 中都是唯一的(= 每个表中没有两条具有相同值的记录),即使两者中都可能出现值表格。
    • 很不幸,您按计算列排序,而且计算非常愚蠢。某种优化是在表本身中一劳永逸地转换频率字符串。步骤是: (1) 将数字频率列添加到表中 (2) 使用从当前文本列转换的值填充它们 (3) 通过输入具有所需小数分隔符的区域设置将新值直接转换为新列。

    【讨论】:

    • @Dario....在 call_sign 上创建索引确实改善了一些事情。非常感谢。我还将执行您建议的最后一步,并将那些应该是数字的文本列转换为新列
    • @user3003374 这是否意味着使用UNION 和随后的两个INNER JOIN 的方法不正确或正确但比你原来的方法差?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多