0

PIVOT の前に呼び出される 2 つの関数呼び出しがあるため、非常に長い時間がかかるストアド プロシージャがあります。これは、レコードごとに 1 回ではなく、レコードごとに関数を 5 回呼び出すことを意味します。クエリの最後にある 2 つの関数呼び出しがピボットの前ではなく後に実行されるようにクエリを書き直すにはどうすればよいですか?

ここにクエリがあります

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
';

これらの関数を、1 つの 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、値を返します。

また、データ値を有効な s に変換するために角括弧を追加するのは正しくありませんsysname。関数を使用する必要がありQuoteNameます。これにより、13 年後にどんなクレイジーな値が入力されても、システムが壊れないようになります (考えてみて'Taiwan [North]'ください)。

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

ノート:

これは Web ページに表示するためのものだとおっしゃっていたので、サーバー上でピボットを行う必要さえありません。代わりに、2 つの行セットをクライアントに返します。1 つはサイト データを含み、もう 1 つは地域の列データを含みます。必要なすべての領域を見つけるには、行セット内のすべての行を通過する追加のパスRegion必要になりますが、これは非常に迅速に行うことができます。最後に、一致するサイトごとに必要に応じて地域の行をステップスルーするようにプログラム コードを調整し、出力を作成します。

これが投資に値する理由の 1 つは、アプリケーションのサイズが大きくなった場合、いつでも別の Web サーバーを問題に投入できますが、別のデータベースを投入するのははるかに難しいからです。新しい Web サーバーのコストは、SQL Server を継続的に強化するよりも低くなります。

PS 動的 SQL でさえ、適切にフォーマットすると扱いやすくなります。:)

于 2013-06-13T18:32:26.667 に答える