Oracle SQL: select 从一组订单中仅包含特定类型的订单
Oracle SQL: select from a set of orders only those that contain certain types
我只想从一组订单中 select 那些标有特定类型的订单。
示例:
假设我有一个 table X.
Order Type
123 A
123 B
123 C
234 A
234 C
345 B
456 C
456 A
567 A
567 B
在我的例子中,我只想 select 那些同时包含 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);
但是,我不喜欢这个解决方案。有没有更好的方法?
一个选项使用子查询来过滤:
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(*)
。
我们还可以使用 window 函数:
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
使用解析函数:
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 | TYPE
-------: | :---
123 | A
123 | B
123 | C
567 | A
567 | B
db<>fiddle here
问题的直接解释将使用 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;
这 returns 每个订单一行,其中订单有两种类型。该行还有一列包含订单的所有类型。
为了完整起见,因为现在是圣诞节,您还可以使用 collect
和 multiset 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
,因为它是内置的,但您可以使用任何标量嵌套 table 类型(尽管它需要匹配列,例如嵌套 table 的 varchar2 以匹配 varchar2 列)。
由于这里的要求是获取所有行,我们必须在子查询中使用它,需要两次遍历 table 和一个连接...
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 的回答中简单地计算出现次数效率要低得多。
我只想从一组订单中 select 那些标有特定类型的订单。
示例:
假设我有一个 table X.
Order Type
123 A
123 B
123 C
234 A
234 C
345 B
456 C
456 A
567 A
567 B
在我的例子中,我只想 select 那些同时包含 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);
但是,我不喜欢这个解决方案。有没有更好的方法?
一个选项使用子查询来过滤:
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(*)
。
我们还可以使用 window 函数:
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
使用解析函数:
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 | TYPE -------: | :--- 123 | A 123 | B 123 | C 567 | A 567 | B
db<>fiddle here
问题的直接解释将使用 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;
这 returns 每个订单一行,其中订单有两种类型。该行还有一列包含订单的所有类型。
为了完整起见,因为现在是圣诞节,您还可以使用 collect
和 multiset 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
,因为它是内置的,但您可以使用任何标量嵌套 table 类型(尽管它需要匹配列,例如嵌套 table 的 varchar2 以匹配 varchar2 列)。
由于这里的要求是获取所有行,我们必须在子查询中使用它,需要两次遍历 table 和一个连接...
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 的回答中简单地计算出现次数效率要低得多。