基于SQL的FIFO逻辑

时间:2012-07-26 15:15:22

标签: sql oracle oracle10g

我需要构建一个查询,该查询将汇总已输入的血液单位数,以便将其与已交叉匹配的血液单位进行比较。血液(一种宝贵的资源)是交叉匹配但不输注的浪费。

提供商应该在创建新的交叉匹配订单之前检查系统记录(Epic)。不这样做的提供者是“受到惩罚的”(提供者20)。对于那些不会输入他们交叉匹配的所有血液的提供者(提供者10),不会受到惩罚(似乎)。

交叉匹配订单:

|ENC_ID|PROV_ID|ORDER_ID|ORDER_TIME     |UNITS|
|     1|     10|     100|26-JUL-12 13:00|    4|
|     1|     20|     231|26-JUL-12 15:00|    2|

输血订单:

|ENC_ID|PROV_ID|ORDER_ID|ORDER_TIME     |UNITS|
|     1|     10|     500|26-JUL-12 13:05|    1|
|     1|     10|     501|26-JUL-12 13:25|    1|
|     1|     20|     501|26-JUL-12 15:00|    1|
|     1|     20|     501|26-JUL-12 15:21|    2|

规则:

  • 将输血与同一次遭遇的交叉匹配进行比较(transfusion.END_ID=cross-match.ENC_ID
  • 输血订单以FIFO方式应用于交叉匹配订单
  • transfusion.ORDER_TIME >= cross-match.ORDER_TIME
  • 只要所有“有效”交叉匹配订单仍有可用单位(提供商20的第二次输血订单),提供商可以输入超过其交叉配货订单

期望的结果:

|ENC_ID|PROV_ID|ORDER_ID|ORDER_TIME     |CROSS-MATCHED|TRANSFUSED|
|     1|     10|     100|26-JUL-12 13:00|            4|         4|
|     1|     20|     231|26-JUL-12 15:00|            2|         1|

提供者10'在提供者20的输入中“记入”。

这种逻辑可以在不诉诸程序的情况下实施吗?

1 个答案:

答案 0 :(得分:3)

您可以在单个SQL查询中执行此操作。这是一个例子(在11gR2上测试,应该在10g上工作):

SETUP:

CREATE TABLE cross_match as (
   SELECT 1 ENC_ID, 10 PROV_ID, 100 ORDER_ID, 
          to_date('2012-07-26 13', 'yyyy-mm-dd hh24') ORDER_TIME, 4 UNITS 
     FROM DUAL
   UNION ALL SELECT 1, 20, 231, to_date('2012-07-26 15', 'yyyy-mm-dd hh24'), 2 FROM DUAL
);
CREATE TABLE transfusion as (
   SELECT 1 ENC_ID, 10 PROV_ID, 500 ORDER_ID, 
          to_date('2012-07-26 13:05', 'yyyy-mm-dd hh24:mi') ORDER_TIME, 1 UNITS 
          FROM DUAL
   UNION ALL SELECT 1, 10, 501, to_date('2012-07-26 13:25', 'yyyy-mm-dd hh24:mi'), 1 FROM DUAL
   UNION ALL SELECT 1, 20, 501, to_date('2012-07-26 15:00', 'yyyy-mm-dd hh24:mi'), 1 FROM DUAL
   UNION ALL SELECT 1, 20, 501, to_date('2012-07-26 15:21', 'yyyy-mm-dd hh24:mi'), 2 FROM DUAL
);  

以下查询将以数字方式构建血液单位列表,并将cross_match表中的每个单位连接到transfusion表中相应的单位(如果存在):

WITH cross_order as (
   SELECT rownum rn FROM DUAL
   CONNECT BY level <= (SELECT MAX(units) FROM cross_match)
),
transfusion_order as (
   SELECT rownum rn FROM DUAL
   CONNECT BY level <= (SELECT MAX(units) FROM transfusion)
)
SELECT c.enc_id, c.prov_id, c.order_id, c.order_time, 
       count(*) cross_matched,
       count(t.enc_id) transfused
  FROM (SELECT cm.*, 
               row_number() over (partition by cm.enc_id 
                                  order by cm.order_time) cross_no
          FROM cross_match cm
          JOIN cross_order co ON cm.units >= co.rn) c
  LEFT JOIN (SELECT t.*, 
                    row_number() over (partition by t.enc_id 
                                       order by t.order_time) trans_no
               FROM transfusion t
               JOIN transfusion_order tor ON t.units >= tor.rn) t
         ON c.enc_id = t.enc_id
            AND c.cross_no = t.trans_no
 GROUP BY c.enc_id, c.prov_id, c.order_id, c.order_time;

ENC_ID PROV_ID ORDER_ID ORDER_TIME CROSS_MATCHED TRANSFUSED
-----------------------------------------------------------
1      20      231      07/26/2012             2          1
1      10      100      07/26/2012             4          4

如果最大单位数仍然很小,这可能是有效的,否则这种一对一的关系可能会变得很麻烦。

这可以通过在两侧使用总计单位而不是基本1-1来改进。连接条件类似于开始单元和结束单元之间的间隔交叉:

SELECT c.enc_id, c.prov_id, c.order_id, c.order_time, 
       sum(c.unit_end - nvl(c.unit_start,0))/count(*) cross_matched,
       sum(least(c.unit_end, t.unit_end)
           -greatest(nvl(c.unit_start, 0), nvl(t.unit_start, 0))) transfused
  FROM (SELECT cm.*, 
               sum(cm.units) over (partition by cm.enc_id 
                                  order by cm.order_time
                                  rows between unbounded preceding
                                           and 1 preceding) unit_start,
               sum(cm.units) over (partition by cm.enc_id 
                                  order by cm.order_time) unit_end
          FROM cross_match cm) c
  LEFT JOIN (SELECT t.*,
               sum(t.units) over (partition by t.enc_id 
                                  order by t.order_time
                                  rows between unbounded preceding
                                           and 1 preceding) unit_start, 
                    sum(t.units) over (partition by t.enc_id 
                                       order by t.order_time) unit_end
               FROM transfusion t) t
         ON c.enc_id = t.enc_id
            AND c.unit_end > nvl(t.unit_start, 0)
            AND t.unit_end > nvl(c.unit_start, 0)
 GROUP BY c.enc_id, c.prov_id, c.order_id, c.order_time;

ENC_ID PROV_ID ORDER_ID ORDER_TIME CROSS_MATCHED TRANSFUSED
-----------------------------------------------------------
1      20      231      07/26/2012             2          1
1      10      100      07/26/2012             4          4
相关问题