postgresql 在GROUP BY之前使用EXCEPT查询失败

qaxu7uf2  于 5个月前  发布在  PostgreSQL
关注(0)|答案(3)|浏览(91)

下面的查询试图从主数组中排除一个数组,然后对结果进行分组。

SELECT utc_offset, is_dst,
       ltrim(
               trim(string_agg(distinct (CASE WHEN abbrev NOT LIKE '+%' AND abbrev NOT LIKE '-%' AND abbrev != name THEN abbrev ELSE '' END), ' ')) ||
               ' ' || string_agg(name, ', ' ORDER BY name)
       )
FROM pg_timezone_names
WHERE name NOT LIKE 'posix/%'
  AND name NOT LIKE 'Etc/%'
  AND (lower(abbrev) <> abbrev)
  AND name NOT IN ('HST', 'Factory', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'localtime', 'UCT', 'Universal', 'UTC', 'PST8PDT', 'ROK', 'W-SU', 'MST', 'CST6CDT')
EXCEPT
SELECT n.*, a.*
FROM   pg_timezone_names n 
JOIN   pg_timezone_abbrevs a ON  a.abbrev = n.name
WHERE  n.utc_offset <> a.utc_offset
GROUP BY utc_offset, is_dst
ORDER BY utc_offset, is_dst
;

字符串
而是生成一个错误:

ERROR:  column "pg_timezone_names.utc_offset" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT utc_offset, is_dst,


查询没有EXCEPT块也能正常工作。显然存在语法问题--GROUP BY(以及扩展)ORDER BY动词没有被看到。
那么这个查询应该如何转换呢?

igetnqfo

igetnqfo1#

在这种情况下,您试图在对两个子查询应用EXCEPT子句后聚合它们的结果。要实现这一点,您可以先应用EXCEPT,然后使用公共表表达式(CTE)执行聚合。以下是更新后的查询:

WITH cte AS (
    SELECT utc_offset, is_dst,
           ltrim(
               trim(string_agg(distinct (CASE WHEN abbrev NOT LIKE '+%' AND abbrev NOT LIKE '-%' AND abbrev != name THEN abbrev ELSE '' END), ' ')) ||
               ' ' || string_agg(name, ', ' ORDER BY name)
           ) AS agg_column
    FROM pg_timezone_names
    WHERE name NOT LIKE 'posix/%'
      AND name NOT LIKE 'Etc/%'
      AND (lower(abbrev) <> abbrev)
      AND name NOT IN ('HST', 'Factory', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'localtime', 'UCT', 'Universal', 'UTC', 'PST8PDT', 'ROK', 'W-SU', 'MST', 'CST6CDT')
    GROUP BY utc_offset, is_dst
)
SELECT utc_offset, is_dst, agg_column
FROM cte
EXCEPT
SELECT n.utc_offset, n.is_dst, a.agg_column
FROM pg_timezone_names n 
JOIN pg_timezone_abbrevs a ON a.abbrev = n.name
WHERE n.utc_offset <> a.utc_offset
ORDER BY utc_offset, is_dst;

字符串
希望能成功:)

eivgtgni

eivgtgni2#

您遇到的错误是由EXCEPT子句引起的,该子句组合了两个查询的结果。使用EXCEPT时,两个查询的列名和数据类型必须匹配。在您的示例中,第一个查询选择特定的列(utc_offset、is_dst和连接字符串),而第二个查询是使用n.* 和a.* 从pg_timezone_names和pg_timezone_focus vs表中选择所有列。列选择不匹配导致了错误。若要解决此问题,请更新查询,使EXCEPT语句的两个部分中的SELECT子句具有相同的列名和数据类型。在EXCEPT子句的两个部分中,您可以专门选择所需的列。以下是如何修改查询:

SELECT utc_offset, is_dst,
       ltrim(
               trim(string_agg(distinct (CASE WHEN abbrev NOT LIKE '+%' AND abbrev NOT LIKE '-%' AND abbrev != name THEN abbrev ELSE '' END), ' ')) ||
               ' ' || string_agg(name, ', ' ORDER BY name)
       )
FROM pg_timezone_names
WHERE name NOT LIKE 'posix/%'
  AND name NOT LIKE 'Etc/%'
  AND (lower(abbrev) <> abbrev)
  AND name NOT IN ('HST', 'Factory', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'localtime', 'UCT', 'Universal', 'UTC', 'PST8PDT', 'ROK', 'W-SU', 'MST', 'CST6CDT')
EXCEPT
SELECT n.utc_offset, n.is_dst,
       ltrim(
               trim(string_agg(distinct (CASE WHEN a.abbrev NOT LIKE '+%' AND a.abbrev NOT LIKE '-%' AND a.abbrev != n.name THEN a.abbrev ELSE '' END), ' ')) ||
               ' ' || string_agg(n.name, ', ' ORDER BY n.name)
       )
FROM   pg_timezone_names n 
JOIN   pg_timezone_abbrevs a ON  a.abbrev = n.name
WHERE  n.utc_offset <> a.utc_offset
GROUP BY n.utc_offset, n.is_dst
ORDER BY n.utc_offset, n.is_dst;

字符串
希望对你有帮助。

ffx8fchx

ffx8fchx3#

提问

我们的目标是……
从主数组中排除一个数组,然后对结果进行分组。
我觉得你真的想要这个:

“在 * 聚合符合条件的行之前,删除存在名称相同但偏移量不同的缩写的时区。"

查询有什么问题?

  • 你真的想在聚合字符串之前 * 排除行。仅仅添加括号是解决不了问题的。
  • EXCEPT需要兼容的SELECT左右列表。
  • SELECT n.*, a.*是一个惯犯:它也不同意GROUP BY子句-观察到的错误消息的直接原因。

概念验证

您的尝试将像这样工作:

SELECT utc_offset, is_dst,
       ltrim(
               trim(string_agg(distinct (CASE WHEN abbrev NOT LIKE '+%' AND abbrev NOT LIKE '-%' AND abbrev != name THEN abbrev ELSE '' END), ' ')) ||
               ' ' || string_agg(name, ', ' ORDER BY name)
       )
FROM  (
   SELECT utc_offset, is_dst, name, abbrev
   FROM   pg_timezone_names
   WHERE  name NOT LIKE 'posix/%'
   AND    name NOT LIKE 'Etc/%'
   AND    (lower(abbrev) <> abbrev)
   AND    name NOT IN ('HST', 'Factory', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'localtime', 'UCT', 'Universal', 'UTC', 'PST8PDT', 'ROK', 'W-SU', 'MST', 'CST6CDT')
   EXCEPT
   SELECT n.utc_offset, n.is_dst, n.name, n.abbrev
   FROM   pg_timezone_names n 
   JOIN   pg_timezone_abbrevs a ON  a.abbrev = n.name
   WHERE  n.utc_offset <> a.utc_offset
   ) sub
GROUP BY utc_offset, is_dst
ORDER BY utc_offset, is_dst
;

字符串
但不要。而是用这个100%的等价物...

上级查询

SELECT utc_offset, is_dst
     , concat_ws(' ', string_agg(DISTINCT abbrev, ' ' ORDER BY abbrev) FILTER (WHERE NOT abbrev ^@ ANY ('{+,-}') AND abbrev <> name)
                    , string_agg(name, ', ' ORDER BY name)) AS abbrevs_and_names
FROM   pg_timezone_names n
WHERE  lower(abbrev) <> abbrev
AND    NOT name ^@ ANY ('{posix/, Etc/}')
AND    name <> ALL ('{HST, Factory, GMT, GMT+0, GMT-0, GMT0, localtime, UCT, Universal, UTC, PST8PDT, ROK, W-SU, MST, CST6CDT}')
AND    NOT EXISTS (  -- !!!
   SELECT FROM pg_timezone_abbrevs a
   WHERE  a.abbrev = n.name
   AND    a.utc_offset <> n.utc_offset
   )
GROUP  BY utc_offset, is_dst
ORDER  BY utc_offset, is_dst
;


fiddle
NOT EXISTS在这里比EXCEPT容易得多。(大多数情况下都是这样。)参见:

  • 选择其他表中不存在的行

我还添加了几个其他(可选)简化和优化
关于“starts with”运算符^@(至少需要Postgres 11):

  • PostgreSQL LIKE查询性能变化

关于ANY结构:

  • PostgreSQL中的IN与ANY运算符

关于concat_ws()的空安全级联:

  • 如何在Postgres SELECT中连接列?-https:stackoverflow.com/questions/12310986/combine-two-columns-and-add-into-one-new-column/12320369#12320369

关于聚合FILTER子句:

  • 具有其他(相异)筛选条件的汇总数据行

相关问题