2013-10-24 75 views
5

性能的角度來看,這是寫關於嵌套查詢以下查詢的最佳方式:嵌套查詢性能的替代品


SELECT a.meg,a.currency 
FROM alt6sal a 
WHERE a.meg_code IN (1,2) 
AND a.sal_year = (SELECT MAX(ia.sal_year) FROM alt6sal ia WHERE a.emp_num = ia.emp_num) 
AND a.sal_mon = (SELECT MAX(ia.sal_mon) FROM alt6sal ia WHERE a.emp_num = ia.emp_num AND a.sal_year = ia.sal_year) 
+1

'a.sal_year>(SELECT MAX(ia.sal_year)'甚至不應該提供一個結果 – bummi

+0

@bummi:抱歉'a.sal_year =(SELECT MAX(ia.sal_year)' –

+1

這是SQL SERVER或INFORMIX?你可以同時標記 – ceinmart

回答

1

如果可以避免相關的子查詢,則性能越好,不相關子查詢的示例如下:

SELECT a.meg,a.currency 
FROM alt6sal a 

join 
(
    select ia.emp_num, max(ia.sal_year) as sal_year_max 
    from alt6sal ia 
    group by ia.emp_num 
) the_year_max 
on a.emp_num = the_year_max.emp_num and a.sal_year = the_year_max.sal_year_max 

join 
(
    select ia.emp_num, ia.sal_year, max(ia.sal_mon) as sal_mon_max 
    from alt6sal ia 
    group by ia.emp_num, ia.sal_year 
) the_month_max 
on a.emp_num = the_month_max.emp_num and a.sal_year = the_month_max.sal_year 
and a.sal_mon = the_month_max.sal_mon_max 

WHERE a.meg_code IN (1,2) 

類似的非相關JOINS的,而不是AND或者,使用LEFT JOIN然後過濾,在非空

SELECT a.meg,a.currency 
FROM alt6sal a 

left join 
(
    select ia.emp_num, max(ia.sal_year) as sal_year_max 
    from alt6sal ia 
    group by ia.emp_num 
) the_year_max 
on a.emp_num = the_year_max.emp_num and a.sal_year = the_year_max.sal_year_max 

left join 
(
    select ia.emp_num, ia.sal_year, max(ia.sal_mon) as sal_mon_max 
    from alt6sal ia 
    group by ia.emp_num, ia.sal_year 
) the_month_max 
on a.emp_num = the_month_max.emp_num and a.sal_year = the_month_max.sal_year 
and a.sal_mon = the_month_max.sal_mon_max 

WHERE a.meg_code IN (1,2) 
     and 
     (the_year_max.ia_emp_num is not null 
     or the_month_max.ia_emp_num is not null) 
+0

如果 AND a.sal_year =(SELECT MAX(ia.sal_year)FROM alt6sal ia WHERE a.emp_num = ia.emp_num) OR a.sal_mon =(SELECT MAX(ia.sal_mon)FROM alt6sal ia WHERE a。 emp_num = ia.emp_num AND a.sal_year = ia.sal_year )' 'OR'而不是'AND'? –

1

你可以試試這個 -

SELECT meg, currency 
FROM 
(
SELECT a.meg,a.currency, 
dense_rank() over (PARTITION BY a.emp_num ORDER BY a.sal_year desc) year_rank, 
dense_rank() over (PARTITION BY a.emp_num ORDER BY a.sal_mon desc) mon_rank 
FROM alt6sal a 
WHERE a.meg_code IN (1,2) 
) 
WHERE year_rank = 1 
AND mon_rank = 1; 
+0

語法錯誤'PARTITION' –

+0

Aditya答案,用於Sql Server,因爲你標記了它... – ceinmart

2

任何建議的性能,這裏將取決於很多:
- 版過濾索引
- - 數據
的量 - 表/索引的統計信息更新

您的Informix引擎
(語法可能不會與< 11.50版工作) 10

這將迫使數據庫首先創建一個臨時表與所有sal_year,然後與主表加入...

建議1)

SELECT a.meg,a.currency 
FROM alt6sal a 
    ,(SELECT emp_num, MAX(ia.sal_year) sal_year FROM alt6sal ia group by 1) as a2 
WHERE a.meg_code IN (1,2) 
AND a.sal_year = a2.sal_year and a.emp_num = a2.emp_num 
AND a.sal_mon = (SELECT MAX(ia.sal_mon) FROM alt6sal ia WHERE a.emp_num = ia.emp_num AND a.sal_year = ia.sal_year) 

建議2)

SELECT a.meg,a.currency 
FROM alt6sal a 
    ,(SELECT aa.emp_num, MAX(aa.sal_year) sal_year FROM alt6sal aa where aa.meg_code in (1,2) group by 1) as a2 
    ,(SELECT ab.emp_num, ab.sal_year, max(ab.sal_mon) sal_mon FROM alt6sal ab where ab.meg_code in (1,2)group by 1,2) as a3 
WHERE a.meg_code IN (1,2) 
AND a.sal_year = a2.sal_year and a.emp_num = a2.emp_num 
and a.sal_mon = a3.sal_mon AND a.sal_year = a2.sal_year and a.emp_num = a2.emp_num 
; 
1

我寧願創建一個臨時表來查找MAX值,並且可能會減少鎖,因爲您在表上進行了2次單獨讀取而不是3個併發讀取。

/*create @table to keep uniqe records for empnum, salaryyear, salarymonth*/ 
    DECLARE @maxyearstage TABLE(empnum BIGINT, combo DATETIME); 
    DECLARE @maxyear TABLE(empnum BIGINT, [year] INT, [month] TINYINT); 
    INSERT INTO @maxyearstage 
    SELECT DISTINCT my.emp_num 
    , CAST(CONVERT(VARCHAR(my.sal_year)+'-'+CONVERT(VARCHAR(my.sal_month)+'-'+'01' [combo] 
    FROM alt6sal my; 

    INSERT INTO @maxyear 
    SELECT t3.empnum, YEAR(t3.combo), MONTH(t3.combo) 
    FROM (SELECT T2.empnum, MAX(T2.combo) combo FROM @maxyear T2 GROUP BY T2.empnum) t3; 

    SELECT a.meg,a.currency 
    FROM alt6sal a 
    INNER JOIN @maxyear t1 ON t1.empnum = a.empnum AND t1.[year] = a.sal_year AND t1.[month] = a.sal_mon 
    WHERE a.meg_code IN (1,2) 
1

在任何情況下,我不會喜歡相關的子查詢。從我看到這是INFORMIX而不是SQL的意見,因此我會建議使用JOIN與嵌套選擇作爲第一首選項。這樣做的好處是這些都是非常原生的編寫查詢方式,您可以期望數據庫優化器使用索引(如果可用)提出良好的執行計劃。 SQL中的 如果我的表不在百萬行中,我會去CTE。我假設你有適當的表格索引。如果不確定你在表格上有以下索引。

注意索引中列的順序及其ASC/DESC順序。

CREATE CLUSTER INDEX IDXc_alt6sal 
    ON alt6sal ( meg_code ASC, 
      sal_year DESC, 
      sal_mon DESC, 
      emp_num ASC 
     ) 

    CREATE INDEX IDXnc_alt6sal 
    ON alt6sal ( meg_code ASC, 
      sal_year DESC, 
      sal_mon DESC, 
      emp_num ASC 
     ) INCLUDE (meg,currency) 

現在測試下面的查詢。請注意,當我使用實際表格時,我在所有選擇中都添加了「meg_code IN(1,2)」條件。即使在嵌套的select語句中,也允許查詢減少結果集中所需的行數。還會注意到Where和JOIN條件中的查詢中提及的列與索引中的列順序匹配。

一件事我留給你的是嘗試

「meg_cod = 1或meg_cod = 2」

,而不是

「meg_code IN(1,2)」

並查看性能是否爲明顯提高。我知道如果它是SQL它不會有任何區別,但對於INFORMIX我不是100%確定。

SELECT t1.meg,t1.currency,t1.emp_num 
    FROM alt6sal t1 
    JOIN 
    (
     Select yer.emp_num,yer.sal_year,MAX(mth.sal_mon) AS sal_mon 
     FROM   
     (SELECT emp_num, MAX(sal_year) AS sal_year 
      FROM  alt6sal 
      WHERE meg_code IN (1, 2) 
      GROUP BY emp_num 
     )yer 
     JOIN alt6sal mth 
     ON yer.sal_year = mth.sal_year AND yer.emp_num=mth.emp_num 
     AND mth.meg_code IN (1,2) 
     GROUP BY yer.sal_year,yer.emp_num 
    )t2 
    ON t1.sal_year=t2.sal_year AND t1.sal_mon=t2.sal_mon AND t1.emp_num=t2.emp_num 
    AND t1.meg_code IN (1,2) 
1

看起來像查詢將訪問表中的大部分數據(如果不是直接進行全表掃描)。如果是這樣,我會建議完全避免相關子查詢,因爲它們充其量只能執行和索引一樣。嘗試將其重新編寫成如下所示的簡單連接,第一部分簡單地查找每個員工的最大年/月,然後將其用作針對alt6sal的連接篩選器。

SELECT a.meg,a.currency 
FROM alt6sal a, 
    (SELECT MAX(ia.sal_year || '-' || ia.sal_mon) max_sal_year_mon, ia.emp_num ia_emp_num FROM alt6sal ia where) ia 
WHERE a.meg_code IN (1,2) 
AND (a.sal_year||'-'||a.sal_mon) = max_sal_year_mon 
AND ia_emp_num = emp_num;