2013-10-24 53 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;