web-dev-qa-db-ja.com

ページング、パフォーマンス、最適化を備えた動的SQLクエリ

目の前に面白い問題があります。年間100万から200万の成長が見込まれる100万ユーザーアカウントのデータベースがあります。データベースは強力なTPTですが、この特定のクエリと関連するテーブルはTPTコンテンツのいずれにも触れません。

Sprocとビューの現在の設計では、2番目のデータポイント(電子メールアドレス、姓、会社など)を指定するときに、実行に最大15秒(x2)かかります。データベースはSQL Azure P11ですが、DTUバインドクエリではなく、利用可能な最高のオファリング(P15)にアップグレードしても結果に影響はありません。

以下は、sproc、ビュー、および実行計画です。すべてのインデックスが過去24時間以内に再構築または再編成され、すべての統計が更新されました。たとえば、データを見る場合、履歴メールアドレス(1..N)の概念は現在CROSS APPLY最新のものを取得します。これにより、インデックス付きビューが回避され、履歴電子メールアドレスを単純に連結して解決し、それらを単一の列に保持できます。

多くのデータベースは、nvarchar(4000-max)列でJSONを利用します。これらの列にはすべて、計算された列が値を公開し、インデックスを有効にします。パラダイムはページングをサポートする必要があり、それを最適化する方法に関するフィードバック/アドバイスを探しています。

テーブル構造を変更することは、現時点では実行可能なオプションではありませんが、少し操作するだけで前向きなパスを確認できます。私が最初にどこを見るべきかについて誰かが何か考えを持っていますか?私は、unknownの再コンパイルと最適化の両方をテストして、影響があったかどうかを確認しました。

注:一部のビジネスロジック(独自の列名は削除または変更されています。sprocおよびビューはそのままでは実行できませんが、機能的にはソースと同じです。

実行計画 および ビュー

Sproc

CREATE PROCEDURE [dbo].[spGetUserDetailsDynamic] @JsonFilter NVARCHAR(MAX)
AS /* Page number*/
DECLARE @Page AS INT = JSON_VALUE(@JsonFilter, '$.requestPaging.page');
/* Number of records on the page*/
DECLARE @Size AS INT = JSON_VALUE(@JsonFilter, '$.requestPaging.size');

IF (@Page = -1)
    SET @Page = 1;

IF (@Size = -1)
    SET @Size = 32767;

/* Sort direction ASC or DESC*/
DECLARE @SortDirection AS VARCHAR(10) = JSON_VALUE(@JsonFilter, '$.requestSorting.direction');
/* Order By Column */
DECLARE @SortColumn AS VARCHAR(200) = JSON_QUERY(@JsonFilter, '$.requestSorting.keys');

SET @SortColumn = REPLACE(@SortColumn, '"', '');
SET @SortColumn = REPLACE(@SortColumn, '[', '');
SET @SortColumn = REPLACE(@SortColumn, ']', '');

/* Filters*/
DECLARE @RequestorApplicationIdFilterValue AS TINYINT
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.applicationIdValue');

DECLARE @CompanyAssignedKeyFilterValue AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.companyAssignedKeyValue');
DECLARE @CompanyAssignedKeyFilterOperation AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.companyAssignedKeyOperation');


DECLARE @EmailFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.emailValue');
DECLARE @EmailFilterOperation AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.emailOperation');


DECLARE @LastNameFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.lastNameValue');
DECLARE @LastNameFilterOperation AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.lastNameOperation');


DECLARE @FirstNameFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.firstNameValue');
DECLARE @FirstNameFilterOperation AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.firstNameOperation');


DECLARE @PhoneNumberFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.phoneNumberValue');
DECLARE @PhoneNumberFilterOperation AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.phoneNumberOperation');

DECLARE @StreetAddressFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.streetAddressValue');
DECLARE @StreetAddressFilterOperation AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.streetAddressOperation');

DECLARE @CityFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.cityValue');
DECLARE @CityFilterOperation AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.cityOperation');

DECLARE @RegionFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.regionValue');
DECLARE @RegionFilterOperation AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.regionOperation');

DECLARE @PostalCodeFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.postalCodeValue');
DECLARE @PostalCodeFilterOperation AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.postalCodeOperation');

DECLARE @CountryFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.countryValue');


DECLARE @HasCompletedRegistrationFilterValue AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.completedRegistrationValue');
DECLARE @HasCompletedRegistrationBitValue AS BIT = NULL;

IF (@HasCompletedRegistrationFilterValue IS NOT NULL)
BEGIN
    IF (@HasCompletedRegistrationFilterValue = 'true')
        SET @HasCompletedRegistrationBitValue = 1;
    ELSE
        SET @HasCompletedRegistrationBitValue = 0;
END;


DECLARE @HasApplicationAccountsFilterValue AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.haveApplicationAccountsValue');

DECLARE @HasApplicationAccountsBitValue AS BIT = NULL;

IF (@HasApplicationAccountsFilterValue IS NOT NULL)
BEGIN
    IF (@HasApplicationAccountsFilterValue = 'true')
        SET @HasApplicationAccountsBitValue = 1;
    ELSE
        SET @HasApplicationAccountsBitValue = 0;
END;

DECLARE @HasProcessorAccountsFilterValue AS NVARCHAR(200)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.haveProcessorAccountsValue');

DECLARE @HasProcessorAccountsBitValue AS BIT = NULL;

IF (@HasProcessorAccountsFilterValue IS NOT NULL)
BEGIN
    IF (@HasProcessorAccountsFilterValue = 'true')
        SET @HasProcessorAccountsBitValue = 1;
    ELSE
        SET @HasProcessorAccountsBitValue = 0;
END;


DECLARE @TargetApplicationIdFilterValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.userTypeValue');


DECLARE @CompanyPublicIdValue AS NVARCHAR(200) = JSON_VALUE(@JsonFilter, '$.requestFiltering.companyPublicIdValue');
DECLARE @CompanyUserIdValue INT = NULL;

IF (@CompanyPublicIdValue IS NOT NULL)
BEGIN
    SET @CompanyUserIdValue =
    (
        SELECT UserId
        FROM Application.Users
        WHERE UserPublicId = @CompanyPublicIdValue
    );
END;


DECLARE @AccountPublicIdFilterValue AS VARCHAR(32)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.accountPublicIdValue');

DECLARE @AccountIdFilterValue AS INT = JSON_VALUE(@JsonFilter, '$.requestFiltering.accountIdValue');



-- User Public ID
DECLARE @UserPublicIdFilterValue AS VARCHAR(32) = JSON_VALUE(@JsonFilter, '$.requestFiltering.userPublicIdValue');
DECLARE @UserIdFilterValue AS INT = JSON_VALUE(@JsonFilter, '$.requestFiltering.userIdValue');
IF (@UserPublicIdFilterValue IS NOT NULL AND @UserIdFilterValue IS NULL)
    SET @UserIdFilterValue =
(
    SELECT UserId
    FROM Application.Users
    WHERE UserPublicId = @UserPublicIdFilterValue
)   ;

DECLARE @UserRegistrationProgressStateFilterValue AS VARCHAR(32)
    = JSON_VALUE(@JsonFilter, '$.requestFiltering.userRegistrationProgressStateValue');
DECLARE @UserRegistrationProgressStateValue AS NVARCHAR(200) = NULL;

IF (@UserRegistrationProgressStateFilterValue IS NOT NULL)
BEGIN
    IF (@UserRegistrationProgressStateFilterValue = '4')
        SET @UserRegistrationProgressStateValue = '1,2';
    ELSE
        SET @UserRegistrationProgressStateValue = @UserRegistrationProgressStateFilterValue;
END;

DECLARE @TotalRecordCount INT;

DECLARE @WHERE AS NVARCHAR(MAX) = '';
DECLARE @GROUPBY AS NVARCHAR(MAX) = '';


DECLARE @SQLViewCount AS NVARCHAR(MAX)
    = '
    SELECT  @_TotalRecordCount = SUM(S.Total)
          FROM    (
                  SELECT    1 AS Total
                  FROM      Application.vwUserDetails T ';

DECLARE @SQL AS NVARCHAR(MAX)
    = '
    SELECT [Application_UserInvitations_ApplicationId]
      ,[Application_UserInvitations_EmailAddress]
      ,[Application_UserInvitations_InvitationStatusTypeId]
      ,[Application_UserInvitations_InvitationKey]
      ,[Application_UserInvitations_CompanyUserId]
      ,[Application_UserInvitations_Created]
      ,[Application_UserInvitations_Expires]
      ,[Application_Users_UserPublicId]
      ,[Application_Users_UserId]
      ,[Application_Users_UserCompanyAssignedKey]
      ,[Application_Users_FirstName]
      ,[Application_Users_LastName]
      ,[Application_Users_Created]
      ,[Application_Users_CompanyUserId]
      ,[Application_Users_ApplicationId]
      ,[Application_UserEmailAddresses_VerifiedEmailAddress]
      ,[Application_UserEmailAddresses_UnverifiedEmailAddress]
      ,[Application_UserInvitationRegistrationInformation_UserCompanyAssignedKey]
      ,[Application_UserInvitationRegistrationInformation_FirstName]
      ,[Application_UserInvitationRegistrationInformation_LastName]
      ,[Application_Companies_Name]
      ,[Application_Companies_UserTitleSubscriptionKey]
      ,[Application_Companies_CompanyUserId]
      ,[Application_Companies_CompanyId]
      ,[Application_Users_EvaluatedEmailAddress]
      ,[Application_Users_EvaluatedFirstName]
      ,[Application_Users_EvaluatedLastName]
      ,[Application_Users_EvaluatedUserCompanyAssignedKey]
      ,[Application_UserProfiles_PrimaryPhoneNumber]
      ,[Application_UserProfiles_MobilePhoneNumber]
      ,[UserHasApplicationAccount]
      ,[UserHasFinancialProcessorAccount]
      ,[Application_RegisterDate]
      ,[Application_Registration_Progress]
      ,[Application_Evaluated_User_Registration_Progress_Step]
      ,[Application_EvaluatedPhoneNumberCountry]
  FROM [Application].[vwUserDetails] T ';


DECLARE @SQLOrderBy AS NVARCHAR(200)
    = '

    ORDER BY 
        ' + ' ' + @SortColumn + ' ' + @SortDirection
      + '
    OFFSET (@_Page-1)*@_Size ROWS
        FETCH NEXT @_Size ROWS ONLY
    ';



/* Based off of requesting application, build the where clause, first with application specific filters, followed by general.*/
IF (
       @RequestorApplicationIdFilterValue IS NULL
       OR @RequestorApplicationIdFilterValue NOT IN ( 1, 2, 3 )
   )
BEGIN
    THROW 60000, 'ApplicationIdFilterValue is null or out of range', 1;
END;



IF (@CountryFilterValue IS NOT NULL)
BEGIN
    SET @WHERE
        = @WHERE + 'AND Application_UserAccountAddressCountryCode IN (''' + CAST(@CountryFilterValue AS VARCHAR(100))
          + ''') ';
END;

IF (@TargetApplicationIdFilterValue IS NOT NULL)
BEGIN
    SET @WHERE
        = @WHERE + 'AND Application_UserInvitations_ApplicationId IN ('
          + CAST(@TargetApplicationIdFilterValue AS VARCHAR(100)) + ') ';
END;

IF (@CompanyUserIdValue IS NOT NULL)
BEGIN
    SET @WHERE
        = @WHERE + 'AND Application_UserInvitations_CompanyUserId IN (' + CAST(@CompanyUserIdValue AS VARCHAR(100)) + ') ';
END;

IF (@HasProcessorAccountsBitValue IS NOT NULL)
BEGIN
    SET @WHERE
        = @WHERE + 'AND UserHasFinancialProcessorAccount = ' + CAST(@HasProcessorAccountsBitValue AS VARCHAR(100))
          + ' ';
END;

IF (@HasApplicationAccountsBitValue IS NOT NULL)
BEGIN
    SET @WHERE = @WHERE + 'AND UserHasApplicationAccount = ' + CAST(@HasApplicationAccountsBitValue AS VARCHAR(100)) + ' ';
END;

IF (@UserRegistrationProgressStateValue IS NOT NULL)
BEGIN
    SET @WHERE = @WHERE + 'AND Application_Registration_Progress IN (' + @UserRegistrationProgressStateValue + ') ';
END;

/* Fuzzy Text Search */
IF (@EmailFilterValue IS NOT NULL AND @EmailFilterOperation IS NOT NULL)
BEGIN
    -- IsEqualTo
    IF (@EmailFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedEmailAddress = ''' + @EmailFilterValue + ''')';
    -- StartsWith
    ELSE IF (@EmailFilterOperation = 2)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedEmailAddress LIKE ''' + @EmailFilterValue + '%'')';
    -- Contains
    ELSE IF (@EmailFilterOperation = 3)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedEmailAddress LIKE ''%' + @EmailFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@EmailFilterOperation = 5)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedEmailAddress LIKE ''%' + @EmailFilterValue + ''')';
END;

IF (
       @CompanyAssignedKeyFilterValue IS NOT NULL
       AND @CompanyAssignedKeyFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@CompanyAssignedKeyFilterOperation = 0)
        SET @WHERE
            = @WHERE + 'AND (Application_Users_EvaluatedUserCompanyAssignedKey = ''' + @CompanyAssignedKeyFilterValue
              + ''')';
    -- StartsWith
    ELSE IF (@CompanyAssignedKeyFilterOperation = 2)
        SET @WHERE
            = @WHERE + 'AND (Application_Users_EvaluatedUserCompanyAssignedKey LIKE ''' + @CompanyAssignedKeyFilterValue
              + '%'')';
    -- Contains
    ELSE IF (@CompanyAssignedKeyFilterOperation = 3)
        SET @WHERE
            = @WHERE + 'AND (Application_Users_EvaluatedUserCompanyAssignedKey LIKE ''%' + @CompanyAssignedKeyFilterValue
              + '%'')';
    -- EndsWith
    ELSE IF (@CompanyAssignedKeyFilterOperation = 5)
        SET @WHERE
            = @WHERE + 'AND (Application_Users_EvaluatedUserCompanyAssignedKey LIKE ''%' + @CompanyAssignedKeyFilterValue
              + ''')';
END;

IF (
       @FirstNameFilterValue IS NOT NULL
       AND @FirstNameFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@FirstNameFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedFirstName = ''' + @FirstNameFilterValue + ''')';
    -- StartsWith
    ELSE IF (@FirstNameFilterOperation = 2)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedFirstName LIKE ''' + @FirstNameFilterValue + '%'')';
    -- Contains
    ELSE IF (@FirstNameFilterOperation = 3)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedFirstName LIKE ''%' + @FirstNameFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@FirstNameFilterOperation = 5)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedFirstName LIKE ''%' + @FirstNameFilterValue + ''')';
END;

IF (
       @LastNameFilterValue IS NOT NULL
       AND @LastNameFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@LastNameFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedLastName = ''' + @LastNameFilterValue + ''')';
    -- StartsWith
    ELSE IF (@LastNameFilterOperation = 2)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedLastName LIKE ''' + @LastNameFilterValue + '%'')';
    -- Contains
    ELSE IF (@LastNameFilterOperation = 3)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedLastName LIKE ''%' + @LastNameFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@LastNameFilterOperation = 5)
        SET @WHERE = @WHERE + 'AND (Application_Users_EvaluatedLastName LIKE ''%' + @LastNameFilterValue + ''')';
END;

IF (@CityFilterValue IS NOT NULL AND @CityFilterOperation IS NOT NULL)
BEGIN
    -- IsEqualTo
    IF (@CityFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_City = ''' + @CityFilterValue + ''')';
    -- StartsWith
    ELSE IF (@CityFilterOperation = 2)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_City LIKE ''' + @CityFilterValue + '%'')';
    -- Contains
    ELSE IF (@CityFilterOperation = 3)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_City LIKE ''%' + @CityFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@CityFilterOperation = 5)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_City LIKE ''%' + @CityFilterValue + ''')';
END;

IF (@RegionFilterValue IS NOT NULL AND @RegionFilterOperation IS NOT NULL)
BEGIN
    -- IsEqualTo
    IF (@RegionFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_Region = ''' + @RegionFilterValue + ''')';
    -- StartsWith
    ELSE IF (@RegionFilterOperation = 2)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_Region LIKE ''' + @RegionFilterValue + '%'')';
    -- Contains
    ELSE IF (@RegionFilterOperation = 3)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_Region LIKE ''%' + @RegionFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@RegionFilterOperation = 5)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_Region LIKE ''%' + @RegionFilterValue + ''')';
END;

IF (
       @PostalCodeFilterValue IS NOT NULL
       AND @PostalCodeFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@PostalCodeFilterOperation = 0)
        SET @WHERE = @WHERE + 'AND (Application_UserAccountAddresses_PostalCode = ''' + @PostalCodeFilterValue + ''')';
    -- StartsWith
    ELSE IF (@PostalCodeFilterOperation = 2)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_PostalCode LIKE ''' + @PostalCodeFilterValue + '%'')';
    -- Contains
    ELSE IF (@PostalCodeFilterOperation = 3)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_PostalCode LIKE ''%' + @PostalCodeFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@PostalCodeFilterOperation = 5)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_PostalCode LIKE ''%' + @PostalCodeFilterValue + ''')';
END;

IF (
       @StreetAddressFilterValue IS NOT NULL
       AND @StreetAddressFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@StreetAddressFilterOperation = 0)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_StreetAddress1 = ''' + @StreetAddressFilterValue
              + ''' OR Application_UserAccountAddresses_StreetAddress2 = ''' + @StreetAddressFilterValue
              + ''' OR Application_UserAccountAddresses_StreetAddress3 = ''' + @StreetAddressFilterValue + ''')';
    -- StartsWith
    ELSE IF (@StreetAddressFilterOperation = 2)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_StreetAddress1 LIKE ''' + @StreetAddressFilterValue
              + '%'' OR Application_UserAccountAddresses_StreetAddress2 LIKE ''' + @StreetAddressFilterValue
              + '%'' OR Application_UserAccountAddresses_StreetAddress3 LIKE ''' + @StreetAddressFilterValue + '%'')';
    -- Contains
    ELSE IF (@StreetAddressFilterOperation = 3)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_StreetAddress1 LIKE ''%' + @StreetAddressFilterValue
              + '%'' OR Application_UserAccountAddresses_StreetAddress2 LIKE ''%' + @StreetAddressFilterValue
              + '%'' OR Application_UserAccountAddresses_StreetAddress3 LIKE ''%' + @StreetAddressFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@StreetAddressFilterOperation = 5)
        SET @WHERE
            = @WHERE + 'AND (Application_UserAccountAddresses_StreetAddress1 LIKE ''%' + @StreetAddressFilterValue
              + ''' OR Application_UserAccountAddresses_StreetAddress2 LIKE ''%' + @StreetAddressFilterValue
              + ''' OR Application_UserAccountAddresses_StreetAddress3 LIKE ''%' + @StreetAddressFilterValue + ''')';
END;

IF (
       @PhoneNumberFilterValue IS NOT NULL
       AND @PhoneNumberFilterOperation IS NOT NULL
   )
BEGIN
    -- IsEqualTo
    IF (@PhoneNumberFilterOperation = 0)
        SET @WHERE
            = @WHERE + 'AND (Application_UserProfiles_Profile_vPrimaryTelephone_Numeric = ''' + @PhoneNumberFilterValue
              + ''' OR Application_UserProfiles_Profile_vMobileTelephone_Numeric = ''' + @PhoneNumberFilterValue + ''')';
    -- StartsWith
    ELSE IF (@PhoneNumberFilterOperation = 2)
        SET @WHERE
            = @WHERE + 'AND (Application_UserProfiles_Profile_vPrimaryTelephone_Numeric LIKE ''' + @PhoneNumberFilterValue
              + ''' OR Application_UserProfiles_Profile_vMobileTelephone_Numeric LIKE ''' + @PhoneNumberFilterValue
              + '%'')';
    -- Contains
    ELSE IF (@PhoneNumberFilterOperation = 3)
        SET @WHERE
            = @WHERE + 'AND (Application_UserProfiles_Profile_vPrimaryTelephone_Numeric LIKE ''%'
              + @PhoneNumberFilterValue + '%'' OR Application_UserProfiles_Profile_vMobileTelephone_Numeric LIKE ''%'
              + @PhoneNumberFilterValue + '%'')';
    -- EndsWith
    ELSE IF (@PhoneNumberFilterOperation = 5)
        SET @WHERE
            = @WHERE + 'AND (Application_UserProfiles_Profile_vPrimaryTelephone_Numeric LIKE ''%'
              + @PhoneNumberFilterValue + ''' OR Application_UserProfiles_Profile_vMobileTelephone_Numeric LIKE ''%'
              + @PhoneNumberFilterValue + ''')';
END;


SET @GROUPBY
    = '[Application_UserInvitations_ApplicationId]
      ,[Application_UserInvitations_EmailAddress]
      ,[Application_UserInvitations_InvitationStatusTypeId]
      ,[Application_UserInvitations_InvitationKey]
      ,[Application_UserInvitations_Created]
      ,[Application_UserInvitations_Expires]
      ,[Application_UserInvitations_CompanyUserId]
      ,[Application_Users_UserPublicId]
      ,[Application_Users_UserId]
      ,[Application_Users_UserCompanyAssignedKey]
      ,[Application_Users_FirstName]
      ,[Application_Users_LastName]
      ,[Application_Users_Created]
      ,[Application_Users_CompanyUserId]
      ,[Application_Users_ApplicationId]
      ,[Application_UserEmailAddresses_VerifiedEmailAddress]
      ,[Application_UserEmailAddresses_UnverifiedEmailAddress]
      ,[Application_UserInvitationRegistrationInformation_UserCompanyAssignedKey]
      ,[Application_UserInvitationRegistrationInformation_FirstName]
      ,[Application_UserInvitationRegistrationInformation_LastName]
      ,[Application_Companies_Name]
      ,[Application_Companies_UserTitleSubscriptionKey]
      ,[Application_Companies_CompanyUserId]
      ,[Application_Companies_CompanyId]
      ,[Application_Users_EvaluatedEmailAddress]
      ,[Application_Users_EvaluatedFirstName]
      ,[Application_Users_EvaluatedLastName]
      ,[Application_Users_EvaluatedUserCompanyAssignedKey]
      ,[Application_UserProfiles_PrimaryPhoneNumber]
      ,[Application_UserProfiles_MobilePhoneNumber]
      ,[UserHasApplicationAccount]
      ,[UserHasFinancialProcessorAccount]
      ,[Application_RegisterDate]
      ,[Application_Registration_Progress]
      ,[Application_Evaluated_User_Registration_Progress_Step]
      ,[Application_EvaluatedPhoneNumberCountry]';

/* Always hide cloaked*/
SET @WHERE = @WHERE + 'AND Application_UserInvitations_InvitationStatusTypeId != 4 ';

/* Build SQL and dynamic WHERE clause */
IF LEN(@WHERE) > 0
BEGIN
    -- Where clause
    SET @SQLViewCount = @SQLViewCount + ' WHERE ' + RIGHT(@WHERE, LEN(@WHERE) - 3);

    -- Group by
    SET @SQLViewCount = @SQLViewCount + ' GROUP BY ' + RIGHT(@GROUPBY, LEN(@GROUPBY) - 0);
    SET @SQLViewCount = @SQLViewCount + ') S';


    -- Main SELECT dynamic SQL
    SET @SQL = @SQL + ' WHERE ' + RIGHT(@WHERE, LEN(@WHERE) - 3);


    SET @SQL = @SQL + ' GROUP BY ' + RIGHT(@GROUPBY, LEN(@GROUPBY) - 0) + ' ' + @SQLOrderBy;


    -- Execute View Total Record Count 
    EXEC sp_executesql @SQLViewCount,
                       N'@_TotalRecordCount 
                INT OUTPUT',
                       @_TotalRecordCount = @TotalRecordCount OUTPUT;

    -- Summarise Counts, Size and Page (resultset 1)
    SELECT CAST(CEILING((CAST(@TotalRecordCount AS FLOAT) / (CAST(@Size AS FLOAT)))) AS INT) AS TotalPages,
           @TotalRecordCount AS TotalRecordCount,
           CASE
               WHEN @Size = 32767 THEN
                   @TotalRecordCount
               ELSE
                   @Size
           END AS PageSize,
           @Page AS PageNumber;

    -- Execute main SELECT (resultset 2) 
    EXEC sp_executesql @SQL,
                       N'@_Page
                INT,
             @_Size 
                INT,
             @_SortColumn
                VARCHAR(40)',
                       @_Page = @Page,
                       @_Size = @Size,
                       @_SortColumn = @SortColumn;

END;
2
James Legan

2つの簡単な改善:

  1. _@CompanyPublicIdValue_をUserPublicIdと同じ型にキャストして、このクエリを修正します。 _@CompanyPublicIdValue_はnvarchar(32)であり、列の型よりも データ型の優先順位 が高くなります。したがって、比較ではすべての行で変換が必要です。

    それは高価であり、そうすべきではありません。

    _SET @CompanyUserIdValue =
        (
            SELECT UserId
            FROM Application.Users
            WHERE UserPublicId = @CompanyPublicIdValue
        )
    _
  2. 1つのクエリで行をカウントして次のクエリで返すのではなく、最初のクエリでキー値を一時テーブル/テーブル変数にフェッチし、2番目のクエリで結合します。

次に、返されるキーを識別するクエリの改善に取り組みます。おそらく、そのクエリは、ベーステーブルまたはインデックス付きビューに直接アクセスできます。次に、最後のクエリは既存のビューをキー値の一時テーブルに結合します。

実行計画には18 CONVERT_IMPLICITがあります。それぞれを見つけて、1つずつ修正してください。

NVARCHARとVARCHARは非常に頻繁に交換可能に使用されます。VARCHARを使用し、サイズをできるだけ制限する必要があると思います。

プロシージャに**SET NOCOUNT ON**がありません。

ページングのため、プロシージャはまだ高速です。

表示される主な問題はGroup Byです。GroupByを使用して重複レコードを排除しているようです。

したがって、クエリが間違っている場合は、どの結合が多くのレコードを引き起こしているのかを見つけます。

または、ROW_NUMBER(ウィンドウ関数)を使用して重複レコードを削除します。

Group BYが不要な場合は、パフォーマンスが向上していなくても削除してください。後でその向上が見られます。

実際、LEFT JOINOUTER APPLYのみを使用している場合は、それぞれINNERCROSS APPLYに変換できるかどうかを確認してください。

登録および招待されたニーズ

これはあなたの要件かもしれませんが、すべての結合を徹底的に調べ、左結合を排除する必要があります。

必要以上のレコードを取得していると思いますので、Group Byを使用しています。これがそうである場合、これがパフォーマンスの低下の背後にある理由です。

ビューで2を除くすべての結合にコメントを付け、プロシージャでGroup Byを削除してテストとデバッグを開始し、正しい行数を取得して、一度に1つの結合をゆっくり削除することをお勧めします。

たとえば、このOUTER APPLYで、

OUTER APPLY
    (
        SELECT TOP 1 RAAA.Application_AccountAddresses_AddressDetails_vCountry
        FROM Application.Accounts RACA
            JOIN Application.AccountAddresses RAAA
                ON RAAA.AccountId = RACA.AccountId
        WHERE RACA.UserId = U.UserId
              AND RAAA.AddressTypeId = 1
        ORDER BY RAAA.AccountAddressId DESC
    ) AS ResidentialAddressCountry

ここでは両方のテーブルがINNER JOINですが、メインクエリの同じテーブルはLEFT JOINです。

それが正しいか ?

ResidentialAddressCountryおよびBusinessAddressCountryOUTER APPLYは必要ですか?

代わりに、

CASE 
        WHEN AddressTypeId = 1
            AND AA.Application_AccountAddresses_AddressDetails_vCountry IS NOT NULL
            THEN ResidentialAddressCountry.Application_AccountAddresses_AddressDetails_vCountry
        WHEN AddressTypeId = 3
            AND AA.BusinessAddressCountry.Application_AccountAddresses_AddressDetails_vCountry IS NOT NULL
            THEN BusinessAddressCountry.Application_AccountAddresses_AddressDetails_vCountry
        ELSE NULL
        END Application_EvaluatedPhoneNumberCountry

そして、両方の外側のコメントをコメントし、理解を深めるためにテーブルのエイリアスに注意してください。

Application.fnRemoveNonNumericCharacters

同じビューを使用したいが、非数値を削除したくない、または連結したくない場合は、新しいビューを作成する必要がありますか?

ユーザーがこれらの小さな問題について決定するようにします。Secondly UDFは悪名高い悪パフォーマンスです。

そのため、ビューからUDFを削除します。procでインラインで「数字以外の文字の削除作業」を実行します

値をフェッチした後、これを上に安全に書き込むことができます。

IF (
       @RequestorApplicationIdFilterValue IS NULL
       OR @RequestorApplicationIdFilterValue NOT IN ( 1, 2, 3 )
   )
BEGIN
    THROW 60000, 'ApplicationIdFilterValue is null or out of range', 1;
END;
0
KumarHarsh