【问题标题】:Oracle SQL: select from a set of orders only those that contain certain typesOracle SQL:从一组订单中仅选择包含某些类型的订单
【发布时间】:2021-04-05 04:23:48
【问题描述】:

我想从一组订单中只选择标有特定类型的订单。

例子:

假设我有一张桌子 X。

Order   Type
123     A
123     B
123     C
234     A
234     C
345     B
456     C
456     A
567     A
567     B

就我而言,我只想选择那些同时包含 A 和 B 的订单:

Order   Type
123     A
123     B
123     C
567     A
567     B

到目前为止我的工作:

WITH FIRST AS (
SELECT DISTINCT ID
FROM X 
WHERE TYPE = 'A'),

SECOND AS (
SELECT DISTINCT ID
FROM X 
WHERE TYPE = 'B'
)
SELECT *
FROM X 
WHERE Order IN (    SELECT DISTINCT FIRST.ID
                    FROM FIRST
                    JOIN SECOND 
                    ON FIRST.ID = SECOND.ID);
                

但是,我不喜欢这种解决方案。有更好的方法吗?

【问题讨论】:

    标签: sql oracle count subquery


    【解决方案1】:

    使用解析函数:

    SELECT order_no,
           type
    FROM   (
      SELECT x.*,
             COUNT( CASE type WHEN 'A' THEN 1 END ) OVER ( PARTITION BY order_no )
               AS num_a,
             COUNT( CASE type WHEN 'B' THEN 1 END ) OVER ( PARTITION BY order_no )
               AS num_b
      FROM   x
    )
    WHERE  num_a > 0
    AND    num_b > 0;
    

    其中,对于样本数据:

    CREATE TABLE x ( Order_no, Type ) AS
    SELECT 123, 'A' FROM DUAL UNION ALL
    SELECT 123, 'B' FROM DUAL UNION ALL
    SELECT 123, 'C' FROM DUAL UNION ALL
    SELECT 234, 'A' FROM DUAL UNION ALL
    SELECT 234, 'C' FROM DUAL UNION ALL
    SELECT 345, 'B' FROM DUAL UNION ALL
    SELECT 456, 'C' FROM DUAL UNION ALL
    SELECT 456, 'A' FROM DUAL UNION ALL
    SELECT 567, 'A' FROM DUAL UNION ALL
    SELECT 567, 'B' FROM DUAL;
    

    输出:

    ORDER_NO |类型 --------: | :--- 123 |一种 123 |乙 123 | C 第567章一种 第567章乙

    db小提琴here

    【讨论】:

      【解决方案2】:

      一个选项使用子查询来过滤:

      select x.*
      from x
      where (select count(*) from x x1 where x1.ord = x.ord and x1.type in ('A', 'B')) = 2
      

      如果您的数据集中有重复项,您需要在子查询中使用count(distinct x1.type1) 而不是count(*)

      我们也可以使用窗口函数:

      select ord, type
      from (
          select x.*,
              max(case when type = 'A' then 1 else 0 end) over(partition by ord) has_a,
              max(case when type = 'B' then 1 else 0 end) over(partition by ord) has_b
          from x
      ) x
      where has_a = 1 and has_b = 1
      

      【讨论】:

      • 在第一个查询中,我认为你应该使用count(distinct type) 来包含同一个订单的重复类型的场景
      • @Popeye:示例数据没有显示这一点,但是好的,我编辑了我的答案以提及这一点。谢谢。
      • !!评论是给OP的。您的回答完全符合 OP 的要求。谢谢!!
      【解决方案3】:

      对问题的直接解释将使用exists

      select x.*
      from x
      where exists (select 1
                    from x x2
                    where x2.order = x.order and x2.type = 'A'
                   ) and
            exists (select 1
                    from x x2
                    where x2.order = x.order and x2.type = 'B'
                   ) ;
      

      但是,如果你想改变结果集的结构,这也可以做你想做的事:

      select order, list_agg(type, ',') within group (order by type)
      from x
      group by order
      having sum(case when x.type in ('A', 'B') then 1 else 0 end) = 2;
      

      这会为每个订单返回一行,其中订单有两种类型。该行还有一列包含订单的所有类型。

      【讨论】:

        【解决方案4】:

        为了完整起见,因为现在是圣诞节,您也可以使用collectmultiset condition 来解决这种关系划分问题。以下给出了其项目类型集包含“A”和“B”的订单号:

        select x.order_no
        from   x
        group by x.order_no
        having ora_mining_varchar2_nt('A','B') submultiset of cast(collect(x.type) as ora_mining_varchar2_nt)
        

        我使用了ora_mining_varchar2_nt,因为它是内置的,但您可以使用您拥有的任何标量嵌套表类型(尽管它需要匹配列,例如 varchar2 的嵌套表来匹配 varchar2 列)。

        由于这里的要求是获取所有行,因此我们必须在子查询中使用它,需要两次遍历表和一次连接...

        select *
        from   x
        where  x.order_no in
               ( select x.order_no
                 from   x
                 group by x.order_no
                 having ora_mining_varchar2_nt('A','B') submultiset of cast(collect(x.type) as ora_mining_varchar2_nt) )
        

        或者把它写成解析:

        select order_no, type
        from   ( select x.order_no
                      , x.type
                      , cast(collect(x.type) over (partition by x.order_no) as ora_mining_varchar2_nt) as order_types
                 from   x )
        where  ora_mining_varchar2_nt('A','B') submultiset of order_types
        

        但是,我希望为每一行构建集合,然后使用多集条件对其进行测试,其效率远低于 MTO 的答案中简单计算出现次数的效率。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-14
          • 1970-01-01
          相关资源
          最近更新 更多