1

这是我正在尝试的查询,

SELECT s.id, s.name AS name,
CASE WHEN (ARRAY_AGG(tgs.status) @> '{Hard} ') THEN 'Hard'
WHEN (ARRAY_AGG(tgs.status) @> '{Soft} ') THEN 'Soft'
WHEN (ARRAY_AGG(tgs.status) @> '{Fluid} ') THEN 'Fluid'
WHEN (ARRAY_AGG(tgs.status) @> '{Gummy} ') THEN 'Gummy'
WHEN (ARRAY_AGG(tgs.status) @> '{Expired} ') THEN 'Expired'
END AS status, 
COUNT(*) OVER()
FROM sweets AS s 
INNER JOIN tasty_goofy_sweets AS tgs on tgs.sweet_id = s.id
GROUP BY s.id;

在实现这一点时,我的朋友建议,我们可以使用 LEFT JOIN LATERAL 并只计算一次,而不是每次在 switch case 中计算 array_agg。即)执行如下

SELECT s.id, s.name AS name,
CASE WHEN (tgs.status @> '{Hard} ') THEN 'Hard'
WHEN (tgs.arr_status @> '{Soft} ') THEN 'Soft'
WHEN (tgs.arr_status @> '{Fluid} ') THEN 'Fluid'
WHEN (tgs.arr_status @> '{Gummy} ') THEN 'Gummy'
WHEN (tgs.arr_status @> '{Expired} ') THEN 'Expired'
END AS status, 
COUNT(*) OVER()
FROM sweets AS s 
LEFT JOIN LATERAL ( SELECT ARRAY_AGG(tgs.status) AS arr_status FROM tasty_goofy_sweets tgs WHERE  tgs.sweet_id = s.id
) AS tgs ON TRUE
GROUP BY s.id;

但我不确定 Postgres 是否ARRAY_AGG每次都计算该值,我们如何确定哪种方法更好?我尝试查看explain analyse这两个查询,后者查询中涉及的行数比前者多。但我不明白为什么会这样?

我凭直觉觉得前一种方法更好,但是有人可以解释一下,哪种更好,为什么还是我要求太多?

4

2 回答 2

2

最有可能的是,Postgres 会优化掉多个array_agg()s,只计算一次并在每次比较中重用结果。这是非常简单的查询优化,数据库应该很容易发现。

但是,让我建议通过使用条件聚合来简化查询。哟不需要聚合成一个数组只是为了检查给定的值是否存在:

select
    s.id,
    s.name
    case 
        when count(*) filter(where status = 'Hard')    > 0 then 'Hard',
        when count(*) filter(where status = 'Soft')    > 0 then 'Soft',
        when count(*) filter(where status = 'Fluid')   > 0 then 'Fluid'
        when count(*) filter(where status = 'Gummy')   > 0 then 'Gummy',
        when count(*) filter(where status = 'Expired') > 0 then 'Expired'
    end status,
    count(*) over() cnt
from sweets s
inner join tasty_goofy_sweets AS tgs on tgs.sweet_id = s.id
group by s.id;

您也可以使用横向连接和条件排序来表达这一点而无需聚合:

select
    s.id,
    s.name,
    tgs.status,
    count(*) over() cnt
from sweets s
cross join lateral (
    select status
    from tasty_goofy_sweets as tgs 
    where tgs.sweet_id = s.id
    order by case status 
        when 'Hard'    then 1
        when 'Soft'    then 2
        when 'Fluid'   then 3
        when 'Gummy'   then 4
        when 'Expired' then 5
    end
    limit 1
) tgs
于 2020-06-14T17:14:53.577 回答
1

我相当肯定,在一个case表达式中,该when子句将针对每个条件单独评估。这意味着你的同事是正确的。. . 大概。

文档的操作部分是:

每个条件都是一个返回布尔结果的表达式。如果条件的结果为真,则 CASE 表达式的值是条件后面的结果,并且不处理 CASE 表达式的其余部分。

Postgres 有可能通过评估一次子表达式来对子表达式进行某种优化。但是,声明:“未处理 CASE 表达式的其余部分”非常强大,它表明每个子句只会在前面的子句评估为假(或)之后才被处理NULL

不管优化器是否发现一个函数只能被调用一次,横向连接保证它会被评估一次,所以对于昂贵的操作来说,这似乎是更安全的解决方案。

于 2020-06-15T13:21:14.383 回答