我有一个报告的SQL查询,它包含一些子查询。它运行得很慢。我尝试了几种方法(比如使用join而不是子查询,添加一些索引)。但他们都没有工作。这是查询:
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
任何人都可以提出一些建议吗?
非常感谢!
艾伦
答案 0 :(得分: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是您之前尝试使用连接重写查询的剩余部分。
答案 1 :(得分: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