在Postgres中加入三个表的更有效方法

时间:2017-04-14 12:14:22

标签: postgresql join processing-efficiency

我试图在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;

2 个答案:

答案 0 :(得分:0)

如果可以的话,你会想要解除这些查询并在一个联接中完成所有操作。像这样的东西:

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。

答案 1 :(得分:0)

在所有四个相关表格的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_signmarket_mf中都没有显示combo_fr个值 WITH nodup ...INNER JOIN nodup ...call_sign可以省略。
  • 我假设combo_frmarket_mfi++中都是唯一的(=每个表中没有两条记录具有相同的值),即使可以存在可以出现在两个表中的值。
  • 非常不幸的是,您通过计算列进行排序,并且计算非常愚蠢。某种优化是在表格本身中一次性地转换频率字符串。步骤将是: (1)向表中添加数字frequncy列(2)使用从当前文本列转换的值填充它们(3)将新值直接转换为新列,方法是使用具有所需小数分隔符的语言环境输入它们。 / LI>