2017-08-07 72 views
3

有在SQL Server中運行兩個similiar sql語句,該表TBSFA_DAT_CUST有millons行和無約束(無索引和主鍵), 其他兩個有隻有幾行和正常的主鍵:爲什麼使用表假脫機慢?

小號較慢的一個:

SELECT A.CUST_ID, C.CUST_NAME, A.xxx --and several specific columns 
FROM TBSFA_DAT_ORD_LIST A JOIN VWSFA_ORG_EMPLOYEE B ON A.EMP_ID = B.EMP_ID 
    LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID 
    JOIN VWSFA_ORG_EMPLOYEE D ON A.REVIEW_ID = D.EMP_ID 
WHERE ISNULL(A.BATCH_ID, '') != '' 

execution plan of slower one

f表示更快的一個:

SELECT * 
FROM TBSFA_DAT_ORD_LIST A JOIN VWSFA_ORG_EMPLOYEE B ON A.EMP_ID = B.EMP_ID 
    LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID 
    JOIN VWSFA_ORG_EMPLOYEE D ON A.REVIEW_ID = D.EMP_ID 
WHERE ISNULL(A.BATCH_ID, '') != '' 

execution plan of faster one

f(0.6s以上)比s(4.6s以上)快得多。

否則,我發現了兩個辦法,使s快速爲f:

1.增加constaint和表TBSFA_DAT_CUST.CUST_ID主鍵;

2.特定的61個以上的表TBSFA_DAT_CUST(共80列)列。

我的問題是爲什麼sql優化器使用表假脫機當我在SELECT子句而不是'*'的特定列,以及爲什麼使用表假脫機一個執行較慢?

我的問題是關於

+0

確保你運行它至少兩次,所以你消除時間創造這個計劃是一個原因。除此之外,我的猜測是'select *',SQL知道它必須執行表掃描,所以它甚至不會嘗試執行某些「優化」。然而,通過選擇單獨的列,它會嘗試,而在這種情況下,不會做得更好。表假脫機意味着SQL基本上將所有數據都放入臨時表中,以便稍後在查詢中重新使用它。雖然這並非天生不好,但需要一些時間,這可能只是優化器丟棄球的情況。 – Xedni

+0

謝謝你!我已經運行這些sql幾十次以獲得平均成本。並且執行計劃顯示兩個sqls都進行了表掃描。我想知道的是爲什麼sql優化器選擇使用表假脫機,而我指定的列和發生的事情,使其緩慢?據我所知,通常選擇特定的列應該比不快。 –

+0

最有可能是因爲它是如此寬闊的桌子。當你選擇*時,它意識到這是一大堆數據,並且認爲桌面假脫機會浪費時間和精力。當你限制列數時,它認爲它現在處於閾值之內,假脫機數據可能是一個很好的調用。如果沒有索引,那麼就沒有多少SQL可以知道數據的統計數據,因此估計會變得更糟。這也可能是爲什麼(除其他原因之外)爲表格添加索引會提高性能。 – Xedni

回答

0

在你限制你的結果設定爲特定的列較慢的查詢。由於這是一個未索引的非約束表,因此優化器正在從原始表掃描中創建一個臨時表,只需要特定的列。然後它通過臨時表上的嵌套循環運算符運行。當它知道它需要表上的每一列(Select *)時,它可以直接在表掃描之外運行嵌套循環操作符,因爲掃描的結果集將全部連接到頂部表。

外,你的查詢有幾個其他可能出現的問題:

LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID 

你不加入任何東西在這裏,你將加入整個表的每一個記錄。意思是a.cust_id = c.cust_idb.cust_id = c.cust_ida.cust_id = c.cust_id and b.cust_id = c.cust_id

此外,該功能在where子句是沒有意義的,而且會降低性能:

WHERE ISNULL(A.BATCH_ID, '') != '' 

將其更改爲:

WHERE A.BATCH_ID is not null and A.Batch_ID <> ''