用于计算不同行状态的分析函数

Posted

技术标签:

【中文标题】用于计算不同行状态的分析函数【英文标题】:Analytic function to compute statuses of different rows 【发布时间】:2015-02-23 07:07:38 【问题描述】:

我正在使用 Oracle 10g,我想计算表 tmp_ord 中包含的行的不同状态。它包含以下数据。

下面有一组标记状态的规则

    应该至少有一个状态为'ABC'的order_position,并且不应该有状态为'NON-ABC'的order_position具有相同的对应order_id,然后将状态标记为'ABC'。 应至少有一个状态为'NON-ABC'的order_position,并且不应有相应状态为'ABC'的order_position,然后将状态标记为'NON-ABC'。 如果对于相同的 ORDER_ID,至少有一个状态为“ABC”的 order_position 和至少一个状态为“NON-ABC”的 order_position,则将状态标记为“AMB”。 如果对应的 ORDER_ID 只有一个状态为 NULL 的 order_position,则将状态标记为“未知”。 假设 ABC 和 NULL 存在,那么它应该返回 ABC,如果 NOn-ABC 和 NULL 存在,那么它应该返回 'NON-ABC'......但是如果只存在单个 NULL 值,那么它应该返回 'UNKNOWN' 但是如果有 ALL'NULL' 值与 ORDER_ID UNIFORMLY 相关联,那么它应该返回 'NULL'

我写了下面的查询来计算数据,但得到一个对应于 ORDER_ID=6 的 NULL 值。

SELECT  distinct ORDER_ID,CASE WHEN R1=0 THEN 'UNKNOWN'
                              WHEN R1=1 AND ORDER_STATUS='ABC'  THEN 'ABC'
                              WHEN R1=1 AND ORDER_STATUS<>'ABC' THEN 'NON-ABC'
                              WHEN R1>1                         THEN 'MD'
                              END STATUS
                     ,CASE WHEN R2=0 THEN 'UNKNOWN'
                          WHEN R2=1 AND ORDER_STATUS_1='ABC'  THEN 'ABC'
                          WHEN R2=1 AND ORDER_STATUS_1<>'ABC' THEN 'NON-ABC'
                          WHEN R2>1                         THEN 'MD'
                          END STATUS_1
FROM
(
SELECT  ORDER_ID,ORDER_STATUS,ORDER_STATUS_1,
 COUNT(DISTINCT order_status) OVER (PARTITION BY ORDER_ID) R1 
,COUNT(DISTINCT order_status_1) OVER (PARTITION BY ORDER_ID) R2
FROM TMP_ORD
ORDER BY ORDER_ID
);

我低于输出。请让我知道如何抑制与 ORDER_ID=6 关联的行,它给出 STATUS=NULL... 它应该是“ABC”,因为它至少包含一个“ABC”并且没有“NON-ABC”的状态。

【问题讨论】:

我写了下面的查询来计算数据,但得到一个对应于 ORDER_ID=6 的 NULL 值。 【参考方案1】:

我正在尝试它,但不知道上下文,尤其是“ABC”和“NON-ABC”真正代表什么,很难做到正确。

您说 6 应该返回“ABC” - 在您的特定情况下,将“NULL”视为等同于非“NON-ABC”是否可以接受? 因此“NULL”=“ABC”?如果这对您来说没问题,请尝试使用 NVL(STATUS, "ABC") 并且应该可以。

但这需要对数据的含义进行一些相当大的假设。订单是否有可能所有行都为 STATUS=NULL? 如果是,你的函数应该返回什么? ABC?


或者,您可以尝试更严格地定义“NON-ABC”尝试替换:

 WHEN ... ORDER_STATUS<>'ABC' THEN 'NON-ABC'

 WHEN ... (ORDER_STATUS<>'ABC'AND ORDER_STATUS IS NOT NULL) THEN 'NON-ABC'

看看你会得到什么

【讨论】:

由于机密性,我无法提供准确的数据,因此我创建了一个示例。假设 ABC 和 NULL 存在那么它应该返回 ABC,如果 NOn-ABC 和 NULL 存在那么它应该返回 'NON-ABC' ......但是如果只有单个 NULL 值存在那么它应该返回'UNKNOWN' 但是,如果有 ALL 'NULL' 值与 ORDER_ID UNIFORMLY 关联,那么它应该返回 'NULL' 我尝试过使用 NVL,但由于条件 NO.4 将其标记为某个值,因此我没有得到 R 或 R1=0 的 ORDER_ID,它只包含单行并且太NULL 请查看上面的cmets 所以您在原始问题中遗漏了几条规则,即如何管理 NULL。请更新问题 - 然后我们可能会尝试使用 EXISTS 来正确管理此问题【参考方案2】:

第 5 点有点不清楚(对我来说),但这个查询应该可以完成工作:

with o0 as (
  select order_id,
    sum(decode (order_status, 'ABC', 1, 0)) s0_abc,
    sum(decode (order_status, 'ABC', 0, null, 0, 1)) s0_xyz,
    sum(decode (order_status, NULL, 1, 0)) s0_null,
    sum(case when order_status is null and order_position is null 
      then 1 else 0 end) s0_null_op
  from tmp_ord group by order_id),
o1 as (
  select order_id,
    sum(decode (order_status_1, 'ABC', 1, 0)) s1_abc,
    sum(decode (order_status_1, 'ABC', 0, null, 0, 1)) s1_xyz,
    sum(decode (order_status_1, NULL, 1, 0)) s1_null,
    sum(case when order_status_1 is null and order_position is null 
      then 1 else 0 end) s1_null_op
  from tmp_ord group by order_id)
select order_id,
    case 
      when s0_abc > 0 and s0_xyz = 0 then 'ABC'
      when s0_abc = 0 and s0_xyz > 0 then 'NON-ABC'
      when s0_abc > 0 and s0_xyz > 0 then 'AMB'
      when s0_null_op = s0_null then null
      else 'UNKNOWN'
    end status,
    case 
      when s1_abc > 0 and s1_xyz = 0 then 'ABC'
      when s1_abc = 0 and s1_xyz > 0 then 'NON-ABC'
      when s1_abc > 0 and s1_xyz > 0 then 'AMB'
      when s1_null_op = s1_null then null
      else 'UNKNOWN'
    end status_1
  from o0 join o1 using (order_id)
  order by order_id

结果:

  ORDER_ID STATUS  STATUS_1
---------- ------- --------
         1 AMB     ABC
         2 AMB     AMB
         3 AMB     AMB
         4 NON-ABC NON-ABC
         5 ABC     ABC
         6 ABC     ABC
         7 UNKNOWN UNKNOWN

7 rows selected

【讨论】:

感谢您的努力【参考方案3】:

很抱歉误导了你们。 如果您清楚地阅读了我的所有规则,它已经提到要对“ORDER_STATUS”和“ORDER_ID”进行计数。 在我的查询(我在一个问题中发布)中,我计算了关于“ORDER_ID”的 ORDER_STATUS。 请在下面找到解决方案。

SELECT ORDER_ID
,CASE 
WHEN ABC_STATUS_CNT  = 0    AND NON_ABC_STATUS_CNT=0  AND TOTAL_COUNT=1 THEN 'UNKNOWN'
WHEN ABC_STATUS_CNT >= 1    AND NON_ABC_STATUS_CNT=0                    THEN 'ABC'
WHEN ABC_STATUS_CNT  = 0    AND NON_ABC_STATUS_CNT>=1                   THEN 'NON_ABC'
WHEN ABC_STATUS_CNT >= 1    AND NON_ABC_STATUS_CNT>=1                   THEN 'AMB'
END STATUS
,CASE 
WHEN ABC_STATUS_CNT_1  = 0  AND NON_ABC_STATUS_CNT_1=0 AND TOTAL_COUNT=1 THEN 'UNKNOWN'
WHEN ABC_STATUS_CNT_1 >= 1  AND NON_ABC_STATUS_CNT_1=0                   THEN 'ABC'
WHEN ABC_STATUS_CNT_1  = 0  AND NON_ABC_STATUS_CNT_1>=1                  THEN 'NON_ABC'
WHEN ABC_STATUS_CNT_1 >= 1  AND NON_ABC_STATUS_CNT_1>=1                  THEN 'AMB'
END STATUS_1
FROM
(
SELECT ORDER_ID,COUNT(CASE WHEN ORDER_STATUS   =  'ABC' THEN ORDER_POSITION ELSE NULL END) ABC_STATUS_CNT
,COUNT(CASE WHEN ORDER_STATUS   <> 'ABC' THEN ORDER_POSITION ELSE NULL END) NON_ABC_STATUS_CNT
,COUNT(CASE WHEN ORDER_STATUS_1 =  'ABC' THEN ORDER_POSITION ELSE NULL END) ABC_STATUS_CNT_1
,COUNT(CASE WHEN ORDER_STATUS_1 <> 'ABC' THEN ORDER_POSITION ELSE NULL END) NON_ABC_STATUS_CNT_1
,COUNT(ORDER_POSITION) AS TOTAL_COUNT
FROM TMP_ORD_BNG2A
GROUP BY ORDER_ID
);   

【讨论】:

请将您的答案标记为正确以关闭此主题:-)

以上是关于用于计算不同行状态的分析函数的主要内容,如果未能解决你的问题,请参考以下文章

oracle 分析函数总结--仅供参考

oracle分析函数

开窗函数是啥?

hive函数之~窗口函数与分析函数

分析函数和开窗函数

Oracle分析函数入门