我有一个存储过程需要很长时间,因为我有2个函数调用在PIVOT之前调用,这意味着它为每个记录调用函数5次而不是每个记录调用一次。如何重写我的查询,以便在查询结束时正确的2函数调用在Pivot之后而不是之前运行?
这是查询
CREATE TABLE #Temp
(
ServiceRecordID INT,
LocationStd VARCHAR(1000),
AreaServedStd VARCHAR(1000),
RegionalLimited BIT,
Region VARCHAR(255),
Visible BIT
)
DECLARE @RegionCount INT
SELECT @RegionCount = COUNT(RegionID) FROM Regions WHERE SiteID = @SiteID AND RegionID % 100 != 0
INSERT INTO #Temp
SELECT TOP (@RegionCount * 100) SR.ServiceRecordID, SR.LocationStd, SR.AreaServedStd, SR.RegionalLimited, R.Region,
CASE WHEN (ISNULL(R_SR.RegionID,0) = 0 AND ISNULL(R_SR_Serv.RegionID,0) = 0) THEN 0 ELSE 1 END AS Visible
FROM ServiceRecord SR
INNER JOIN Sites S ON SR.SiteID = S.SiteID
INNER JOIN Regions R ON R.SiteID = S.SiteID
LEFT OUTER JOIN lkup_Region_ServiceRecord R_SR ON R_SR.RegionID = R.RegionID AND R_SR.ServiceRecordID = SR.ServiceRecordID
LEFT OUTER JOIN lkup_Region_ServiceRecord_Serv R_SR_Serv ON R_SR_Serv.RegionID = R.RegionID AND R_SR_Serv.ServiceRecordID = SR.ServiceRecordID AND SR.RegionalLimited = 0
WHERE SR.SiteID = @SiteID
AND R.RegionID % 100 != 0
ORDER BY SR.ServiceRecordID
DECLARE @RegionList varchar(2000),@SQL varchar(max)
SELECT @RegionList = STUFF((SELECT DISTINCT ',[' + Region + ']' FROM #Temp ORDER BY ',[' + Region + ']' FOR XML PATH('')),1,1,'')
SET @SQL='SELECT * FROM
(SELECT ServiceRecordID,
dbo.fn_ServiceRecordGetServiceName(ServiceRecordID,'''') AS ServiceName,
LocationStd,
AreaServedStd,
RegionalLimited,
Region As Region,
dbo.fn_GetOtherRegionalSitesForServiceRecord(ServiceRecordID) AS OtherSites,
CAST(Visible AS INT) AS Visible FROM #Temp) B PIVOT(MAX(Visible) FOR Region IN (' + @RegionList + ')) A'
EXEC(@SQL)
答案 0 :(得分:0)
在PIVOT之后移动函数调用:
SET @SQL='
SELECT
A.*,
N.ServiceName,
S.OtherSites
FROM
(
SELECT
ServiceRecordID,
LocationStd,
AreaServedStd,
RegionalLimited,
Region,
CAST(Visible AS INT) AS Visible
FROM #Temp
) B
PIVOT(MAX(Visible) FOR Region IN (' + @RegionList + ')) A
OUTER APPLY (
SELECT dbo.fn_ServiceRecordGetServiceName(A.ServiceRecordID,'''')
) N (ServiceName)
OUTER APPLY (
SELECT dbo.fn_GetOtherRegionalSitesForServiceRecord(A.ServiceRecordID)
) S (OtherSites);
';
或者只是将它们放在外部的SELECT:
中SET @SQL='
SELECT
A.*,
ServiceName = dbo.fn_ServiceRecordGetServiceName(A.ServiceRecordID,''''),
OtherSites = dbo.fn_GetOtherRegionalSitesForServiceRecord(A.ServiceRecordID)
FROM
(
SELECT
ServiceRecordID,
LocationStd,
AreaServedStd,
RegionalLimited,
Region,
CAST(Visible AS INT) AS Visible
FROM #Temp
) B
PIVOT(MAX(Visible) FOR Region IN (' + @RegionList + ')) A
';
如果你可以将这些函数转换为由单个SELECT语句组成的表值行集返回,那么你也可以获得巨大的性能提升。
CREATE FUNCTION dbo.fn_ServiceRecordGetServiceName2(
@ServiceRecordID itn
)
RETURNS TABLE
AS
RETURN ( -- single select statement
SELECT ServiceName = Blah
FROM dbo.Gorp
WHERE Gunk = 'Ralph'
);
然后
OUTER APPLY dbo.fn_ServiceRecordGetServiceName(ServiceRecordID,'''') N
N.ServiceName
将返回值。
此外,使用方括号将数据值转换为有效sysname
是不正确的。您应该使用QuoteName
功能。无论13年后输入什么疯狂价值,这都将确保您的系统不会中断(想想'Taiwan [North]'
):
STUFF((SELECT DISTINCT ',' + QuoteName(Region) FROM #Temp ...
注意:
由于您说这是为了在网页中显示,您甚至不需要在服务器上进行透视。而是将2个行集返回给客户端,一个包含Site数据,另一个包含Regions的列数据。您 需要在Region
行集中的每一行进行额外的传递,以找出所需的所有区域,但这可以非常快速地完成。最后,根据每个匹配站点的需要调整程序代码以逐步浏览Region行,并创建输出。
值得投资的一个原因是,如果您的应用程序的大小增加,您可以随时抛出另一个Web服务器来解决问题,但是 lot 更难以在其上引入另一个数据库。新的Web服务器的成本低于持续增强SQL Server的成本。
P.S。格式化时,即使是动态SQL也更容易处理。 :)