2016-10-31 5 views
1

従業員の不在の詳細に関するレコードを含むテーブルがあります。既存の行が更新される代わりに不在が更新されるとき、代わりに新しい行が作成され、前のレコードのLinkedRecordIdフィールドに新しいレコードのIDが設定されます.LinkedRecordIdフィールドのNULL値は、そのレコードがチェーン内で最新であることを示します。複数のリンクされたレコードの複数の文字列をレコードチェーンごとに1つの文字列に連結する効率的な方法

各親レコードのメモを1つの文字列に結合したすべての不在レコードのデータセットを取得し、チェーンの最新レコードの他のフィールドをリストする必要があります。私の問題は、パフォーマンスある

CREATE TABLE [dbo].[AbsenceData](
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [Notes] [varchar](max) NULL, 
    [LinkedRecordId] [int] NULL, 
    [CreatedAt] [datetime] NULL 

) 

;:ここ

は、データを含むテーブルのカットダウン版です現在の実装のカットダウン版ここで

CREATE FUNCTION [dbo].[AbsenceNotesFor](@AbsenceDataId INT) 
RETURNS @return TABLE 
(
    AbsenceDataId INT 
    ,Notes VARCHAR(MAX) 
) 
AS 
BEGIN 
    DECLARE @notes VARCHAR(MAX) 
    ;WITH AbsenceNotes AS (
     SELECT 
      ad.Id 
      ,ad.Notes 
      ,ad.CreatedAt 
     FROM 
      AbsenceData ad WITH (NOLOCK) 
     WHERE 
      ad.Id = @absenceDataId 
     UNION ALL 
     SELECT 
      ad.Id 
      ,ad.Notes 
      ,ad.CreatedAt 
     FROM 
      AbsenceData ad WITH (NOLOCK) 
      INNER JOIN AbsenceNotes an ON an.Id = ad.LinkedRecordId 
    ) 

    SELECT @notes = CONVERT(VARCHAR(11),CreatedAt, 105) + ' ' + CONVERT(VARCHAR(5),CreatedAt, 114)+ CHAR(13)+CHAR(10) + CAST(Notes AS VARCHAR(MAX)) + CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10) + COALESCE(@notes,'') 
    FROM AbsenceNotes 

    INSERT INTO @return 
     SELECT AbsenceDataId = @AbsenceDataId, Notes = @notes 

    RETURN; 
END 

されています:

SELECT 
    Id 
    ,n.Notes  
FROM AbsenceData 
    CROSS APPLY dbo.AbsenceNotesFor(Id) n 
WHERE 
    LinkedRecordId IS NULL 
私の現在のソリューションを収集し、チェーン内のすべての親レコードから文字列を連結し、次のテーブル値関数を使用することです

これを数百レコードのデータセットに対して実行すると、テーブル値関数内のロジックに起因すると思われるパフォーマンスの問題が既に表示されます。

私はこれを行うより効率的な方法をお探しですか?

は、我々はここでMS SQL Serverの2016標準

を使用してSQLフィドルの一例であるされています。マルチステートメントの構文http://sqlfiddle.com/#!6/b9834

+0

テーブル価値関数 - 特にこのような複雑なものは、パフォーマンスを低下させるでしょう。あなたのやりたいことをする良い方法があるかもしれないので、あなたはあなたの質問を編集し、サンプルデータと望ましい結果を提供するべきです。 –

答えて

1

表値関数がよく、絶対に悪いパフォーマンスで知られています。可能であれば、BEGINENDのTVF構文を避けるべきです。

インラインまたはad-hoc機能と同じ機能がさらに優れています。サンプルデータがなければ、これは盲目飛行ですが、私はあなたが同じ結果になるだろうと思うが、これとはるかに優れた性能:

CREATE FUNCTION [dbo].[AbsenceNotesFor](@AbsenceDataId INT) 
RETURNS TABLE 
AS 
    RETURN 
    WITH AbsenceNotes AS (
     SELECT 
      ad.Id 
      ,ad.Notes 
      ,ad.CreatedAt 
     FROM 
      AbsenceData ad WITH (NOLOCK) 
     WHERE 
      ad.Id = @absenceDataId 
     UNION ALL 
     SELECT 
      ad.Id 
      ,ad.Notes 
      ,ad.CreatedAt 
     FROM 
      AbsenceData ad WITH (NOLOCK) 
      INNER JOIN AbsenceNotes an ON an.Id = ad.LinkedRecordId 
    ) 

    SELECT @AbsenceDataId AS AbsenceDataId 
     ,(
      REPLACE 
      ( 
        STUFF 
        (
         (
         (
          SELECT '|#|'+ '|#|' 
             + CONVERT(VARCHAR(11),CreatedAt, 105) + ' ' 
             + CONVERT(VARCHAR(5),CreatedAt, 114) 
             + '|#|' 
             + CAST(Notes AS VARCHAR(MAX)) 
          FROM AbsenceNotes 
          FOR XML PATH(''),TYPE 
         ).value('.','nvarchar(max)') 
        ),1,6,'' 
       ),'|#|',CHAR(13)+CHAR(10) 
      ) 
     ) AS Notes; 

短い説明:

私はあなたのCTEに触れませんでした。

SELECT @[email protected] + Somethingの文字列連結は非常に悪い実行手順のアプローチです。私はこれをFOR XML PATH('')に置き換えました。 Group concatとSql-Serverを検索すると、これについて多くを見つけることができます。

後で問題を避けるために改行を魔法値(|#|)に置き換えました。

STUFF機能が先頭に6つの文字を切り取っほかなら(改行のための2回の魔法値)

REPLACE機能が戻って本当の改行に魔法の値を変更しません。

ノートを降順(コードはこのように見えます)にするには、適切なORDER BYを内側のSELECTに追加します。

+0

こんにちはShnugo、あなたの応答と解決に感謝します。この方法は間違いなく速く、約4倍の速さです! TVFを完全に排除する方法があるのだろうか? – Chriz

+0

まあ、*インラインTVF *は - だれもそう考えていなかった! - インライン。これをTVFとして書くか、同じ場所に書くかは、外部(呼び出し)ステートメントに直接書き込むかどうかにはあまり違いはありません。パフォーマンスがまだ問題である場合は、新しい質問を開始することをお勧めします。あなたの目標についての詳細を提供し、[MCVE(テーブル、サンプルデータ、期待される出力)](http://stackoverflow.com/help/mcve)を提供してください。この新しい質問へのリンクをここに置くと、すぐにそこにポップアップします(SOの支援軍と共に) – Shnugo

0

さらに大きなセットでパフォーマンスの問題がありますが、実行計画を詳細に確認した後でも、キーフィールドがIdであり、含まれているフィールドがNotesCreatedAtである欠落インデックスを追加すると、パフォーマンスが大幅に向上しました。しかし、私はこれが賢明ではないと思うかもしれません、おそらく私はこの主題に関して別の質問を提起する必要があります...

関連する問題