如何检查Oracle中的项目表中是不是存在一组值

Posted

技术标签:

【中文标题】如何检查Oracle中的项目表中是不是存在一组值【英文标题】:How to check if a set of values exist in item table in Oracle如何检查Oracle中的项目表中是否存在一组值 【发布时间】:2015-02-18 16:11:09 【问题描述】:

我有两个表 - 'Order' 和 'Order Item'。

订单表包含- 订单号、订单日期等。 订单项目表包含- 订单号、订单项号、产品名称等

这两个表的连接条件是订单号。

在我的目标表中,我需要订单和标志。该标志应该表明,如果有一组预定义的产品已作为该订单的一部分订购,那么它应该设置为“是”。

例如,假设订单“ORD-01”在 Order Item 表中包含三个产品 - “Mobile”、“PC”和“Tablet”,那么我的结果表应该包含 ORD-01 的订单号和“是”的标志'。 同样,如果订单“ORD-02”仅包含两个产品“Mobile”和“Tablet”,则结果表应包含“ORD-02”和标记“No”。 同样,如果订单“ORD-03”包含三个不同的产品“Notebook”、“PC”和“Tablet”,则结果表应包含“ORD-03”和“No”标志。

根据我的理解,我在下面写了查询-

SELECT order_number,(SELECT CASE WHEN COUNT(DISTINCT product_name)>=3     
THEN 'Yes' ELSE 'No' END Prod_Flag 
FROM order_item b
WHERE a.order_number=b.order_number
AND b.product_name IN ('Mobile','PC','Tablet'))
FROM order a
WHERE order_date>last_run_date;

但这需要太多时间,因为订单项是一个非常大的表(>10 亿行)。但是,我需要基于 Order 表中的订单日期的增量数据。即使两个表都有订单号的索引,也是需要时间的。

【问题讨论】:

欢迎来到 SO!您可以通过在格式选项上达到峰值来改善阅读体验。 【参考方案1】:

这样的查询会让您更快地得到结果吗?

SELECT ON.ORDER_NUMBER,
   CASE WHEN SET_FOUND.ORDER_NUMBER IS NOT NULL 
   THEN 'Yes' ELSE 'No' END PROD_FLAG
FROM ORDER ON,
(SELECT ORDER_NUMBER
   FROM ORDER_ITEM
  WHERE PRODUCT_NAME = 'Mobile'
 INTERSECT
 SELECT ORDER_NUMBER
   FROM ORDER_ITEM
  WHERE PRODUCT_NAME = 'PC'
 INTERSECT
 SELECT ORDER_NUMBER
   FROM ORDER_ITEM
  WHERE PRODUCT_NAME = 'Tablet') SET_FOUND
WHERE ON.ORDER_NUMBER = SET_FOUND.ORDER_NUMBER (+)

【讨论】:

这个查询也需要时间,因为它正在订单项表中进行全表扫描。【参考方案2】:

我的建议是这个:

WITH t AS
   (SELECT product_name, order_number
   FROM order_item
   WHERE product_name IN ('Mobile','PC','Tablet')
   GROUP BY order_number, product_name)
SELECT order_number, 
   CASE WHEN COUNT(DISTINCT product_name) >= 3 THEN 'Yes' ELSE 'No' END
FROM t
   JOIN order USING (order_number)
GROUP BY order_number

【讨论】:

这也需要时间。【参考方案3】:

订单号是递增的序号吗?如果是这样,一种方法是通过在 order_number 上设置条件来限制从 order_item 中选择的数据,你说它是一个大表,你说它是一个索引列。我假设 last_run_date 显着限制了相关订单的数量。 如果是这样,您可以:

select min(order_number) into order_num_from from Order where       order_date>last_run_date

然后进行查询

SELECT order_number,(SELECT CASE WHEN COUNT(DISTINCT product_name)>=3     
THEN 'Yes' ELSE 'No' END Prod_Flag 
FROM order_item b
WHERE a.order_number=b.order_number
AND b.order_number> order_num_from
AND b.product_name IN ('Mobile','PC','Tablet'))
FROM order a
WHERE order_date>last_run_date;

如果这运行得更快(我没有看到解释计划,所以这只是一个如何避免全表扫描的想法),在 order_date 列上放置一个索引,并最终将 order_num_from 查找到子查询中以进行单个查询。

【讨论】:

对不起,它是字母数字和非连续的。【参考方案4】:

一般来说,您的查询是正确的。据我了解,您希望提高它的速度。如果是这样,您可以尝试多种方法。

您可以考虑将这些表放入索引集群。它将存储物理连接的数据,因此查询需要更少的物理读取。

对于此查询,服务器应扫描两张表:一张用于适当的日期(全表扫描或索引扫描),另一张用于产品并通过 rowid 读取 ORDER_NUMBER 来连接结果。反正也不是很快。最简单的方法是为 ORDER 添加 (ORDER_DATE, ORDER_NUMBER) 索引,为 ORDER_ITEM 添加 (ORDER_NUMBER, PRODUCT_NAME) 索引;它将只允许使用索引。

也许制作一个可快速刷新的物化视图是合适的,比如

create materialized view as
select 
  a.order_date, 
  a.order_number,
  sum(case when b.product_name = 'Mobile' then 1 else 0 end) cnt_mobiles,
  sum(case when b.product_name = 'PC' then 1 else 0 end) cnt_pcs,
  sum(case when b.product_name = 'Tablet' then 1 else 0 end) cnt_tablets
from
  order a, order_item b
where
  a.order_number = b.order_number
group by
  a.order_number, a.order_date

如果无法使其快速刷新,您可以使用触发器手动执行相同的操作。无论如何,在这种情况下,您将获得准备好检查的预先计算的数据。

【讨论】:

感谢 Sanders 的回答,但我的想法是,如果我们可以通过使用一些“exists”子句(因为存在被认为更快)来获得这个标志的替代方法。但我没有办法在这种情况下编写它。 你肯定错了。带有聚合的 MV 可以快速刷新。例如,如果它包含 SUM(field),则快速刷新会在处理 MV 日志时添加 :new.field - :old.field。如果它包含 count(*),它将加和/或减一。等等。例如,请参阅adellera.it/blog/2013/08/19/…。 @SanderstheSoftwarer,好的,我的评论是通用的,但是有一些限制:Restrictions on Fast Refresh on Materialized Views with Aggregates。我不知道count(case when b.product_name = 'Mobile' then 1 else 0 end) 是否有效。 是的,我也没查。这就是为什么我说'如果不可能'.. :)

以上是关于如何检查Oracle中的项目表中是不是存在一组值的主要内容,如果未能解决你的问题,请参考以下文章

检查数组是不是至少包含 PHP 中的一组值

Oracle - 在删除其他表中的几行之前检查行是不是存在

如何检查元素是不是存在于一组项目中?

确定一个值是不是在 Java 中的一组值中的最快方法是啥?

oracle sql语句

Oracle数据库语句