簡體   English   中英

嵌套查詢性能替代方案

[英]nested query performance alternatives

性能角度來看 ,這是編寫以下有關嵌套查詢的查詢的最佳方法:


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)

這里任何建議的表現將取決於很多:
- Informix引擎的版本(語法可能不適用於版本<11.50)
- 過濾索引
- 數據量
- 更新了表/索引統計信息

這將強制數據庫首先使用所有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
;

你可以嘗試這個 -

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;

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

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)

OR的類似非相關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)

我寧願創建一個臨時表來查找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)

無論如何我不喜歡Co相關的Sub查詢。 從評論我看到這是為INFORMIX而不是SQL,因此我建議使用JOIN與嵌套選擇作為第一選擇。 這樣做的好處是那些非常本地的編寫查詢的方式,你可以期望DB優化器使用索引(如果可用)提出良好的執行計划。 在SQL中,如果我的表不是數百萬行,我會選擇CTE。 我假設你在桌子上有適當的索引。 如果不確保您在表上有以下索引。

注意Index中的列順序及其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語句中也是如此。 還要注意Query中的Where和JOIN條件中提到的列,並與Indexes中的列順序匹配。

我留給你的一件事是

“meg_cod = 1 OR 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)

看起來查詢將訪問表中的大部分數據(如果不是直接進行全表掃描)。 如果是這樣,我建議完全避免相關子查詢,因為它們最多只能執行索引。 嘗試將其重新編寫為如下所示的簡單連接,前半部分只需找到每個員工的最大年/月,然后將其用作針對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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM