代码之家  ›  专栏  ›  技术社区  ›  Ghoti

可变日期格式查询

  •  1
  • Ghoti  · 技术社区  · 7 年前

    我正在尝试将从PostgreSQL表(使用索引聚合数组)提取的字符串转换为格式正确的日期进行查询。我的问题是,我的日期格式不同,包括YYYY、Mon YYYY和DD Mon YYYY。我的计划是创建日期范围,以包含由不明确日期表示的所有可能时间。例如,“2000”将转换为“2000年1月1日”和“2000年12月31日”,并根据自定义输入日期范围进行测试。同样,“2014年2月”将改为“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”来处理的)。相关部分(最底行)归结为选择以字符串格式表示时间的索引聚合。我的搜索条件有意筛选出不符合所用连接方法的日期(不包括Mon-YYYY和DD-Mon-YYYY日期)。我一直在尝试合并一个“CASE”条件,但我不确定是否/如何将其适应现有查询。

    问题的简化

    我需要修改以下查询:

    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\u agg(e.value))[3]索引选择日期字段。接下来,我需要修改日期字符串并将其插入到单独的输出列中。我认为应该是这样的(当前不起作用):

    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;
    

    我正试图重现此堆栈帖子中答案的查询结构: IF-THEN-ELSE statements in postgresql

    编辑1 -已经有好几个月了,我想我至少在SQL方面有点能力。也就是说,我真的对这个旧的解决方案不满意。任何其他建议或解决方案都会有所帮助。

    演示包含时间信息的表格:

    specimen_collection_date 
    --------------------------
     01-Nov-2013
     2013
     2012
     04-Jul-2013
     16-Jan-2011
     Jan-2011
     2001
     Nov-2005
    
    1 回复  |  直到 6 年前
        1
  •  0
  •   Ghoti    6 年前

    简化问题答案

    代码:

    create or replace function last_day(date) returns date as 'select 
    cast(date_trunc(''month'', $1) + ''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\u day函数,因此必须进行定制(来源: https://www.postgresql.org/message-id/Pine.LNX.4.44.0309021522180.17073-100000%40kix.fsv.cvut.cz ).

    Edit1-更新当前答案以匹配最近的问题编辑。