嵌套查询性能替代方案

时间:2013-10-24 10:21:57

标签: sql performance informix nested-queries

性能角度,这是编写以下有关嵌套查询的查询的最佳方式:


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)

6 个答案:

答案 0 :(得分:2)

这里任何建议的表现将取决于很多:
  - 您的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
;

答案 1 :(得分: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;

答案 2 :(得分: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)

答案 3 :(得分: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)

答案 4 :(得分:1)

在任何情况下我都不会喜欢与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或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)

答案 5 :(得分: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;