工会与案例或其他替代方案

Posted

技术标签:

【中文标题】工会与案例或其他替代方案【英文标题】: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

【问题讨论】:

【参考方案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 的关系。不过,我喜欢这个简单的想法!

以上是关于工会与案例或其他替代方案的主要内容,如果未能解决你的问题,请参考以下文章

在Vim中保留替代案例

有哪些不同类型的案例?

关于路由交换设备接头物理层或协议层无法UP及其他情况案例分析

隐式转换的替代方案不是万能的

PayPal Adaptive Payments 和 Stripe 的更便宜的替代品

ArcGIS实验教程——实验四十三:ArcGIS栅格重分类(Reclass)案例详解