【问题标题】:Union versus Case or other alternative工会与案例或其他替代方案
【发布时间】:2021-03-25 21:52:54
【问题描述】:

我有 2 个表,加入时会告诉我与特定产品关联的组织。每个组织都有一个状态,“活跃”或“传统”。我试图只返回“活动”组织,除非没有“活动”组织,然后我想要“传统”组织。如果没有联合,我无法弄清楚如何解决这个问题,我不想使用联合,因为我的一些查询很长而且很复杂。

我当前的方法是两个子查询:一个用于活动的,一个用于旧的,然后在 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 以外的任何东西,我会得到下面的输出,但是当还有一个 Active org 时,我不想要 Legacy org。

|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

【问题讨论】:

    标签: sql oracle case union


    【解决方案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 行。我使用了你的 liveSQL 示例(语句 34),我可以得到完全相同的输出,但目标是消除 RN 2. 从您上面的查询中,我看到您在 from 语句中是如何做到的,但是当您像 liveSQL 中的语句一样纠正它时,我不知道您是如何做到的(这真的很好。)
    • @ShandaFin - 在我发布的答案中,我使用谓词 where rn = 1 将整个内容包裹在一个内联视图中。看起来我在运行那个版本的查询之前保存了我的 LiveSQL 会话,我只是把它放在我的答案中。
    【解决方案2】:

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

    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;
    

    【讨论】:

    • 我能够让它运行,但我仍然得到 4 行,而不是 3 行。实际上我的查询中涉及第三个表 - 一个连接产品和组织的表。我简化了问题中的表格,但也许这就是问题所在?这是我现在拥有的:select p.product_name, o.org_name, o.status from products p, product_pairs pp left join lateral (select o.* from organizations o where pp.ogn_id_established_to = o.ID order by o.STATUS fetch first 1 row only) o on 1=1 WHERE p.ID = pp.prod_id
    【解决方案3】:

    你应该可以做这样的事情:

    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 关系。

    【讨论】:

    • 今日提示:切换到现代、明确的JOIN 语法。更容易编写(没有错误),更容易阅读(和维护),并且在需要时更容易转换为外连接。
    • 这就是问题所在。我的一些产品与组织之间存在 1:2 的关系。不过,我喜欢这个简单的想法!
    猜你喜欢
    • 2011-07-24
    • 1970-01-01
    • 2014-07-08
    • 1970-01-01
    • 2011-09-01
    • 2012-12-27
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    相关资源
    最近更新 更多