GROUP BY 失败之前使用 EXCEPT 进行查询

问题描述 投票:0回答:3

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

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
动词没有被看到。

这个查询应该如何转换?

sql postgresql except
3个回答
0
投票

在这种情况下,您尝试在对两个子查询应用 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;

希望它有效:)


0
投票

您遇到的错误是由于 EXCEPT 子句造成的,该子句组合了两个查询的结果。当您使用 EXCEPT 时,两个查询的列名和数据类型必须匹配。在您的情况下,第一个查询是选择特定列(utc_offset、is_dst 和连接字符串),而第二个查询是使用 n.* 和 a.* 从 pg_timezone_names 和 pg_timezone_abbrevs 表中选择所有列。列选择中的不匹配导致了错误。 要解决此问题,请更新查询,使 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;

希望有帮助。


0
投票

问题

规定的目标是......

从主数组中排除一个数组,然后对结果进行分组。

我想你真的想要这个:

“排除存在名称相同但偏移量不同的缩写的时区 - 在聚合合格行之前。”

查询有什么问题?

  • 您确实想在聚合字符串之前排除行。仅添加括号并不能解决问题。
  • 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 ;

小提琴 这里的

NOT EXISTS

EXCEPT
容易得多。 (大多数时候都是这样。)参见:

  • 选择其他表中不存在的行
我投入了一些

其他(可选)简化和优化

关于“开头为”运算符

^@

(至少需要 Postgres 11):

  • PostgreSQL LIKE 查询性能变化
关于

ANY

 构造:

  • IN 与 PostgreSQL 中的 ANY 运算符
关于与

concat_ws()

 的空安全串联:

关于聚合

FILTER

条款:

  • 使用附加(不同)过滤器聚合列
© www.soinside.com 2019 - 2024. All rights reserved.