来自多个表PostgreSQL的每月总计数

时间:2014-08-14 16:13:41

标签: sql postgresql

我有一个搜索设置,它给出了新患者就诊总数和患者就诊总数,并将所需年份的总数与上一年的总数进行了比较。

SQL从表patient_info中查询日期字段firstexam和lastexam。

我发现有些用户不会在每次患者就诊时更新lastexam,因此lastexam不会给出患者就诊的总数。

通过搜索交易表可以获得患者就诊总数。交易表中的发票用列transstype标记为' Inv'。因此,患者就诊总数将是日期范围内的发票总数(考虑到在一天内为患者输入的两张发票计为一次访问)。

以下是基于firstexam和lastexam设置的SQL查询的代码。

我一直在努力解决这个问题已经有一段时间了。任何帮助将不胜感激。

select
        to_char(('2012-' || m || '-01')::date, 'Month'),
        thisyear, lastyear, totalthisyear, totallastyear
    from (
        select
            extract(month from m) as m,
            sum(case
                when firstexam between '2013-01-01' and '2013-12-31' then firstexam_count
                else 0 end
            ) as thisyear,
            sum(case
                when firstexam between '2012-01-01' and '2012-12-31' then firstexam_count
                else 0 end
            ) as lastyear,
            sum(case
                when lastexam between '2013-01-01' and '2013-12-31' then lastexam_count
                else 0 end
            ) as totalthisyear,
            sum(case
                when lastexam between '2012-01-01' and '2012-12-31' then lastexam_count
                else 0 end
            ) as totallastyear
        from
            generate_series (
                '2012-01-01'::date, '2013-12-31', '1 month'
            ) g(m)
            left join (
                select count(*) as firstexam_count, date_trunc('month', firstexam) as firstexam
                from patient_info
                where firstexam between '2012-01-01' and '2013-12-31'
                group by 2
            ) pif on firstexam = m
            left join (
                select count(*) as lastexam_count, date_trunc('month', lastexam) as lastexam
                from patient_info
                where lastexam between '2012-01-01' and '2013-12-31'
                group by 2
            ) pil on lastexam = m
        group by 1
    ) s
    order by m

1 个答案:

答案 0 :(得分:1)

  1. 如果您想报告有关考试的信息,您应该存储有关考试的信息。
  2. 更具体地说,如果您想计算考试,您应该存储有关每个考试的信息。
  3. 请勿使用“thisyear”和“lastyear”等列名。今年不是2013年,虽然你就是这样展示的。
  4. 通常,访问考试是不同的事情。术语要小心。 (这里不是什么大问题,因为我们没有关于访问考试的信息。只有关于发票。但是,这是一个好习惯。)
  5. 如果您担心特定的输出格式,请问问自己是在构建查询还是报告。在SQL中构建查询。使用报告编写器或应用程序代码构建报告。
  6. 为简单起见,我要去

    • 忽略“patient_info”表格,
    • 忽略您需要的外部联接,以便在没有考试的几个月内生成零,并且
    • 使用公用表表达式。 (在生产中,我宁愿使用视图而不是常用的表表达式。)

    让我们从一个交易表开始吧。

    create table transactions (
      ptnumber INT,
      dateofservice date,
      transtype varchar(3)
    );
    
    -- Not quite the same data you started with.
    insert into transactions (ptnumber, dateofservice, transtype) 
    values
    (1, '2012-01-01', 'Inv'),
    (1, '2012-02-11', 'Inv'),
    (2, '2012-01-02', 'Inv'),
    (3, '2013-01-01', 'Inv'),
    (4, '2013-02-12', 'Inv'),
    (5, '2012-12-31', 'Inv'),
    (5, '2013-12-31', 'Inv'),
    (5, '2013-12-31', 'Inv'),
    (6, '2013-06-21', 'Inv');
    

    您说“在一天内为患者输入的两张发票算作一个[检查]”。我猜这意味着两个或更多。因此,我们可以像这样提取一组患者检查。对于患者5,我预计有两排 - 一个在2012年,一个在2013年。

    select distinct ptnumber, dateofservice
    from transactions
    where transtype = 'Inv' 
      and dateofservice between '2012-01-01' and '2013-12-31'
    order by ptnumber;
    
    ptnumber  dateofservice
    --
    1         2012-01-01
    1         2012-02-11
    2         2012-01-02
    3         2013-01-01
    4         2013-02-12
    5         2012-12-31
    5         2013-12-31
    6         2013-06-21
    

    这是您整个问题的关键 - 在规定的日期范围内进行一系列不同的患者检查。根据这一组,按月计算患者就诊是直截了当的。 (按照哪种方式计算它们很简单。)

    with patient_exams as (
      select distinct ptnumber, dateofservice
      from transactions
      where transtype = 'Inv' 
        and dateofservice between '2012-01-01' and '2013-12-31'
    )
    select to_char(dateofservice, 'YYYY-MM') as month_of_service, count(*) as num_patient_exams
    from patient_exams
    group by 1
    order by 1;
    
    month_of_service    num_patient_visits
    --
    2012-01             2
    2012-02             1
    2012-12             1
    2013-01             1
    2013-02             1
    2013-06             1
    2013-12             1
    

    首次考试

    再次,从派生一个能给你可靠计数的集合开始。您希望每位患者一行,并且您想要最早的发票日期。患者第一次检查的日期与您要报告的日期范围无关;包含此查询的日期范围的WHERE子句将为您提供错误的数据。

    select ptnumber, min(dateofservice) as first_exam_date
    from transactions
    where transtype = 'Inv'
    group by ptnumber
    order by ptnumber;
    
    ptnumber  first_exam_date
    --
    1         2012-01-01
    2         2012-01-02
    3         2013-01-01
    4         2013-02-12
    5         2012-12-31
    6         2013-06-21
    

    现在计算每个月你获得的新患者数量是多么简单。

    with first_exams as (
      select ptnumber, min(dateofservice) as first_exam_date
      from transactions
      where transtype = 'Inv'
      group by ptnumber
    )
    select to_char(first_exam_date, 'YYYY-MM') exam_month, count(*) num_first_exams
    from first_exams
    where first_exam_date between '2012-01-01' and '2013-12-31'
    group by 1
    order by 1;
    
    exam_month  num_first_exams
    --
    2012-01     2
    2012-12     1
    2013-01     1
    2013-02     1
    2013-06     1