我有一张类似于以下内容的表格:
CREATE TABLE stats (
name character varying(15),
q001001 numeric(9,0),
q001002 numeric(9,0),
q001003 numeric(9,0),
q001004 numeric(9,0),
q001005 numeric(9,0)
)
我需要在此表中查询各个字段的总和,如下所示:
SELECT sum(q001001) as total001,
sum(q001002) as total002,
sum(q001005) as total005,
FROM stats;
这会产生一行数据和三列数据 但是,出于报告目的,我需要以相反的方式列出结果。我需要三行和一列(好吧,实际上是两个,第一个是总和的字段是这样的):
FieldName | SUM
----------+-------
q001001 | 12345
q001002 | 5432
q001005 | 986
我想使用这样的SQL,其中field_name
(来自stats表中字段名称的查找表)用于子查询:
select l.field_name, (select sum(l.field_name) from stats)
from stats_field_names_lookup as l
where l.field_name in ('Q001001', 'Q001002', 'Q001005');
这里的想法是sum(l.field_name)
将替换为所讨论的实际字段名称,对于WHERE
子句中的每一个,然后进行评估以提供正确的总和结果值。但是,这会因以下错误而失败:
函数sum(字符变化)不存在
因为值有文字/字符。如何将该字符值转换为要正确计算的未加引号的字符串?
这个SQL有效。但是,当然,为每个field_name
提供相同的总和值,因为它在此处被硬编码为q001001
。
select l.field_name, (select sum(q001001) from stats)
from stats_field_names_lookup as l
where l.field_name in ('Q001001', 'Q001002', 'Q001005');
所以,我认为这个想法在理论上是合理的。只需要帮助弄清楚如何将该字符/字符串理解为field_name。有人有什么想法吗?
答案 0 :(得分:1)
单独计算每笔金额效率低下。在单个SELECT
和“交叉制表”结果中执行此操作
为了使答案“简短”,我在结果中减少了两列。根据需要展开。
将两个数组并行的数组相同。有关此技术的详细信息here和here。
SELECT unnest('{q001001,q001002}'::text[]) AS fieldname
,unnest(ARRAY[sum(q001001), sum(q001002)]) AS result
FROM stats;
“Dirty”,因为并行取消是一种非标准的Postgres行为,有些人不赞同。虽然像魅力一样。请点击链接了解更多信息。
使用CTE和UNION ALL
个别行:
WITH cte AS (
SELECT sum(q001001) AS s1
,sum(q001002) AS s2
FROM stats
)
SELECT 'q001001'::text AS fieldname, s1 AS result FROM cte
UNION ALL
SELECT 'q001002'::text, s2 FROM cte;
“清理”,因为它纯粹是标准的SQL。
最短的形式,但也更难理解:
SELECT unnest(ARRAY[
('q001001', sum(q001001))
,('q001002', sum(q001002))])
FROM stats;
这是一系列匿名记录,很难取消(但可能)。
要获取具有原始类型的单个列,请在系统中声明类型:
CREATE TYPE fld_sum AS (fld text, fldsum numeric)
您可以通过创建临时表临时对会话执行相同的操作:
CREATE TEMP TABLE fld_sum (fld text, fldsum numeric);
然后:
SELECT (unnest(ARRAY[
('q001001'::text, sum(q001001)::numeric)
,('q001002'::text, sum(q001002)::numeric)]::fld_sum[])).*
FROM stats;
所有四种变体的性能基本相同,因为昂贵的部分是聚合 SQL Fiddle展示所有变体(基于fiddle provided by @klin)。
构建并执行上面相应章节中概述的代码。
CREATE OR REPLACE FUNCTION f_list_of_sums1(_tbl regclass, _flds text[])
RETURNS TABLE (fieldname text, result numeric) AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT '
SELECT unnest ($1)
,unnest (ARRAY[sum(' || array_to_string(_flds, '), sum(')
|| ')])::numeric
FROM ' || _tbl)
USING _flds;
END
$func$ LANGUAGE plpgsql;
呼叫:
SELECT * FROM f_list_of_sums1('stats', '{q001001, q001002}');
构建并执行上面相应章节中概述的代码。
CREATE OR REPLACE FUNCTION f_list_of_sums2(_tbl regclass, _flds text[])
RETURNS TABLE (fieldname text, result numeric) AS
$func$
BEGIN
-- RAISE NOTICE '%', ( -- to get debug output uncomment this line ..
RETURN QUERY EXECUTE ( -- .. and comment this one
SELECT 'WITH cte AS (
SELECT ' || string_agg(
format('sum(%I)::numeric AS s%s', _flds[i], i)
,E'\n ,') || '
FROM ' || _tbl || '
)
' || string_agg(
format('SELECT %L, s%s FROM cte', _flds[i], i)
, E'\nUNION ALL\n')
FROM generate_subscripts(_flds, 1) i
);
END
$func$ LANGUAGE plpgsql;
如上所述。
stats
。numeric
)。format()
和regclass
解释了:string_agg()
. SQL Fiddle展示所有变体。
数据类型numeric(9,0)
是表定义的一种相当低效的选择。由于您不存储小数位数且不超过9位小数,因此请使用简单的 integer
。它仅对 4字节的存储(而不是numeric(9,0)
的8-12字节)执行相同的操作。如果在计算中需要数值精度,则始终可以以可忽略的成本投射色谱柱
另外,I don't use varchar(n)
unless I have to. Just use text
.
所以我建议:
CREATE TABLE stats (
name text
,q001001 int
,q001002 int
, ...
);
答案 1 :(得分:1)
在plpgsql函数中使用execute。 SqlFiddle
create or replace function show_stats(field_names text[])
returns table ("FieldName" text, "SUM" numeric)
language plpgsql as $$
declare
fname text;
begin
foreach fname in array field_names loop
return query execute format('
select ''%s''::text, sum(%s) from stats',
fname, fname);
end loop;
end $$;
select * from show_stats(array['q001001', 'q001002', 'q001003', 'q001004']);
select * from show_stats(array['q001001', 'q001004']);
答案 2 :(得分:0)
其实我不知道如何动态指定列名,但我建议这样做。
SELECT 'q001001' as FieldName, sum(q001001) as SUM FROM stats
UNION SELECT 'q001002' as FieldName, sum(q001002) as SUM FROM stats
UNION SELECT 'q001003' as FieldName, sum(q001003) as SUM FROM stats;
这很容易,并且可以解决您原来的问题。