Union vs Case 或其他替代方案

时间:2021-03-25 21:52:54

标签: sql oracle case union

我有 2 个表,加入后可以告诉我与特定产品相关联的组织。每个组织都有一个状态,“Active”或“Legacy”。我试图只返回“活跃”组织,除非没有“活跃”组织,然后我想要“传统”组织。我不知道如何在没有联合的情况下解决这个问题,我不想使用联合,因为我的一些查询很长而且很复杂。

我目前的方法是两个子查询:一个用于活动,一个用于遗留,然后在 Excel 中手动组合它们。这是低效和烦人的。我希望 CASE 语句可能会消除 UNION,但到目前为止我没有运气。这是问题的简化图:

组织表

|ORG_NAME      |ORG_STATUS|PRODUCT_ID|
|--------------|----------|----------|
|Organization 1|Active    |1         |
|Organization 2|Legacy    |1         |
|Organization 3|Legacy    |2         |
|Organization 4|Active    |3         |

产品表

|ID|PRODUCT_NAME|
|--|------------|
|1 |Product 1   |
|2 |Product 2   |
|3 |Product 3   |

所需的输出:(我确实通过 UNION 得到了它,但我希望有一个不那么麻烦的解决方案。)

|PRODUCT_NAME|ORG_NAME      |ORG_STATUS|
|------------|--------------|----------|
|Product 1   |Organization 1|Active    |
|Product 2   |Organization 3|Legacy    |
|Product 3   |Organization 4|Active    |

如果我使用除 UNION 以外的任何东西,我会得到以下作为我的输出,但是当还有一个活动组织时,我不想要旧组织。

|PRODUCT_NAME|ORG_NAME      |ORG_STATUS|
|------------|--------------|----------|
|Product 1   |Organization 1|Active    |
|Product 1   |Organization 2|Legacy    |
|Product 2   |Organization 3|Legacy    |
|Product 3   |Organization 4|Active    |

我觉得应该有一种简单的方法来根据指定的标准选择填充哪个组织,但我无法找出正确的标准来使其工作。任何帮助将非常感激!提前致谢!

这是简化的联合查询:

    SELECT PROD.PRODUCT_NAME, ORG.ORG_NAME
      FROM products prod, organizations org
     WHERE PROD.ID = ORG.PRODUCT_ID AND ORG.STATUS = 'Active'
    UNION
    SELECT PROD.PRODUCT_NAME, ORG.ORG_NAME
      FROM products prod, organizations org
     WHERE PROD.ID = ORG.PRODUCT_ID AND ORG.STATUS = 'Legacy'
           AND ORG.PRODUCT_ID NOT IN (SELECT PROD.ID
                                        FROM products prod, organizations org
                                       WHERE ORG.STATUS = 'Active')

我在 select 和 where 语句中都尝试了 CASE,但无法让它工作。下面是其中一种尝试,但这并没有考虑组织和产品之间的连接,只是给了我所有的组合。我尝试连接 org 和 product 来给我一个单一的变量来处理,但这也没有帮助。也许做不到?

WHEN ORG.STATUS = 'Active' THEN ORG.ORG_NAME
ELSE ORG.ORG_NAME
END

3 个答案:

答案 0 :(得分:1)

您可以使用分析函数 row_number 来确定“最佳”行,然后仅选择该行。

select *
  from (
select o.org_name, o.org_status, p.product_id, p.product_name,
       row_number() over (partition by p.product_id 
                              order by case when o.org_status = 'Active'
                                            then 1
                                            else 2
                                        end asc) rn
  from products p
       join organizations o 
         on p.product_id = o.product_id
) 
 where rn = 1;

这是一个example from liveSQL

答案 1 :(得分:0)

你应该能够做这样的事情:

SELECT PROD.PRODUCT_NAME, ORG.ORG_NAME, MIN(ORG.STATUS) as OrgStatus
FROM   products prod, organizations org
WHERE  PROD.ID = ORG.PRODUCT_ID
GROUP BY PROD.PRODUCT_NAME, ORG.ORG_NAME

这当然依赖于 Active 在 Legacy 之前排序的事实...... 这还假设产品和组织之间存在 1:1 关系。

答案 2 :(得分:0)

这是横向连接的一个很好的用例:

select p.*, o.org_name, o.order_status
from products p left join lateral
     (select o.*
      from organizations o
      where o.product_name = p.product_name
      order by o.order_status   -- "Active" before "Legacy"
      fetch first 1 row only
     ) o
     on 1=1;