如何旋转两列 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 BY
和ORDER 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 字符串立即反馈给服务器。参见:
这可能是一个新手问题 – 我还在学习中。我在 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 BY
和ORDER 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 字符串立即反馈给服务器。参见: