0

我有一个需要很长时间的存储过程,因为我有 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)
4

1 回答 1

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的 s 是不正确的。您应该使用该QuoteName功能。这将确保您的系统不会在 13 年后输入任何疯狂的值(想想'Taiwan [North]'):

STUFF((SELECT DISTINCT ',' + QuoteName(Region) FROM #Temp ...

笔记:

既然您说这是在网页中显示,您甚至不需要在服务器上进行旋转。相反,向客户端返回 2 个行集,一个带有站点数据,一个带有区域的列数据。您需要对行集中的每一行进行额外的遍历,Region以找出所有需要的区域,但这可以很快完成。最后,调整您的程序代码以根据每个匹配站点的需要逐步遍历区域行,并创建您的输出。

值得投资的一个原因是,如果您的应用程序规模扩大,您总是可以将另一台 Web 服务器投入使用,但投入另一台数据库则要困难得多。一个新的 Web 服务器的成本将低于不断增强 SQL Server 的成本。

PS即使是动态SQL,只要格式化好,也更容易处理。:)

于 2013-06-13T18:32:26.667 回答