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 式を使用) を使用できます。

あなたのクエリには、そのようなグループが 4 つ、おそらく 5 つ表示されます。ここでは、4 つのサブクエリを使用するように書き直されています。

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サブクエリを別々に結合して 2 つに分割すると、5 つのサブクエリになる可能性が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 に答える