如何使用一个SQL查询从多个日期范围中提取数据?

时间:2016-10-11 19:39:48

标签: sql postgresql

我有两个问题。每个查询都会提取组织和客户之间的订单总数,以及订单的应收款总和。除日期范围外,查询相同。

SELECT org.organization_id, org.name, cust.name as customer,
count(*) as num_orders, round (sum(cast(o.total_charge as real))) as  receivables
FROM 
organization as org, orders as o, organization as cust, reconcile_order as ro
WHERE org.organization_id = o.shipper_org_id
and o.broker_org_id = cust.organization_id
and o.order_id = ro.order_id
and o.status = 'D'
and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
and (NOW()::DATE - o.delivery_confirmed_date::DATE) < 31
group by org.organization_id, org.name,
cust.name
order by org.name asc limit 20

SELECT org.organization_id, org.name, cust.name as customer,
count(*) as num_orders, round (sum(cast(o.total_charge as real))) as   receivables
FROM 
organization as org, orders as o, organization as cust, reconcile_order as ro
WHERE org.organization_id = o.shipper_org_id
and o.broker_org_id = cust.organization_id
and o.order_id = ro.order_id
and o.status = 'D'
and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
and (NOW()::DATE - o.delivery_confirmed_date::DATE) between 31 and 60
group by org.organization_id, org.name,
cust.name
order by org.name asc limit 20

但我需要进行一次查询,以便输出是一个表,其中第一个日期范围内的订单和应收款列,以及第二个日期范围的另一对列旁边的列。 (即num_orders&lt; 31,应收账款&lt; 31,num_orders 31-60,应收账款31-60)

3 个答案:

答案 0 :(得分:1)

您可以将条件语句放在count()和sum()函数中。

因此,如果您调整了where子句以恢复所有订单(跨越两个日期范围),那么您可以在select子句中创建多个结果列,每个列都只根据您想要的日期范围进行计数和求和。

SELECT ...
       count(CASE WHEN (NOW()::DATE - o.delivery_confirmed_date::DATE) < 31 THEN 1 ELSE NULL END) as num_orders_a,
       round(sum(CASE WHEN (NOW()::DATE - o.delivery_confirmed_date::DATE) < 31 THEN cast(o.total_charge as real) ELSE NULL END)) as receivables_a,
       count(CASE WHEN (NOW()::DATE - o.delivery_confirmed_date::DATE) BETWEEN 31 AND 60 THEN 1 ELSE NULL END) as num_orders_b,
       round(sum(CASE WHEN (NOW()::DATE - o.delivery_confirmed_date::DATE) BETWEEN 31 AND 60 THEN cast(o.total_charge as real) ELSE NULL END)) as receivables_b
(same FROM, WHERE, GROUP BY, and ORDER BY sections)

答案 1 :(得分:1)

有很多方法可以让这只猫受到影响,并且在性能和代码可维护性之间存在着真正的潜在折衷。

这里的CTE将有助于代码可读性/透明度/可维护性。这有点像黑客的做法,但这是一个想法:

with order_data as (
  SELECT
    org.organization_id, org.name, cust.name as customer,
    o.total_charge::real,
    case
      when current_date - o.delivery_confirmed_date::DATE < 31 then 1
      when current_date - o.delivery_confirmed_date::date < 61 then 2
      else 3
    end as cat
  FROM 
    organization as org, 
    orders as o, 
    organization as cust, 
    reconcile_order as ro
  WHERE
    org.organization_id = o.shipper_org_id
    and o.broker_org_id = cust.organization_id
    and o.order_id = ro.order_id
    and o.status = 'D'
    and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
)
select
  organization_id, name, customer,
  sum (case when cat = 1 then 1 else 0 end) as "Orders < 31",
  round (sum (case when cat = 1 then total_charge else 0 end)) as "Rec < 31",
  sum (case when cat = 2 then 1 else 0 end) as "Orders 31-60",
  round (sum (case when cat = 2 then total_charge else 0 end)) as "Rec 31-60",
  sum (case when cat = 3 then 1 else 0 end) as "Orders 61+",
  round (sum (case when cat = 3 then total_charge else 0 end)) as "Rec 61+"
from order_data
group by
  organization_id, name, name
order by name asc

我认为更常见的方法可能是通过&#34; days_delta&#34;来自CTE的列(作为current_date - o.delivery_confirmed_date::DATE)并使您的求和函数看起来更像这样:

sum (case when days_delta between 31 and 60 then ...  end) as "31-60"

而且......任何说你不需要CTE的人 - 他们都是对的。你没有。对我来说,它只是使代码更加愉快。

- 编辑 -

CTE中较不吸引人(且功能较少)的表亲,子查询:

select
  organization_id, name, customer,
  sum (case when cat = 1 then 1 else 0 end) as "Orders < 31",
  round (sum (case when cat = 1 then total_charge else 0 end)) as "Rec < 31",
  sum (case when cat = 2 then 1 else 0 end) as "Orders 31-60",
  round (sum (case when cat = 2 then total_charge else 0 end)) as "Rec 31-60",
  sum (case when cat = 3 then 1 else 0 end) as "Orders 61+",
  round (sum (case when cat = 3 then total_charge else 0 end)) as "Rec 61+"
from (
  SELECT
    org.organization_id, org.name, cust.name as customer,
    o.total_charge::real,
    case
      when current_date - o.delivery_confirmed_date::DATE < 31 then 1
      when current_date - o.delivery_confirmed_date::date < 61 then 2
      else 3
    end as cat
  FROM 
    organization as org, 
    orders as o, 
    organization as cust, 
    reconcile_order as ro
  WHERE
    org.organization_id = o.shipper_org_id
    and o.broker_org_id = cust.organization_id
    and o.order_id = ro.order_id
    and o.status = 'D'
    and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
  ) as order_data
group by
  organization_id, name, name
order by name asc

答案 2 :(得分:0)

我不确定我理解你的确切问题,但是如何:

Select earlier_ones.organization_id,earlier_ones.organization_id, name, customer, earlier_ones.receivables, later_ones.receivables
FROM (
  SELECT org.organization_id, org.name, cust.name as customer,
  count(*) as num_orders, round (sum(cast(o.total_charge as real))) as  receivables
  FROM
  organization as org, orders as o, organization as cust, reconcile_order as ro
  WHERE org.organization_id = o.shipper_org_id
  and o.broker_org_id = cust.organization_id
  and o.order_id = ro.order_id
  and o.status = 'D'
  and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
  and (NOW()::DATE - o.delivery_confirmed_date::DATE) < 31
  group by org.organization_id, org.name,
  cust.name
  order by org.name asc limit 20
) earlier_ones
LEFT JOIN (
  SELECT org.organization_id, org.name, cust.name as customer,
  count(*) as num_orders, round (sum(cast(o.total_charge as real))) as   receivables
  FROM
  organization as org, orders as o, organization as cust, reconcile_order as ro
  WHERE org.organization_id = o.shipper_org_id
  and o.broker_org_id = cust.organization_id
  and o.order_id = ro.order_id
  and o.status = 'D'
  and (ro.receive_payment_in_full = 0 or ro.receive_payment_in_full is NULL)
  and (NOW()::DATE - o.delivery_confirmed_date::DATE) between 31 and 60
  group by org.organization_id, org.name,
  cust.name
  order by org.name asc limit 20
) later_ones ON earlier_ones.organization_id = later_ones.organization_id AND earlier_ones.name = later_ones.name;