1

我有一个报告的 sql 查询,它包括一些子查询。它运行得很慢。我尝试了几种方法(例如使用联接而不是子查询,添加更多索引)。但他们都没有工作。这是查询:

declare @time_from       datetime
declare @time_to         datetime   
set @time_from ='2012-01-01'
set @time_to = '2014-01-01'
select a.a_id, c.c_id, c.c_chat_line_id, a.a_first_name, a.a_last_name
    ,(select isnull(SUM(ac.ac_amount),0) from t_actress_credit ac join t_order o on o.o_id = ac.order_id where o.o_status = 1 and ac.actress_id = a.a_id and ac.ac_time>=@time_from and ac.ac_time<=@time_to) as  credit
    ,(select isnull(SUM(ac.ac_amount),0) from t_actress_credit ac join t_order o on o.o_id = ac.order_id where o.o_status = 1 and ac.ac_is_paid = 1 and ac.actress_id = a.a_id and ac.ac_time>=@time_from and ac.ac_time<=@time_to) as  paid_credit
    ,(select COUNT(1) from t_message pm join t_call_log l1 on pm.call_log_id = l1.c_id where pm.m_type = 2 and l1.caller_id = c.c_id and pm.m_time>=@time_from and pm.m_time<=@time_to) as pmsg_sent
    ,(select COUNT(1) from t_message pm join t_call_log l2 on pm.m_to_call_log_id = l2.c_id where pm.m_type = 2 and l2.caller_id = c.c_id and pm.m_time>=@time_from and pm.m_time<=@time_to) as pmsg_received
    ,(select COUNT(1) from t_message pm join t_call_log l3 on pm.call_log_id = l3.c_id where pm.m_type = 1 and l3.caller_id = c.c_id and pm.m_time>=@time_from and pm.m_time<=@time_to) as lcmsg_sent
    ,(select COUNT(1) from t_message pm join t_call_log l4 on pm.m_to_call_log_id = l4.c_id where pm.m_type = 1 and l4.caller_id = c.c_id and pm.m_time>=@time_from and pm.m_time<=@time_to) as lcmsg_received
    ,(select COUNT(1) from t_actress_live_minute where actress_id = a.a_id and alm_time>=@time_from and alm_time<=@time_to ) as live_calls
    ,(select isnull(SUM(alm_minutes),0) from t_actress_live_minute where actress_id = a.a_id  and alm_time>=@time_from and alm_time<=@time_to) as live_call_minutes
    ,(select isnull(count(1),0) from t_call_log l where l.caller_id = c.c_id and l.c_time_out is not null and c_time_in >=@time_from and c_time_in <= @time_to) as total_calls
    ,(select isnull(SUM(DATEDIFF(minute, l.c_time_in, l.c_time_out)),0) from t_call_log l where c_time_in >=@time_from and c_time_in <= @time_to and l.caller_id = c.c_id and l.c_time_out is not null ) as total_call_minutes
from t_actress a
join t_caller c on c.c_id = a.caller_id
group by a.a_id,c.c_id, c.c_chat_line_id, a.a_first_name, a.a_last_name

任何人都可以提供一些建议吗?

非常感谢!

艾伦

4

2 回答 2

0

您可以尝试将从同一个表或一组表中提取的子查询组合到一个子查询中。要考虑条件的变化,您可以使用条件聚合(使用 CASE 表达式)。

我可以在您的查询中看到四个,可能五个这样的组。这里重写为使用四个子查询:

SELECT
  a.a_id,
  c.c_id,
  c.c_chat_line_id,
  a.a_first_name,
  a.a_last_name,
  ISNULL(cr.credit            , 0) AS credit
  ISNULL(cr.paid_credit       , 0) AS paid_credit
  ISNULL(m.pmsg_sent          , 0) AS pmsg_sent,
  ISNULL(m.pmsg_received      , 0) AS pmsg_received,
  ISNULL(m.lcmsg_sent         , 0) AS lcmsg_sent,
  ISNULL(m.lcmsg_received     , 0) AS lcmsg_received,
  ISNULL(alm.live_calls       , 0) AS live_calls,
  ISNULL(alm.live_call_minutes, 0) AS live_call_minutes,
  ISNULL(l.total_calls        , 0) AS total_calls,
  ISNULL(l.total_call_minutes , 0) AS total_call_minutes,

FROM t_actress AS a

INNER JOIN t_caller AS c
  ON c.c_id = a.caller_id

LEFT JOIN (
  SELECT
    ac.actress_id,
    SUM(                               ac.ac_amount    ) AS credit,
    SUM(CASE ac.ac_is_paid WHEN 1 THEN ac.ac_amount END) AS paid_credit
  FROM t_actress_credit AS ac
  JOIN t_order o ON o.o_id = ac.order_id
  WHERE o.o_status = 1
    AND ac.ac_time BETWEEN @time_from AND @time_to
  GROUP BY ac.actress_id
) AS ac
  ON ac.actress_id = a.a_id

LEFT JOIN (
  SELECT
    l.caller_id,
    COUNT(CASE WHEN m.     call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
    COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
    COUNT(CASE WHEN m.     call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
    COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
  FROM t_message AS m
  JOIN t_call_log AS l ON l.c_id IN (m.call_log_id, m.m_to_call_log_id)
  WHERE m.m_type IN (1, 2)
    AND m.m_time BETWEEN @time_from AND @time_to
  GROUP BY l.caller_id
) AS m
  ON m.caller_id = c.c_id

LEFT JOIN (
  SELECT
    actress_id,
    COUNT(*)         AS live_calls,
    SUM(alm_minutes) AS live_call_minutes
  FROM t_actress_live_minute
  WHERE alm_time BETWEEN @time_from AND @time_to
  GROUP BY actress_id
) AS alm
  ON alm.actress_id = a.a_id

LEFT JOIN (
  SELECT
    caller_id,
    COUNT(*)                                     AS total_calls,
    SUM(DATEDIFF(MINUTE, c_time_in, c_time_out)) AS total_call_minutes
  FROM t_call_log
  WHERE c_time_out IS NOT NULL
    AND c_time_in BETWEEN @time_from AND @time_to
  GROUP BY caller_id
) AS l
  ON l.actress_id = a.a_id
;

如果您m通过单独连接将子查询分成两个,call_log_id则可能是五个子查询m_to_call_log_id(从而可能为查询计划器提供更多优化空间),即而不是

LEFT JOIN (
  SELECT
    l.caller_id,
    COUNT(CASE WHEN m.     call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
    COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
    COUNT(CASE WHEN m.     call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
    COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
  FROM t_message AS m
  JOIN t_call_log AS l ON l.c_id IN (m.call_log_id, m.m_to_call_log_id)
  WHERE m.m_type IN (1, 2)
    AND m.m_time BETWEEN @time_from AND @time_to
  GROUP BY l.caller_id
) AS m
  ON m.caller_id = c.c_id

这将是

LEFT JOIN (
  SELECT
    l.caller_id,
    COUNT(CASE WHEN m.call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
    COUNT(CASE WHEN m.call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
  FROM t_message AS m
  JOIN t_call_log AS l ON l.c_id = m.call_log_id
  WHERE m.m_type IN (1, 2)
    AND m.m_time BETWEEN @time_from AND @time_to
  GROUP BY l.caller_id
) AS mf
  ON mf.caller_id = c.c_id

LEFT JOIN (
  SELECT
    l.caller_id,
    COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
    COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
  FROM t_message AS m
  JOIN t_call_log AS l ON l.c_id = m.m_to_call_log_id
  WHERE m.m_type IN (1, 2)
    AND m.m_time BETWEEN @time_from AND @time_to
  GROUP BY l.caller_id
) AS mt
  ON mt.caller_id = c.c_id

还更改主 SELECT 子句中的相应引用。

我不确定哪种变体更好,您需要测试两者才能找出答案。

请注意,我省略了主查询的 GROUP BY 子句。在您的查询和我的查询中似乎都没有必要,因为据我所知,它包括来自两者的主键,t_actress并且t_caller这些组合无论如何都是唯一的。我假设 GROUP BY 是您之前尝试使用联接重写查询的遗留物。

于 2013-03-13T23:26:57.050 回答
0

感谢您的回复。我试过你的方法,还是很慢。这就是我所做的,终于奏效了。我基本上将所有子查询放入表中,然后在最后加入表。不知道为什么,但它更快:这是代码;

-- total calls
declare @t_call table(
    a_id bigint,
    total_calls bigint,
    total_call_minutes bigint
);
insert into @t_call
    SELECT  a_id, COUNT(1) AS total_calls, isnull(SUM(DATEDIFF(MINUTE, c_time_in, c_time_out)),0) AS total_call_minutes
      FROM t_actress aa
            join t_call_log l on aa.caller_id = l.caller_id and c_time_in BETWEEN @time_from AND @time_to and c_time_out IS NOT NULL
      GROUP BY a_id;

-- total live minutes
declare @t_live table(
    a_id bigint,
    live_calls bigint,
    live_call_minutes bigint
);
insert into @t_live
    SELECT a_id, COUNT(*) AS live_calls, isnull(SUM(alm_minutes),0) AS live_call_minutes
      FROM t_actress a
          join t_actress_live_minute alm on alm.actress_id = a.a_id and alm_time BETWEEN @time_from AND @time_to
      GROUP BY a_id 

-- total message by caller
declare @t_cm table(
    caller_id bigint,
    pmsg_sent bigint,
    pmsg_received bigint,
    lcmsg_sent bigint,
    lcmsg_received bigint
)
insert into @t_cm
    SELECT l.caller_id,
            COUNT(CASE WHEN m.call_log_id       = l.c_id AND m.m_type = 2 THEN 1 END) AS _pmsg_sent,
            COUNT(CASE WHEN m.m_to_call_log_id  = l.c_id AND m.m_type = 2 THEN 1 END) AS _pmsg_received,
            COUNT(CASE WHEN m.call_log_id       = l.c_id AND m.m_type = 1 THEN 1 END) AS _lcmsg_sent,
            COUNT(CASE WHEN m.m_to_call_log_id  = l.c_id AND m.m_type = 1 THEN 1 END) AS _lcmsg_received
          FROM t_message m
            join t_call_log l on l.c_id in (m.call_log_id, m.m_to_call_log_id)  
          where m.m_time BETWEEN @time_from AND @time_to
      GROUP BY l.caller_id

-- total message by actress
declare @t_msg table(
    a_id bigint,
    pmsg_sent bigint,
    pmsg_received bigint,
    lcmsg_sent bigint,
    lcmsg_received bigint
)
insert into @t_msg
    select a_id, isnull(SUM(cm.pmsg_sent),0), isnull(SUM(cm.pmsg_received),0), isnull(SUM(cm.lcmsg_sent),0), isnull(SUM(cm.lcmsg_received),0)
        from t_actress a
            join @t_cm cm on a.caller_id = cm.caller_id
    group by a_id

-- total credit
declare @t_credit table(
    a_id bigint,
    credit money,
    paid_credit money
)   
insert into @t_credit
    SELECT a_id,    isnull(SUM(ac.ac_amount),0) AS credit, isnull(SUM(CASE ac.ac_is_paid WHEN 1 THEN ac.ac_amount else 0 END),0) AS paid_credit
        FROM t_actress a
            join t_actress_credit ac on ac.actress_id = a.a_id AND ac.ac_time BETWEEN @time_from AND @time_to
            JOIN t_order o ON o.o_id = ac.order_id and o_status = 1
    GROUP BY a_id   

-- the report       
select a.a_id, cl.c_id, cl.c_chat_line_id, a.a_first_name, a.a_last_name, 
        isnull(ac.credit,0) credit, isnull(ac.paid_credit,0) paid_credit, 
        isnull(m.pmsg_sent,0) pmsg_sent, isnull(m.pmsg_received,0) pmsg_received, isnull(m.lcmsg_sent,0) lcmsg_sent, isnull(m.lcmsg_received,0) lcmsg_received,
        isnull(l.live_calls,0) live_calls, isnull(l.live_call_minutes,0) live_call_minutes,
        isnull(c.total_calls,0) total_calls, isnull(c.total_call_minutes,0) total_call_minutes
from t_actress a
    join t_caller cl on cl.c_id = a.caller_id
    left outer join @t_call c on c.a_id = a.a_id
    left outer join @t_live l on l.a_id = a.a_id                
    left outer join @t_msg m on m.a_id = a.a_id
    left outer join @t_credit ac on ac.a_id = a.a_id
order by a_id
于 2013-03-14T18:14:10.220 回答