2008-09-23 68 views
12

我试图运行在Oracle中下面的SQL语句,它需要年龄运行:优化运行缓慢甲骨文很快地运行SQL Server的SELECT查询

SELECT orderID FROM tasks WHERE orderID NOT IN 
(SELECT DISTINCT orderID FROM tasks WHERE 
engineer1 IS NOT NULL AND engineer2 IS NOT NULL) 

如果我只运行子部分即IN子句,在甲骨文运行速度很快的,即

SELECT DISTINCT orderID FROM tasks WHERE 
engineer1 IS NOT NULL AND engineer2 IS NOT NULL 

为什么整个声明采取的Oracle这么长的时间?在SQL Server中,整个语句运行得很快。

另外还有一个更简单/不同/更好的SQL语句,我应该使用?

有关该问题的更多的细节:

  • 每个订单是由许多任务
  • 每个订单将被分配(一个或多个它的任务将有工程师1和engineer2设置)或顺序可以未分配(其所有任务对于工程师字段都为空值)
  • 我正在尝试查找所有未分配的orderID。

为了防万一,它有什么区别,表中有〜120K行,每个订单有3个任务,所以〜40K个不同的订单。

回应的答案:

  • 我宁愿这两个SQL Server和Oracle在工作的SQL语句。
  • 这些任务只有orderID和taskID上的索引。
  • 我尝试了这个声明的NOT EXISTS版本,但是在我取消它之前运行了3分钟。也许需要一个JOIN版本的声明?
  • 还有一个“orders”表以及orderID列。但是我试图通过不将它包含在原始SQL语句中来简化问题。

我想在原来的SQL语句中,每次在SQL语句的第一部分中都会运行子查询 - 尽管它是静态的,只需要运行一次?

执行

ANALYZE TABLE tasks COMPUTE STATISTICS; 

了我原来的SQL语句执行速度更快。

虽然我仍然很好奇为什么我必须这样做,如果/当我需要再次运行它时?

根据统计甲骨文是 需要确定效率的不同的执行计划 基于成本 optimzer信息:为 例如,rowsin表, 行的平均宽度的数量,最高 每列最低值,每列不同值 ,集群 索引等因素

在一个小型数据库中,您只需设置 就可以每晚收集统计数据 ,并保持独立。其实这是 默认在10g以下。对于较大的 实现,您通常必须以 衡量执行的稳定性 计划与数据 更改的方式,这是一个棘手的平衡。

Oracle还有一个称为其用于 样品表来确定在执行时相关 统计 “动态采样”功能。这是 更经常与数据使用 仓库,其中 采样的开销超过了 长时间运行的查询的潜在性能提升。

+1

我永远不会明白为什么程序员经常把DISTINCT放在他们的IN子句中。 (1,1,1,2,2,2,7)是7?是5?如果我的列表是(1,2,7),答案不会改变。当我在Oracle中运行它时,它忽略了独特的... CBO意识到没有价值。 – 2008-10-24 21:38:16

回答

9

通常这种类型的问题消失了,如果你分析涉及的表(所以Oracle具有数据分布的一个更好的想法)

ANALYZE TABLE tasks COMPUTE STATISTICS; 
3

Oracle中的“IN” - 子句是非常慢的。实际上,Oracle中的内部查询优化器无法处理“IN”语句非常好的语句。尝试使用“EXISTS”:

SELECT orderID FROM tasks WHERE orderID NOT EXISTS 
    (SELECT DISTINCT orderID FROM tasks WHERE 
     engineer1 IS NOT NULL AND engineer2 IS NOT NULL)`print("code sample");` 

小心:请检查查询是否生成相同的数据结果。

伊迪丝说:ooops,查询不是很好形成,但总的想法是正确的。 Oracle必须为第二个(内部)查询完成全表扫描,然后构建结果,然后将它们与第一个(外部)查询进行比较,这就是为什么它会变慢。尝试

SELECT orderID AS oid FROM tasks WHERE NOT EXISTS 
    (SELECT DISTINCT orderID AS oid2 FROM tasks WHERE 
     engineer1 IS NOT NULL AND engineer2 IS NOT NULL and oid=oid2) 

或类似的东西;-)

+0

我来到相同的查询(请参阅下面的答案),除了: *子查询没有理由选择DISTINCT orderIDs。 *删除WHERE和NOT EXISTS之间的“orderID”(语法错误)。 *删除'print(“code sample”),显然;-) – Mac 2008-09-23 12:08:49

+0

当我尝试第二个查询时,它会得到一个错误? ORA-00904:“OID2”:无效标识符 – RickL 2008-09-23 12:16:09

+0

使用插入的“AS oid2”,它不在查询之前。 – Georgi 2008-09-23 12:19:51

0

是不是你的查询一样

SELECT orderID FROM tasks 
WHERE engineer1 IS NOT NULL OR engineer2 IS NOT NULL 

+0

不,不是。我犯了同样的错误:-) 每个订单都有多个任务,如果其中一个任务有一个工程师分配,则该订单计为“已分配” – 2008-09-23 12:01:20

0

如何:

SELECT DISTINCT orderID FROM tasks t1 WHERE NOT EXISTS (SELECT * FROM tasks t2 WHERE t2.orderID=t1.orderID AND (engineer1 IS NOT NULL OR engineer2 IS NOT NULL)); 

我不是最优化的大师,但也许你也忽视了一些指标在Oracle数据库。

+0

我试过这个,但它在一分钟后仍然运行,当我取消它。 – RickL 2008-09-23 12:10:46

0

另一种选择是使用减号(除MSSQL)

SELECT orderID FROM tasks 
MINUS 
SELECT DISTINCT orderID FROM tasks WHERE engineer1 IS NOT NULL 
AND engineer2 IS NOT NULL 
-1

这里是一个替代的办法,我想给你想要的东西:

SELECT orderID 
FROM tasks 
GROUP BY orderID 
HAVING COUNT(engineer1) = 0 OR COUNT(engineer2) = 0 

我不知道,如果你想HAVING子句中的“AND”或“OR”。这听起来像根据业务逻辑,这两个字段应该都填充或者都是NULL;如果这是有保证的,那么你可以减少条件,只检查工程师1。

您的原始查询,我认为,每个订单ID都会给出多个行,而我的只会给出一个。我猜这是好的,因为你只是获取orderID。

2

一些问题:

  • 在任务中有多少行呢?
  • 它定义了哪些索引?
  • 最近有没有对表进行分析?

另一种方式来写同样的查询是:

select orderid from tasks 
minus 
select orderid from tasks 
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL 

不过,我宁愿希望查询涉及的 “订单” 表:

select orderid from ORDERS 
minus 
select orderid from tasks 
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL 

select orderid from ORDERS 
where orderid not in 
(select orderid from tasks 
    where engineer1 IS NOT NULL AND engineer2 IS NOT NULL 
) 

select orderid from ORDERS 
where not exists 
(select null from tasks 
    where tasks.orderid = orders.orderid 
    and engineer1 IS NOT NULL OR engineer2 IS NOT NULL 
) 
2

我TZQTZIO同意,我不明白您的查询。

如果我们假设查询没有意义,那么你可能想用尽量EXISTS一些建议和避免。 IN并不总是很糟糕,并且有可能出现这样的情况,即人们可能会发现它实际上比EXISTS表现更好。

问题标题不是很有帮助。我可以在一个Oracle数据库中设置此查询,并使其运行缓慢并使其在另一个数据库中快速运行。确定数据库如何解析查询,对象统计信息,SYS模式统计信息和参数以及服务器性能的因素很多。 Sqlserver与Oracle不是这里的问题。

对于那些有兴趣在查询调优和性能,并想了解更多一些谷歌方面的搜索都是“橡木桌子甲骨文”和“甲骨文乔纳森·刘易斯”。

3

我会尝试使用连接代替

SELECT 
    t.orderID 
FROM 
    tasks t 
    LEFT JOIN tasks t1 
     ON t.orderID = t1.orderID 
     AND t1.engineer1 IS NOT NULL 
     AND t1.engineer2 IS NOT NULL 
WHERE 
    t1.orderID IS NULL 

也是你的原始查询可能会更容易理解,如果它被指定为:

SELECT orderID FROM orders WHERE orderID NOT IN 
(SELECT DISTINCT orderID FROM tasks WHERE 
engineer1 IS NOT NULL AND engineer2 IS NOT NULL) 

(假设你有订单表的所有订单列出)

然后可以使用连接重写为:

SELECT 
    o.orderID 
FROM 
    orders o 
    LEFT JOIN tasks t 
     ON o.orderID = t.orderID 
     AND t.engineer1 IS NOT NULL 
     AND t.engineer2 IS NOT NULL 
WHERE 
    t.orderID IS NULL 
-1

如果你有过工程师1和Engineer2列没有索引,那么你总是会在SQL Server和任何可能在Oracle中等同于生成表扫描。

如果你只是需要有未分配的任务订单那么下面应该只是罚款在两个平台上,但你也应该考虑增加指标的任务表来提高查询性能比较。

SELECT DISTINCT orderID 
FROM tasks 
WHERE (engineer1 IS NULL OR engineer2 IS NULL) 
0

如果你决定创建订单表,我分配的标志添加到它,并创建位图索引。这种方法也会迫使您修改业务逻辑以保持标志更新,但查询速度会非常快。这取决于应用程序的查询有多关键。

关于答案,在这种情况下越简单越好。忘记子查询,连接,截然不同的分组,他们根本不需要!

1

我认为几个人有几乎正确的SQL,但缺少内部和外部查询之间的联接。
试试这个:

SELECT t1.orderID 
FROM tasks t1 
WHERE NOT EXISTS 
     (SELECT 1 
     FROM tasks t2 
     WHERE t2.orderID = t1.orderID 
     AND t2.engineer1 IS NOT NULL 
     AND t2.engineer2 IS NOT NULL) 
0

什么比例表中的行符合条件“engineer1 IS NOT NULL AND engineer2 IS NOT NULL”?

这会告诉您(粗略)是否值得尝试使用索引来检索关联的orderid。

另一种方式写在Oracle中的查询,将处理没有索引的情况下,很好地将是:

select distinct orderid 
from 
(
select orderid, 
     max(case when engineer1 is null and engineer2 is null then 0 else 1) 
      over (partition by orderid) 
      as max_null_finder 
from tasks 
) 
where max_null_finder = 0 
0

Oracle的优化做的处理减号陈述一份好工作。如果您使用MINUS重新编写查询,它可能运行得相当快:

SELECT orderID FROM tasks 
MINUS 
SELECT DISTINCT orderID FROM tasks WHERE 
engineer1 IS NOT NULL AND engineer2 IS NOT NULL 
0

新的采取。

IFF

  • 的COUNT()函数不计算NULL值

  • 你想要的所有任务将订单,其中没有的任务有工程师1或工程师2设置的值

然后这应该做你想要什么:

SELECT orderID 
FROM tasks 
GROUP BY orderID 
HAVING COUNT(engineer1) = 0 AND COUNT(engineer2) = 0 

请测试它。

1

“虽然我仍然很好奇为什么我必须这样做,如果/当我需要再次运行它时?”

统计数据为Oracle提供了基于成本的优化器信息,以确定不同执行计划的效率:例如,表中的行数,行的平均宽度,每列的最高值和最低值,每列不同的值,索引的聚类因子等。

在一个小型数据库中,您可以设置一个作业,以便每晚收集统计数据并保持独立。实际上,这是10g以下的默认值。对于更大的实现,您通常必须权衡执行计划的稳定性与数据更改的方式,这是一个棘手的平衡。

Oracle还有一个称为“动态采样”的功能,用于在执行时间对表格进行采样以确定相关统计信息。它更经常用于数据仓库,因为长时间运行的查询可能会提高性能,因此这些数据仓库的采样开销会超过它。

0

我同意ΤΖΩΤΖΙΟΥ和wearejimbo你的查询应该是...

SELECT DISTINCT orderID FROM Tasks 
WHERE Engineer1 IS NULL OR Engineer2 IS NULL; 

我不知道SQL Server中,但此查询将无法采取任何指标的优势,因为空行不在索引中。解决这个问题的方法是重新编写查询,以允许创建仅包含空值行的基于函数的索引。这可以用NVL2完成,但可能不能移植到SQL Server。

我认为最好的答案不是符合您的标准的答案,而是为每个平台写出最适合该平台的不同陈述。