SQL Server - 如何优化此查询?
Posted
技术标签:
【中文标题】SQL Server - 如何优化此查询?【英文标题】:SQL Server - how can this query be optimized? 【发布时间】:2013-07-22 18:36:01 【问题描述】:此查询平均需要 4 秒。它将成为存储过程中的子查询,我需要它花费一秒。这是查询:
(select customercampaignname + ' $' + convert(varchar, cast(amount as numeric(36,2) ) ) As 'Check_Stub_Comment2' from (
select ROW_NUMBER() OVER (ORDER BY amount desc) as rownumber, customercampaignname, amount from (
select * from (
select distinct d.customercampaignname
,sum(d.mastercurrencyamount) As amount
from bb02_donation d
JOIN bb02_donationline dl on d.donationid = dl.donationid
JOIN bb02_fundraiserrevenuestream frs on dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid and frs.fundraiserid = 1869
where d.customercampaignname is not null
and d.customercampaignname != ''
group by d.CustomerCampaignName
) as x
) as sub ) as y where rownumber = 1)
【问题讨论】:
澄清 - “where rownumber =”很重要。我需要能够仅选择第 1 行,或仅选择第 2 行。 我在下面标记的答案确实使这个查询更快,但是它仍然是对数据库的 n+ 调用。就我而言,我只关心 2 个结果,所以我将它们存储到一个临时表中,只需调用一次数据库。然后我能够从临时表中取出前 1 个结果,对其进行排序,然后再次取出前 1 个结果。这将我的总查询时间减少了一半。谢谢大家的回复。 【参考方案1】:如果您实际上不需要将行号用于任何事情,那么我会选择 TOP 1 行。查询可以简化很多。我喜欢首先从表中选择最能被您的谓词过滤掉的表。希望您对 frs.fundraiserid 以及参与连接的所有列都有一个良好的索引。
SELECT
TOP 2 customercampaignname + ' $' + CONVERT(VARCHAR(255), CAST(SUM(d.mastercurrencyamount) AS NUMERIC(36,2) ) ) AS 'Check_Stub_Comment2',
ROW_NUMBER() OVER(ORDER BY SUM(d.mastercurrencyamount) DESC) as rownumber
FROM bb02_fundraiserrevenuestream frs
JOIN bb02_donationline dl ON
dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid
JOIN bb02_donation d ON
d.donationid = dl.donationid
WHERE frs.fundraiserid = 1869
AND d.customercampaignname IS NOT NULL
AND d.customercampaignname != ''
GROUP BY d.CustomerCampaignName
ORDER BY SUM(d.mastercurrencyamount) DESC
由于您需要能够选择第一行或第二行,因此将其包装在 CTE 或子查询中。
WITH topcampaign AS (
SELECT
TOP 2 customercampaignname + ' $' + CONVERT(varchar(255), CAST(SUM(d.mastercurrencyamount) AS NUMERIC(36,2) ) ) AS 'Check_Stub_Comment2',
ROW_NUMBER() OVER(ORDER BY SUM(d.mastercurrencyamount) DESC) as rownumber
FROM bb02_fundraiserrevenuestream frs
JOIN bb02_donationline dl ON
dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid
JOIN bb02_donation d ON
d.donationid = dl.donationid
WHERE frs.fundraiserid = 1869
AND d.customercampaignname IS NOT NULL
AND d.customercampaignname != ''
GROUP BY d.CustomerCampaignName
ORDER BY SUM(d.mastercurrencyamount) DESC
)
SELECT * from topcampaign WHERE rownumber = 1
作为另一种可能的优化,我将 CONVERT 从 CTE 中取出并放入最终选择中。不确定这是否有很大帮助。
WITH topcampaign AS (
SELECT
TOP 2
customercampaignname,
SUM(d.mastercurrencyamount) AS amount,
ROW_NUMBER() OVER(ORDER BY SUM(d.mastercurrencyamount) DESC) as rownumber
FROM bb02_fundraiserrevenuestream frs
JOIN bb02_donationline dl ON
dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid
JOIN bb02_donation d ON
d.donationid = dl.donationid
WHERE frs.fundraiserid = 1869
AND d.customercampaignname IS NOT NULL
AND d.customercampaignname != ''
GROUP BY
d.CustomerCampaignName
ORDER BY
SUM(d.mastercurrencyamount) DESC
)
SELECT
rownumber,
customercampaignname + ' $' + CONVERT(varchar(255), CAST(amount AS NUMERIC(36,2) ) ) AS 'Check_Stub_Comment2'
FROM topcampaign
WHERE rownumber = 1
【讨论】:
谢谢 - 我实际上需要行号,以便我可以选择第一行或第二行。 Top 1 可以,但我仍然需要一种方法来查询第二行。 我编辑了答案以将 ROW_NUMBER 添加到输出中。您可以将 TOP 更改为 TOP N 其中 N 是任何有效整数。所以如果你只需要top 2,还需要rownumber,那就改成TOP 2吧。 让我再解释一下。我需要它要么返回第 1 行,要么只返回第 2 行。所以,我需要说“where rownumber = 2”并且只得到结果集中的第 2 行。我现在正在尝试使用上面的 sql 来实现这一点。 我能够用 select * from (-your sql') as x where rownumber = 2 包装你的 sql 运行时间不到 1 秒,因此目前看来很有希望。【参考方案2】:我不知道这是否会运行得更快,但根据我的估计,这可以简化为:
Select top 1 customercampaignname + ' $' + convert(varchar(255), cast(sum(d.mastercurrencyamount) as numeric(36,2) ) ) As amount
from bb02_donation d JOIN
bb02_donationline dl
on d.donationid = dl.donationid JOIN
bb02_fundraiserrevenuestream frs
on dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid and frs.fundraiserid = 1869
where d.customercampaignname is not null and d.customercampaignname != ''
group by d.CustomerCampaignName
order by sum(d.mastercurrencyamount)
【讨论】:
【参考方案3】:我不知道这是否有什么不同
;WITH cte AS
(
select *, ROW_NUMBER() OVER (ORDER BY amount desc) as rownumber
from (
select distinct d.customercampaignname, sum(d.mastercurrencyamount) As amount
from bb02_donation d
INNER JOIN bb02_donationline dl on d.donationid = dl.donationid
INNER JOIN bb02_fundraiserrevenuestream frs on dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid and frs.fundraiserid = 1869
where d.customercampaignname is not null
and d.customercampaignname != ''
group by d.CustomerCampaignName
) as x
)
SELECT TOP 1 *
FROM cte
ORDER BY RowNumber
如果表格非常好,您可能需要帮助过滤掉一些东西,例如,您可以只选择 bb02_fundraiserrevenuestream.fundraiserid = 1869,然后执行其余的查询。 我不知道,这可能是一个尝试并查看执行计划的问题。
;WITH Frs AS
( SELECT dl.fundraiserrevenuestreamid
FROM bb02_fundraiserrevenuestream
WHERE fundraiserid = 1869
)
, cte AS (
select d.customercampaignname, sum(d.mastercurrencyamount) As amount
from Frs
INNER JOIN bb02_donationline dl ON dl.fundraiserrevenuestreamid = frs.fundraiserrevenuestreamid
INNER JOIN bb02_donation d on d.donationid = dl.donationid
where d.customercampaignname is not null
and d.customercampaignname != ''
group by d.CustomerCampaignName
)
SELECT TOP 1 customercampaignname + ' $' + convert(varchar, cast(amount as numeric(36,2) ) ) As 'Check_Stub_Comment2'
FROM cte
ORDER BY sum(d.mastercurrencyamount)
与往常一样,INDEXES
可能是这些性能问题的解决方案。
【讨论】:
以上是关于SQL Server - 如何优化此查询?的主要内容,如果未能解决你的问题,请参考以下文章