2008-09-23 91 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。

我認爲最好的答案不是符合您的標準的答案,而是爲每個平臺寫出最適合該平臺的不同陳述。