如何检查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中的项目表中是不是存在一组值的主要内容,如果未能解决你的问题,请参考以下文章