我有一个PostgreSQL9.2.1数据库,我在其中尝试并且未能编写SQL查询,该查询将向我显示testname失败的不同测试()的计数(current_status='FAILED'如果没有失败,则显示0),按月进行隔离(last_update)。这是表的定义:
testname
current_status='FAILED'
last_update
Table "public.tests" Column | Type | Modifiers ----------------+-----------------------------+------------------------------------------------------------- id | bigint | not null default nextval('tests_id_seq'::regclass) testname | text | not null last_update | timestamp without time zone | not null default now() current_status | text | not null
我想从中得到的是这样的:
testname | Jan2012 | Feb2012 | Mar2012 | Apr2012 | May2012 | Jun2012 | Jul2012 | Aug2012 | Sep2012 | Oct2012 | Nov2012 | Dec2012 -------------+----------------------------------------------------------------------------------------------------------------------------------------- abq | 2 | 5 | 2 | 0 | 7 | 4 | 8 | 0 | 6 | 15 | 1 | 0 bar | 0 | 0 | 2 | 0 | 9 | 8 | 8 | 2 | 6 | 15 | 1 | 1 cho | 15 | 1 | 2 | 3 | 4 | 8 | 7 | 3 | 6 | 1 | 5 | 6
在这一点上,我能想到的最好的方法是以下方法,但还不能完全解决:
SELECT testname, count(current_status) AS failure_count FROM tests WHERE current_status='FAILED' AND last_update>'2012-09-01' AND last_update<='2012-09-30' GROUP by testname ORDER BY testname ;
我想我需要以某种方式COALESCE获取0值以显示在结果中,再加上一些疯狂的JOIN来显示多个月的结果,甚至可能需要一个窗口函数?
COALESCE
0
crosstab()
应该像这样工作,以获取2012年的价值:
SELECT * FROM crosstab( $$SELECT testname, to_char(last_update, 'mon_YYYY'), count(*)::int AS ct FROM tests WHERE current_status = 'FAILED' AND last_update >= '2012-01-01 0:0' AND last_update < '2013-01-01 0:0' -- proper date range! GROUP BY 1,2 ORDER BY 1,2$$ ,$$VALUES ('jan_2012'::text), ('feb_2012'), ('mar_2012') , ('apr_2012'), ('may_2012'), ('jun_2012') , ('jul_2012'), ('aug_2012'), ('sep_2012') , ('oct_2012'), ('nov_2012'), ('dec_2012')$$) AS ct (testname text , jan_2012 int, feb_2012 int, mar_2012 int , apr_2012 int, may_2012 int, jun_2012 int , jul_2012 int, aug_2012 int, sep_2012 int , oct_2012 int, nov_2012 int, dec_2012 int);
我没有测试。 正如@Craig所说,样本值会有所帮助。 现在使用我自己的测试用例进行了测试。
crosstab()具有两个参数的函数避免了主要问题(根本不会出现没有行的月份)。
您不能COALESCE在内部查询中使用,因为这些NULL值是crosstab()自己插入的。你可以 …
NULL
SELECT testname ,COALESCE(jan_2012, 0) AS jan_2012 ,COALESCE(feb_2012, 0) AS feb_2012 ,COALESCE(mar_2012, 0) AS mar_2012 , ... FROM ( -- query from above) ) x;
LEFT JOIN
在这种情况下,根据定义,您不需要第二个参数。 对于更大的范围,您可以使用generate_series()创建值。
generate_series()
SELECT * FROM crosstab( $$SELECT t.testname, m.mon, count(x.testname)::int AS ct FROM ( VALUES ('jan_2012'::text), ('feb_2012'), ('mar_2012') ,('apr_2012'), ('may_2012'), ('jun_2012') ,('jul_2012'), ('aug_2012'), ('sep_2012') ,('oct_2012'), ('nov_2012'), ('dec_2012') ) m(mon) CROSS JOIN (SELECT DISTINCT testname FROM tests) t LEFT JOIN ( SELECT testname ,to_char(last_update, 'mon_YYYY') AS mon FROM tests WHERE current_status = 'FAILED' AND last_update >= '2012-01-01 0:0' AND last_update < '2013-01-01 0:0' -- proper date range! ) x USING (mon) GROUP BY 1,2 ORDER BY 1,2$$ ) AS ct (testname text , jan_2012 int, feb_2012 int, mar_2012 int , apr_2012 int, may_2012 int, jun_2012 int , jul_2012 int, aug_2012 int, sep_2012 int , oct_2012 int, nov_2012 int, dec_2012 int);
这是一个测试案例,其中包含一些OP无法提供的示例数据。我用它来测试它并使它工作。
CREATE TEMP TABLE tests ( id bigserial PRIMARY KEY ,testname text NOT NULL ,last_update timestamp without time zone NOT NULL DEFAULT now() ,current_status text NOT NULL ); INSERT INTO tests (testname, last_update, current_status) VALUES ('foo', '2012-12-05 21:01', 'FAILED') ,('foo', '2012-12-05 21:01', 'FAILED') ,('foo', '2012-11-05 21:01', 'FAILED') ,('bar', '2012-02-05 21:01', 'FAILED') ,('bar', '2012-02-05 21:01', 'FAILED') ,('bar', '2012-03-05 21:01', 'FAILED') ,('bar', '2012-04-05 21:01', 'FAILED') ,('bar', '2012-05-05 21:01', 'FAILED');