如何优化以下查询?

时间:2017-09-08 05:21:45

标签: sql sql-server performance

我有一个表,表有超过7000万行。如果我执行此查询,则需要大约1581秒才能完成。如何优化此查询?而且该表是日志表。该表没有任何索引。有什么建议吗?

select a.volumeOfUser_Show_Psg, b.volumeOfUser_Continue_Psg, (a.volumeOfUser_Show_Psg - b.volumeOfUser_Continue_Psg) as totalNumberCancleProgressSaver
from
(select Count(R_CRE_ID) as volumeOfUser_Show_Psg from CADT where CUR_REC like 'Show_Progress_Saver%'
and CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate) a,
(select Count(R_CRE_ID) as volumeOfUser_Continue_Psg from CADT where CUR_REC like 'Continue_Progress_Saver%'
and CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate) b;

4 个答案:

答案 0 :(得分:1)

现在我看到以下问题:

  • 到目前为止,您投出了2.4亿个值;你可以避免这样做

  • 您在此表中没有索引

  • 如果可以执行一次

  • ,则可能会扫描两次

要解决这些问题:

  • 创建持久计算列,其值为r_cre_id的日期转换版本。这意味着演员操作将在插入时发生一次,而不是每次运行查询时都会发生..

  • 在一个索引中将COMPUTED COLUMN和cur_rec列一起索引。这是您将获得的单一最大性能提升,因为您将拥有涵盖整个查询的索引

  • 通过这种模式点击表/索引一次:

查询

SELECT 
  count(case when cur_rec like 's%' then 1 end) as count_show,
  count(case when cur_rec like 'c%' then 1 end) as count_cont
FROM
  table
WHERE
  computed_column_date BETWEEN @fromdate and @todate ANd
  (Cur_rec like 'continue_progress_saver%' or cur_rec like 'show_progress_saver%')

不要跳过计算列。不要使用时间组件索引日期列(如果索引较小,则此索引的性能会更好,这意味着通过减少粒度来索引更少的唯一值)。仅创建日期部分的计算列并将其编入索引。如果您执行此操作,此查询将在几秒钟内返回结果

注意,如果你的cur_rec列也是高度变化的(即它包含超过一百万个唯一值),你应该考虑创建函数LEFT(cur_rec, 20)的结果的另一个计算列,改为索引这个计算列,并且使您的查询看起来像:

SELECT 
  count(case when computed_column_left20currec like 's%' then 1 end) as count_show,
  count(case when computed_column_left20currec like 'c%' then 1 end) as count_cont
FROM
  table
WHERE
  computed_column_date BETWEEN @fromdate and @todate AND
  computed_column_left20currec in ('show_progress_saver','continue_progress_s')

我之所以建议为这些列制作计算列,是因为这些计算列的唯一值要少得多。这意味着任何生成的索引都具有较少的键。如果我们所做的只是计算,最好不要计算每个引用一行的数亿个索引键,以获得总数。

有关创建计算列的信息,请参阅以下内容:

https://www.mssqltips.com/sqlservertip/1682/using-computed-columns-in-sql-server-with-persisted-values/

答案 1 :(得分:0)

WITH AS

WITH Data (volumeOfUser_Show_Psg, volumeOfUser_Continue_Psg)
AS 
(
    SELECT Count(1) AS volumeOfUser_Show_Psg, 0 AS volumeOfUser_Continue_Psg
    FROM CADT
    WHERE CUR_REC LIKE 'Show_Progress_Saver%' AND CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate
    UNION
    SELECT Count(1) AS volumeOfUser_Show_Psg, 0 AS volumeOfUser_Continue_Psg
    FROM CADT
    WHERE CUR_REC LIKE 'Continue_Progress_Saver%' AND CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate
)
SELECT SUM(volumeOfUser_Show_Psg), SUM(volumeOfUser_Continue_Psg), (SUM(volumeOfUser_Show_Psg) - SUM(volumeOfUser_Continue_Psg)) as totalNumberCancleProgressSaver
FROM Data

案例

WITH Data (volumeOfUser_Show_Psg, volumeOfUser_Continue_Psg)
AS 
(
    SELECT SUM(CASE WHEN CUR_REC LIKE 'Show_Progress_Saver%' THEN 1 ELSE 0 END) AS volumeOfUser_Show_Psg, SUM(CASE WHEN CUR_REC LIKE 'Continue_Progress_Saver%' THEN 1 ELSE 0 END) AS volumeOfUser_Continue_Psg
    FROM CADT
    WHERE CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate
)
SELECT volumeOfUser_Show_Psg, volumeOfUser_Continue_Psg, (volumeOfUser_Show_Psg - volumeOfUser_Continue_Psg) as totalNumberCancleProgressSaver
FROM Data

但无论如何,您是否为C_CRE_TIME创建了一个索引,并且可能与CUR_REC结合使用?那个施放日期可能很糟糕,为什么需要呢?

答案 2 :(得分:0)

如果能提供性能,请你试试下面的

 SELECT volumeofuser_show_psg, volumeofuser_continue_psg, 
     ( volumeofuser_show_psg - volumeofuser_continue_psg ) AS 
           totalNumberCancleProgressSaver 
(
       SELECT 
             COUNT(CASE WHEN cur_rec LIKE 'Show_Progress_Saver%' then 1 ELSE NULL END) as "volumeofuser_show_psg",
           COUNT(CASE WHEN cur_rec LIKE 'Continue_Progress_Saver%' then 1 ELSE NULL END) as "volumeofuser_continue_psg"

       FROM     cadt 
       WHERE    Cast(r_cre_time AS DATE) >= @fromDate 
                AND Cast(r_cre_time AS DATE) <= @toDate)      
         ) T;

答案 3 :(得分:0)

你的意思是做笛卡尔式的加入吗?

即。 from a,b

Here's a nice reference of how this join would work

如果你没有,这可能会提供你想要的东西:

select  a.volumeOfUser_Show_Psg,
        a.volumeOfUser_Continue_Psg,
        (a.volumeOfUser_Show_Psg - a.volumeOfUser_Continue_Psg) as totalNumberCancleProgressSaver
    from(
            select sum(case when cur_rec like 'Show%' then 1 else 0 end) as volumeOfUser_Show_Psg,
                    sum(case when cur_rec like 'Cont%' then 1 else 0 end) as count_cont as volumeOfUser_Continue_Psg
            from CADT 
            where (CUR_REC like 'Show_Progress_Saver%' or CUR_REC like 'Continue_Progress_Saver%')
                    and CAST(R_CRE_TIME AS DATE) >= @fromDate and CAST(R_CRE_TIME AS DATE) <= @toDate)
        ) a;

这能为您提供所需的结果吗?

提示,尝试格式化代码,使语句分开。这使得代码更容易阅读。

每个记录的转换将影响性能,最好将@fromDate和@toDate更改为与R_CRE_TIME相同。 如果可能,请在表上创建索引。这将有很大帮助。

如果没有关于数据的更多信息,涉及的表格或意图没有太多其他内容可以添加。