如何旋转两列 table?

How to rotate a two-column table?

这可能是一个新手问题 – 我还在学习中。我在 PostgreSQL 9.6 上使用以下查询:

SELECT locales, count(locales) FROM (
    SELECT lower((regexp_matches(locale, '([a-z]{2,3}(-[a-z]{2,3})?)', 'i'))[1]) 
    AS locales FROM users) 
AS _ GROUP BY locales

我查询 returns 以下动态行:

locales count
en 10
fr 7
de 3
n additional locales (~300)... n-count

我正在尝试旋转它,以便语言环境值最终成为具有单行的列,如下所示:

en fr de n additional locales (~300)...
10 7 3 n-count

我必须这样做才能很好地处理时间序列 db/app

我试过使用 crosstab(),但所有示例都显示了具有 3 列或更多列的更好定义的表格。

我看过使用 join 的示例,但我不知道如何动态执行。

基本查询

在 Postgres 10 或更高版本中,您可以使用更简单、更快的 regexp_match() 而不是 regexp_matches()。 (因为无论如何你只取每行的第一个匹配项。)但不要费心,使用更简单的 substring() 代替:

SELECT lower(substring(locale, '(?i)[a-z]{2,3}(?:-[a-z]{2,3})?')) AS locale
     , count(*)::int AS ct
FROM   users
WHERE  locale ~* '[a-z]{2,3}'  -- eliminate NULL, allow index support
GROUP  BY 1
ORDER  BY 2 DESC, 1

比原来的基本查询更简单、更快。

关于GROUP BYORDER BY中的那些序数:

  • Select first row in each GROUP BY group?

细微差别:regexp_matches() returns 无行表示不匹配,而substring() returns null .我添加了一个 WHERE 子句来消除 non-matches a-priori - 并允许索引支持(如果适用),但我不希望索引在这里有所帮助。

注意前缀 (?i),这是 so-called "embedded option" 使用 case-insensitive 匹配。

添加了确定性 ORDER BY 子句。你需要一个简单的 crosstab().

旁白:对于像“en_US”这样的语言环境,您可能需要在模式中使用 _ 而不是 -

枢轴

尽您所能,SQL 不允许在单个查询中使用动态结果列。您需要两次往返服务器。见;

可以使用动态生成的crosstab()查询。基础知识:

  • PostgreSQL Crosstab Query

动态查询:

  • PostgreSQL convert columns to rows? Transpose?

但是因为你生成单行普通整数值,我建议一个简单的方法:

SELECT 'SELECT ' || string_agg(ct || ' AS ' || quote_ident(locale), ', ')
FROM  (
   SELECT lower(substring(locale, '(?i)[a-z]{2,3}(?:-[a-z]{2,3})?')) AS locale
        , count(*)::int AS ct
   FROM   users
   WHERE  locale ~* '[a-z]{2,3}'
   GROUP  BY 1
   ORDER  BY 2 DESC, 1
   ) t

生成以下形式的查询:

SELECT 10 AS en, 7 AS fr, 3 AS de, 3 AS "de-at"

执行它以产生您想要的结果。

在 psql 中,您可以将 \gexec 附加到生成查询以将生成的 SQL 字符串立即反馈给服务器。参见: