可变日期格式查询
Variable date format query
我正在尝试将从 PostgreSQL table(使用索引聚合数组)中提取的字符串转换为格式正确的日期以供查询。我的问题是我的日期格式各不相同,包括 YYYY、Mon YYYY 和 DD Mon YYYY。我的计划是创建日期范围以包含所有可能由模糊日期表示的时间。例如,“2000”将转换为“2000 年 1 月 1 日”和“2000 年 12 月 31 日”,并针对自定义输入日期范围进行测试。同样,"Feb 2014" 将更改为“2014 年 2 月 1 日”和“2014 年 2 月 28 日”。 (注意 - 我目前想不出一种方法来解释闰年)
我目前使用的方法涉及简单的字符串连接。但是,我需要能够区分字符串长度(这将指示日期格式),并且我正在努力将条件表达式合并到我的查询中。这是我目前得到的:
SELECT a.accession, string_agg(b.value, ' | ') AS bvalue_list, c.name, d.description, string_agg(e.value, ' | ') AS evalue_list, f.seqlen, f.residues
FROM dbxref a INNER JOIN dbxrefprop b ON a.dbxref_id = b.dbxref_id
INNER JOIN biomaterial d ON b.dbxref_id = d.dbxref_id
INNER JOIN feature f ON d.dbxref_id = f.dbxref_id
INNER JOIN biomaterialprop e ON d.biomaterial_id = e.biomaterial_id
INNER JOIN contact c ON d.biosourceprovider_id = c.contact_id
GROUP BY a.accession, c.name, d.description, f.seqlen, f.residues
HAVING ((array_agg(b.value))[5] = 'source018' OR (array_agg(b.value))[5] = 'source015')
AND to_date('04 Jan ' || (array_agg(e.value))[3], 'DD Mon YYYY') BETWEEN '01 Jan 1999' AND '31 Jan 2000';
对于疯狂的查询语句,我深表歉意。由于它的怪癖(其中信息由 "HAVING" 子句而不是 "WHERE" 处理),我想完整地展示该陈述。相关部分(最底行)归结为 selecting 用于表示字符串格式时间的索引聚合。我的搜索条件有意过滤掉不符合所用连接方法的日期(不包括 Mon YYYY 和 DD Mon YYYY 日期)。我一直在尝试合并 "CASE" 条件,但我不确定 if/how 我能否将其放入现有查询中。
问题的简化
我需要修改以下查询:
SELECT e.biomaterial_id, string_agg(e.value, ' | ') AS evalue_list
FROM biomaterialprop e
GROUP BY e.biomaterial_id;
产生:
biomaterial_id | evalue_list
----------------+------------------------------------------------
8 | NULL | Feb 2002 | Canada | T2
4 | NULL | 03 Mar 2008 | Hainan, China | T2
5 | nasal swab | Oct 2010 | Fujian, China | T1
11 | nasal swab | 10 Apr 2014 | Nebraska, USA | T1
3 | lung tissue | 01 Jan 2005 | Nebraska, USA | T2
10 | lung tissue | 2005 | USA | T2
9 | serum | 2001 | Ohio, USA | T1
6 | serum | 2000 | Utah, USA | T1
2 | serum | 01 Jan 2005 | Iowa, USA | T1
7 | NULL | 02 Aug 1998 | Alberta, Canada | T2
我可以通过使用 (array_agg(e.value))[3] 进行索引来 select 日期字段。接下来我需要修改日期字符串并将它们插入单独的输出 column/s。我认为它应该看起来像这样(目前不工作):
SELECT e.biomaterial_id, string_agg(e.value, ' | ') AS evalue_list,
CASE char_length((array_agg(e.value))[3])
WHEN 11
THEN to_date((array_agg(e.value))[3], 'DD Mon YYYY')
WHEN 8
THEN to_date('01 ' || (array_agg(e.value))[3], 'DD Mon YYYY')
ELSE to_date('01 Jan ' || (array_agg(e.value))[3], 'DD Mon YYYY')
END
AS date1
CASE char_length((array_agg(e.value))[3])
WHEN 11
THEN to_date((array_agg(e.value))[3], 'DD Mon YYYY')
WHEN 8
THEN last_day(to_date('01 ' || (array_agg(e.value))[3], 'DD Mon YYYY'))
ELSE to_date('31 Dec ' || (array_agg(e.value))[3], 'DD Mon YYYY')
END
AS date2
FROM biomaterialprop e
GROUP BY e.biomaterial_id, date1, date2;
我正在尝试从这个堆栈中重现答案的查询结构 post:
IF-THEN-ELSE statements in postgresql
Edit1 - 已经好几个月了,我想我至少在 SQL 方面更胜一筹了。也就是说,我真的不满意这个旧的解决方案。任何替代建议或解决方案都会有所帮助。
包含时间信息的table演示:
specimen_collection_date
--------------------------
01-Nov-2013
2013
2012
04-Jul-2013
16-Jan-2011
Jan-2011
2001
Nov-2005
简化问题的答案
代码:
create or replace function last_day(date) returns date as 'select
cast(date_trunc(''month'', ) + ''1 month''::interval as date) - 1'
language sql;
SET search_path = chado;
SELECT specimen_collection_date,
CASE
WHEN char_length(specimen_collection_date) = 11
THEN to_date(specimen_collection_date, 'DD Mon YYYY')
WHEN char_length(specimen_collection_date) = 8
THEN to_date('01 ' || specimen_collection_date, 'DD Mon YYYY')
ELSE to_date('01 Jan ' || specimen_collection_date, 'DD Mon YYYY')
END
AS date1,
CASE
WHEN char_length(specimen_collection_date) = 11
THEN to_date(specimen_collection_date, 'DD Mon YYYY')
WHEN char_length(specimen_collection_date) = 8
THEN last_day(to_date('01 ' || specimen_collection_date, 'DD Mon YYYY'))
ELSE to_date('31 Dec ' || specimen_collection_date, 'DD Mon YYYY')
END
AS date2
FROM prrsv_search_mv WHERE specimen_collection_date != '';
输出:
specimen_collection_date | date1 | date2
--------------------------+------------+------------
01-Nov-2013 | 2013-11-01 | 2013-11-01
2013 | 2013-01-01 | 2013-12-31
2012 | 2012-01-01 | 2012-12-31
04-Jul-2013 | 2013-07-04 | 2013-07-04
16-Jan-2011 | 2011-01-16 | 2011-01-16
Jan-2011 | 2011-01-01 | 2011-01-31
2001 | 2001-01-01 | 2001-12-31
Nov-2005 | 2005-11-01 | 2005-11-30
因为 PostgreSQL 没有 last_day 函数,所以必须定制(来源:https://www.postgresql.org/message-id/Pine.LNX.4.44.0309021522180.17073-100000%40kix.fsv.cvut.cz)。
Edit1 - 更新当前答案以匹配最近的问题编辑。
我正在尝试将从 PostgreSQL table(使用索引聚合数组)中提取的字符串转换为格式正确的日期以供查询。我的问题是我的日期格式各不相同,包括 YYYY、Mon YYYY 和 DD Mon YYYY。我的计划是创建日期范围以包含所有可能由模糊日期表示的时间。例如,“2000”将转换为“2000 年 1 月 1 日”和“2000 年 12 月 31 日”,并针对自定义输入日期范围进行测试。同样,"Feb 2014" 将更改为“2014 年 2 月 1 日”和“2014 年 2 月 28 日”。 (注意 - 我目前想不出一种方法来解释闰年)
我目前使用的方法涉及简单的字符串连接。但是,我需要能够区分字符串长度(这将指示日期格式),并且我正在努力将条件表达式合并到我的查询中。这是我目前得到的:
SELECT a.accession, string_agg(b.value, ' | ') AS bvalue_list, c.name, d.description, string_agg(e.value, ' | ') AS evalue_list, f.seqlen, f.residues
FROM dbxref a INNER JOIN dbxrefprop b ON a.dbxref_id = b.dbxref_id
INNER JOIN biomaterial d ON b.dbxref_id = d.dbxref_id
INNER JOIN feature f ON d.dbxref_id = f.dbxref_id
INNER JOIN biomaterialprop e ON d.biomaterial_id = e.biomaterial_id
INNER JOIN contact c ON d.biosourceprovider_id = c.contact_id
GROUP BY a.accession, c.name, d.description, f.seqlen, f.residues
HAVING ((array_agg(b.value))[5] = 'source018' OR (array_agg(b.value))[5] = 'source015')
AND to_date('04 Jan ' || (array_agg(e.value))[3], 'DD Mon YYYY') BETWEEN '01 Jan 1999' AND '31 Jan 2000';
对于疯狂的查询语句,我深表歉意。由于它的怪癖(其中信息由 "HAVING" 子句而不是 "WHERE" 处理),我想完整地展示该陈述。相关部分(最底行)归结为 selecting 用于表示字符串格式时间的索引聚合。我的搜索条件有意过滤掉不符合所用连接方法的日期(不包括 Mon YYYY 和 DD Mon YYYY 日期)。我一直在尝试合并 "CASE" 条件,但我不确定 if/how 我能否将其放入现有查询中。
问题的简化
我需要修改以下查询:
SELECT e.biomaterial_id, string_agg(e.value, ' | ') AS evalue_list
FROM biomaterialprop e
GROUP BY e.biomaterial_id;
产生:
biomaterial_id | evalue_list
----------------+------------------------------------------------
8 | NULL | Feb 2002 | Canada | T2
4 | NULL | 03 Mar 2008 | Hainan, China | T2
5 | nasal swab | Oct 2010 | Fujian, China | T1
11 | nasal swab | 10 Apr 2014 | Nebraska, USA | T1
3 | lung tissue | 01 Jan 2005 | Nebraska, USA | T2
10 | lung tissue | 2005 | USA | T2
9 | serum | 2001 | Ohio, USA | T1
6 | serum | 2000 | Utah, USA | T1
2 | serum | 01 Jan 2005 | Iowa, USA | T1
7 | NULL | 02 Aug 1998 | Alberta, Canada | T2
我可以通过使用 (array_agg(e.value))[3] 进行索引来 select 日期字段。接下来我需要修改日期字符串并将它们插入单独的输出 column/s。我认为它应该看起来像这样(目前不工作):
SELECT e.biomaterial_id, string_agg(e.value, ' | ') AS evalue_list,
CASE char_length((array_agg(e.value))[3])
WHEN 11
THEN to_date((array_agg(e.value))[3], 'DD Mon YYYY')
WHEN 8
THEN to_date('01 ' || (array_agg(e.value))[3], 'DD Mon YYYY')
ELSE to_date('01 Jan ' || (array_agg(e.value))[3], 'DD Mon YYYY')
END
AS date1
CASE char_length((array_agg(e.value))[3])
WHEN 11
THEN to_date((array_agg(e.value))[3], 'DD Mon YYYY')
WHEN 8
THEN last_day(to_date('01 ' || (array_agg(e.value))[3], 'DD Mon YYYY'))
ELSE to_date('31 Dec ' || (array_agg(e.value))[3], 'DD Mon YYYY')
END
AS date2
FROM biomaterialprop e
GROUP BY e.biomaterial_id, date1, date2;
我正在尝试从这个堆栈中重现答案的查询结构 post: IF-THEN-ELSE statements in postgresql
Edit1 - 已经好几个月了,我想我至少在 SQL 方面更胜一筹了。也就是说,我真的不满意这个旧的解决方案。任何替代建议或解决方案都会有所帮助。
包含时间信息的table演示:
specimen_collection_date
--------------------------
01-Nov-2013
2013
2012
04-Jul-2013
16-Jan-2011
Jan-2011
2001
Nov-2005
简化问题的答案
代码:
create or replace function last_day(date) returns date as 'select
cast(date_trunc(''month'', ) + ''1 month''::interval as date) - 1'
language sql;
SET search_path = chado;
SELECT specimen_collection_date,
CASE
WHEN char_length(specimen_collection_date) = 11
THEN to_date(specimen_collection_date, 'DD Mon YYYY')
WHEN char_length(specimen_collection_date) = 8
THEN to_date('01 ' || specimen_collection_date, 'DD Mon YYYY')
ELSE to_date('01 Jan ' || specimen_collection_date, 'DD Mon YYYY')
END
AS date1,
CASE
WHEN char_length(specimen_collection_date) = 11
THEN to_date(specimen_collection_date, 'DD Mon YYYY')
WHEN char_length(specimen_collection_date) = 8
THEN last_day(to_date('01 ' || specimen_collection_date, 'DD Mon YYYY'))
ELSE to_date('31 Dec ' || specimen_collection_date, 'DD Mon YYYY')
END
AS date2
FROM prrsv_search_mv WHERE specimen_collection_date != '';
输出:
specimen_collection_date | date1 | date2
--------------------------+------------+------------
01-Nov-2013 | 2013-11-01 | 2013-11-01
2013 | 2013-01-01 | 2013-12-31
2012 | 2012-01-01 | 2012-12-31
04-Jul-2013 | 2013-07-04 | 2013-07-04
16-Jan-2011 | 2011-01-16 | 2011-01-16
Jan-2011 | 2011-01-01 | 2011-01-31
2001 | 2001-01-01 | 2001-12-31
Nov-2005 | 2005-11-01 | 2005-11-30
因为 PostgreSQL 没有 last_day 函数,所以必须定制(来源:https://www.postgresql.org/message-id/Pine.LNX.4.44.0309021522180.17073-100000%40kix.fsv.cvut.cz)。
Edit1 - 更新当前答案以匹配最近的问题编辑。