我有人,公司和员工。这些表中的每一个都有一个外键返回到我称之为派对的表,它通过触发器维护兄弟记录。缔约方有0到N个site_locators,它们本身引用参考城市的网站。
我正在构建一个视图,它将返回收件人的全名和它的城市。因此,代码就是:
SELECT
surname
, rest_of_name
, 'Employee' AS party_type_code
, party_id
, cities.name AS city_name
FROM
employees
LEFT JOIN site_locators USING (party_id)
LEFT JOIN sites USING (site_id)
LEFT JOIN cities USING (city_id)
UNION ALL
SELECT
surname
, NULL::text
, 'Company' AS party_type_code
, party_id
, cities.name AS city_name
FROM
companies
LEFT JOIN site_locators USING (party_id)
LEFT JOIN sites USING (site_id)
LEFT JOIN cities USING (city_id)
UNION ALL
SELECT
surname
, rest_of_name
, 'Person' AS party_type_code
, party_id
, cities.name AS city_name
FROM
people
LEFT JOIN site_locators USING (party_id)
LEFT JOIN sites USING (site_id)
LEFT JOIN cities USING (city_id)
我真的不喜欢每个UNION上的多个LEFT JOIN。我重构了代码,只作为一个单独的步骤执行JOIN:
SELECT
parties.*
, cities.name AS city_name
FROM (
SELECT /* As above, minus city_name */
FROM employees
UNION ALL
SELECT /* As above, minus city_name */
FROM companies
UNION ALL
SELECT /* As above, minus city_name */
FROM people
) AS parties
LEFT JOIN site_locators USING (party_id)
LEFT JOIN sites USING (site_id)
LEFT JOIN cities USING (city_id)
第二个查询更快,因为JOIN只在所有表被过滤后发生一次。我想知道的是,如果我可以进一步重构这样,如果site_locators没有为一方返回任何行,那么我甚至不需要搜索网站或城市?我想知道这是否可以用一些INNER JOIN而不是所有LEFT JOIN重写。我之前对LEFT JOIN的体验很糟糕,并希望减少它们的数量。
注意:存在派对表以减少表的数量。如果它不存在,我需要每个聚会类型和定位器类型的表:employee_site_locators,people_site_locators,company_site_locators,employee_email_locators,people_email_locators等等。使用派对表,每个定位器类型可以有一个* _locator表。
答案 0 :(得分:0)
首先,第一个重构非常好。
其次,是左联接约束计划者。然而,将这些阻塞到内部连接块中并使用外部连接向后以更多方式约束规划器,这些是更大的限制。一般来说,我看到规划人员在左连接被你的描述方式考虑在内时,不太可能使用索引。
一般来说,最好首先关注清晰度,然后再考虑调整以提高性能,而不是先担心性能,然后再进行清晰度处理。有一点可以帮助用USING
替换ON.
。最后看一下你的模型,看看什么是左连接,什么是内连接。请注意,这些是非常不同的,所以不能直接替换它们。