如何降低For JSON Path对性能的影响

问题描述 投票:0回答:1

我们的前端和后端都使用 JSON,并且我们拥有将其完美绑定到前端和后端的模型。因此,完全保留 JSON 格式确实让我作为开发人员的生活变得更轻松,并减少了我对实体框架的需求来满足我的开发需求。然而,从我读过的各种文章来看,使用 For JSON 对性能有轻微影响,但我们有一个包含大约 2500 条记录的查询,大约需要 10-15 秒,感觉我们的查询出了问题。我不得不使用分页来使我们的前端更容易忍受。我们对 SQL 比较陌生(不是程序员,但确实到处使用 sql),所以我们还有很多东西要学习,但这一点非常关键,所以我希望这里有人能够查明我的查询的问题。有人告诉我不要让 SQL Server 处理反序列化和序列化,但说实话,我看到过用户的帖子拥有数万条记录,并且他们的 json 代码运行时间不到一秒,但这对我来说是可以接受的并且完全值得如果可以实现的话,性能会受到影响。请看下面的代码:

    SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date, ,>
-- Description: <Description, ,>
-- =============================================
ALTER FUNCTION [dbo].[A2Q_0171_RR_CertificationReviewModel_Browse_JSON](@StartingIndex int, @MaxRecords int)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN
(
select
    (
    select 
        JSON_QUERY ((select 
        --top (@MaxRecords)
            t1.MMCID,
            dbo.A2F_0012_ReturnMMCIDforDisplay(t1.MMCID) as MMCIDForDisplay
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) as MMCID,
        JSON_QUERY ((select distinct
            t2.STATE_ID,
            t2.ENTITY_NAME,
            t2.ENTITY_ABREVIATION,
            --t3.PROGRAM_SHORT_NAME as PROGRAM_CONCATENATE,
                (select distinct
                    t4.PROGRAM_NAME_DD,
                    t5.PROGRAM_NAME,
                    t5.PROGRAM_SHORT_NAME,
                    t5.PROGRAM_ACTIVE
                from
                    dbo.RRBaseProgramTBL as t4,
                    dbo.RRProgramListTBL as t5
                where
                    t2.STATE_ID = t5.STATE_ID
                    AND t1.MMCID = t4.MMCID
                    AND t4.PROGRAM_NAME_DD = t5.PROGRAM_DD
                FOR JSON PATH) as PROGRAM_INFO
            --dbo.A2F_0013_RR_ConcatenateProgramsByMMCID() as t3
            --AND t1.MMCID = t3.MMCID
        from
            dbo.USStateListTBL as t2
        where
            t1.STATE_ID = t2.STATE_ID
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) as STATE_INFORMATION,
        JSON_QUERY ((select 
            t1.OLD_TRACKER_NAME,
            t1.NEW_TRACKER_NAME
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as TRACKER_NAME,
        JSON_QUERY ((select 
            t1.RATING_PERIOD_BEGINS,
            format(t1.RATING_PERIOD_BEGINS, 'MM/dd/yyyy') as RATING_PERIOD_BEGINS_FORMATTED,
            t1.RATING_PERIOD_ENDS,
            format(t1.RATING_PERIOD_ENDS, 'MM/dd/yyyy') as RATING_PERIOD_ENDS_FORMATTED
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) as RATING_PERIOD,
        JSON_QUERY ((select 
            t1.CERTIFICATION_DATE,
            format(t1.CERTIFICATION_DATE, 'MM/dd/yyyy') as CERTIFICATION_DATE_FORMATTED
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) as CERTIFICATION_DATE,
        (select distinct
            t7.FIRST_NAME,
            t7.LAST_NAME,
            t7.INTERNAL_USER_NUMBER,
            t8.User_Role_Description,
            t8.User_Role_Description_abbrev
        from
            dbo.RRRoleAssignmentMMCIDTBL as t6,
            dbo.UsersAccountsTBL as t7,
            dbo.UserRoleDefinitionsTBL as t8
        where
            t1.MMCID = t6.MMCID
            AND (t6.JOB_ASSIGNMENT = 1 OR t6.JOB_ASSIGNMENT = 2 OR t6.JOB_ASSIGNMENT = 5)
            AND t6.USER_ASSIGNMENT = t7.INTERNAL_USER_NUMBER
            AND t6.JOB_ASSIGNMENT = t8.User_Role_Number_DD
        FOR JSON PATH, INCLUDE_NULL_VALUES) as RATE_REVIEWERS,
        JSON_QUERY((select distinct
            --t9.Signing_Actuary as SIGNING_ACTUARY_CONCATENATED,
            t9.ACTUARIAL_FIRM_DD,
            t10.FIRM_NAME
        from
            dbo.ActuarySigningTBL as t9,
            dbo.ActuarialFirmTBL as t10
        where
            t1.MMCID = t9.MMCID
            AND t9.ACTUARIAL_FIRM_DD = t10.FIRM_DD_NUMBER
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as ACTUARIAL_FIRM,
        (select
            (t11.FIRST_NAME + ' ' + t11.LAST_NAME) as NAME,
            t11.INTERNAL_USER_NUMBER
        from
            dbo.ActuarySigningTBL as t16,
            dbo.UsersAccountsTBL as t11
        where
            t1.MMCID = t16.MMCID
            AND t16.SIGNING_ACTUARY_DD = t11.INTERNAL_USER_NUMBER
        FOR JSON PATH, INCLUDE_NULL_VALUES) as SIGNING_ACTUARY,
        JSON_QUERY((select
            t1.REVIEW_TYPE_DD,
            t12.ReviewTypeDescription
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)) as REVIEW_TYPE,
        JSON_QUERY((select 
            t13.STATE_SUBMISSION_DATE,
            format(t13.STATE_SUBMISSION_DATE, 'MM/dd/yyyy') as STATE_SUBMISSION_DATE_FORMATTED,
            t13.DMCP_SUBMISSION_DATE,
            format(t13.DMCP_SUBMISSION_DATE, 'MM/dd/yyyy') as DMCP_SUBMISSION_DATE_FORMATTED
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as SUBMISSION_DATE,
        JSON_QUERY((select 
            t1.ACTUARIAL_REVIEW_STATUS_DD,
            t14.DISPLAY,
            t13.OACT_MEMO_DATE,
            format(t13.OACT_MEMO_DATE, 'MM/dd/yyyy') as OACT_MEMO_DATE_FORMATTED
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as OACT_REVIEW_STATUS,
        JSON_QUERY((select
            t1.DMCP_REVIEW_STATUS_DD,
            t15.DISPLAY,
            t13.DMCP_RECOMMENDATION_DATE,
            format(t13.DMCP_RECOMMENDATION_DATE, 'MM/dd/yyyy') as DMCP_RECOMMENDATION_DATE_FORMATTED,
            t13.DMCP_RECOMMENDATION_COMMENT
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as DMCP_REVIEW_STATUS,
        JSON_QUERY((select
            t13.RISK_MITIGATION,
            t13.RATE_RANGES,
            t13.REVISED_CERT,
            t13.REVISED_CERT_DATE,
            format(t13.REVISED_CERT_DATE, 'MM/dd/yyyy') as REVISED_CERT_DATE_FORMATTED
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES)) as DMCP_ADDITIONAL_INFO
    from
        dbo.RateReviewBaseTBL as t1,
        dbo.RRReviewTypeTBL as t12,
        dbo.DMCPAdditionalTrackerInformationTBL as t13,
        dbo.ActuarialReviewStatusDisplay_TBL as t14,
        dbo.RRDMCPReviewStatusTBL as t15
    where
        t1.REVIEW_TYPE_DD = t12.ReviewTypeDD
        AND t1.MMCID = t13.MMCID
        AND t1.ACTUARIAL_REVIEW_STATUS_DD = t14.ACTUARIAL_REVIEW_STATUS_DD
        AND t1.DMCP_REVIEW_STATUS_DD = t15.DMCP_REVIEW_STATUS_DD
        --AND t1.MMCID >= (select max(t16.MMCID) from
        --              (select top (@StartingIndex) MMCID
        --              from dbo.RateReviewBaseTBL
        --              order by MMCID) as t16)
    order by t1.MMCID
    OFFSET (@StartingIndex - 1) ROWS
    FETCH NEXT @MaxRecords ROWS ONLY
    FOR JSON PATH, INCLUDE_NULL_VALUES
    ) as DATA,
    @StartingIndex as STARTING_INDEX,
    (select count(MMCID) from dbo.RateReviewBaseTBL) as TOTAL_ROWS,
    @MaxRecords as MAX_RECORDS_PER_QUERY
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES
)
END
GO

如果我应该提供任何其他信息来帮助调试,我很乐意添加它。

sql query-optimization sql-server-2019 for-json
1个回答
0
投票

有很多事情需要你考虑。

  • 首先,通过使用 JSON 路径作为列名可以极大地简化查询。任何时候你有标量对象属性(即不是数组,使用
    WITHOUT_ARRAY_WRAPPER
    )时,这都有效。这允许您删除大部分子查询和
    JSON_QUERY
    调用。
  • 针对
    UsersAccountsTBL
    的双子查询可能可以以某种方式组合,但我不知道您的架构或要求。
  • 如果可能,请使用带有样式编号的
    CONVERT
    ,而不是慢速
    FORMAT
    函数。
CREATE OR ALTER FUNCTION dbo.A2Q_0171_RR_CertificationReviewModel_Browse_JSON
  (@StartingIndex int, @MaxRecords int)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN
(
  select
    (
      select
        rrb.MMCID AS [MMCID.MMCID],
        dbo.A2F_0012_ReturnMMCIDforDisplay(rrb.MMCID) as [MMCID.MMCIDForDisplay],
        usstate.STATE_ID AS [STATE_INFORMATION.STATE_ID],
        usstate.ENTITY_NAME AS [STATE_INFORMATION.ENTITY_NAME],
        usstate.ENTITY_ABREVIATION AS [STATE_INFORMATION.ENTITY_ABREVIATION],
        (
            select distinct
              bp.PROGRAM_NAME_DD,
              pl.PROGRAM_NAME,
              pl.PROGRAM_SHORT_NAME,
              pl.PROGRAM_ACTIVE
            from
                dbo.RRBaseProgramTBL as bp
            join
                dbo.RRProgramListTBL as pl
                ON bp.PROGRAM_NAME_DD = pl.PROGRAM_DD
            where
                usstate.STATE_ID = pl.STATE_ID
                AND rrb.MMCID = bp.MMCID
            FOR JSON PATH
        ) as [STATE_INFORMATION.PROGRAM_INFO],
        rrb.OLD_TRACKER_NAME AS [TRACKER_NAME.OLD_TRACKER_NAME],
        rrb.NEW_TRACKER_NAME AS [TRACKER_NAME.NEW_TRACKER_NAME],
        rrb.RATING_PERIOD_BEGINS AS [RATING_PERIOD.RATING_PERIOD_BEGINS],
        convert(date, rrb.RATING_PERIOD_BEGINS, 101) as [RATING_PERIOD.RATING_PERIOD_BEGINS_FORMATTED],
        rrb.RATING_PERIOD_ENDS AS [RATING_PERIOD.RATING_PERIOD_ENDS],
        convert(date, rrb.RATING_PERIOD_ENDS, 101) as [RATING_PERIOD.RATING_PERIOD_ENDS_FORMATTED],
        rrb.CERTIFICATION_DATE AS [CERTIFICATION_DATE.CERTIFICATION_DATE],
        convert(date, rrb.CERTIFICATION_DATE, 101) as [CERTIFICATION_DATE.CERTIFICATION_DATE_FORMATTED],
        (
          select distinct
            ua.FIRST_NAME,
            ua.LAST_NAME,
            ua.INTERNAL_USER_NUMBER,
            urd.User_Role_Description,
            urd.User_Role_Description_abbrev
          from
              dbo.RRRoleAssignmentMMCIDTBL as ram
          join
              dbo.UsersAccountsTBL as ua
              on ram.USER_ASSIGNMENT = ua.INTERNAL_USER_NUMBER
          join
              dbo.UserRoleDefinitionsTBL as urd
              on ram.JOB_ASSIGNMENT = urd.User_Role_Number_DD
          where
              rrb.MMCID = ram.MMCID
              AND ram.JOB_ASSIGNMENT IN (1, 2, 5)
          FOR JSON PATH, INCLUDE_NULL_VALUES
        ) as RATE_REVIEWERS,
        acts.ACTUARIAL_FIRM_DD AS [ACTUARIAL_FIRM.ACTUARIAL_FIRM_DD],
        actf.FIRM_NAME AS [ACTUARIAL_FIRM.FIRM_NAME]
        (
          select
            ua.FIRST_NAME + ' ' + ua.LAST_NAME as NAME,
            ua.INTERNAL_USER_NUMBER
          from
              dbo.ActuarySigningTBL as acts
          join
              dbo.UsersAccountsTBL as ua
              on acts.SIGNING_ACTUARY_DD = ua.INTERNAL_USER_NUMBER
          where
              rrb.MMCID = acts.MMCID
          FOR JSON PATH, INCLUDE_NULL_VALUES
        ) as SIGNING_ACTUARY,
        rrb.REVIEW_TYPE_DD AS [REVIEW_TYPE.REVIEW_TYPE_DD],
        rrvt.ReviewTypeDescription AS [REVIEW_TYPE.ReviewTypeDescription]
        dati.STATE_SUBMISSION_DATE AS [SUBMISSION_DATE.STATE_SUBMISSION_DATE],
        convert(date, dati.STATE_SUBMISSION_DATE, 101) AS [SUBMISSION_DATE.STATE_SUBMISSION_DATE_FORMATTED],
        dati.DMCP_SUBMISSION_DATE AS [SUBMISSION_DATE.DMCP_SUBMISSION_DATE],
        convert(date, dati.DMCP_SUBMISSION_DATE, 101) AS [SUBMISSION_DATE.DMCP_SUBMISSION_DATE_FORMATTED],
        rrb.ACTUARIAL_REVIEW_STATUS_DD AS [OACT_REVIEW_STATUS.ACTUARIAL_REVIEW_STATUS_DD],
        arsd.DISPLAY AS [OACT_REVIEW_STATUS.DISPLAY],
        dati.OACT_MEMO_DATE AS [OACT_REVIEW_STATUS.OACT_MEMO_DATE].
        convert(date, dati.OACT_MEMO_DATE, 101) AS [OACT_REVIEW_STATUS.OACT_MEMO_DATE_FORMATTED],
        rrb.DMCP_REVIEW_STATUS_DD AS [DMCP_REVIEW_STATUS.DMCP_REVIEW_STATUS_DD],
        rrdrs.DISPLAY AS [DMCP_REVIEW_STATUS.DISPLAY],
        dati.DMCP_RECOMMENDATION_DATE AS [DMCP_REVIEW_STATUS.DMCP_RECOMMENDATION_DATE],
        convert(date, dati.DMCP_RECOMMENDATION_DATE, 101) AS [DMCP_REVIEW_STATUS.DMCP_RECOMMENDATION_DATE_FORMATTED],
        dati.DMCP_RECOMMENDATION_COMMENT AS [DMCP_REVIEW_STATUS.,
        dati.RISK_MITIGATION AS [DMCP_ADDITIONAL_INFO.RISK_MITIGATION],
        dati.RATE_RANGES AS [DMCP_ADDITIONAL_INFO.RATE_RANGES],
        dati.REVISED_CERT AS [DMCP_ADDITIONAL_INFO.REVISED_CERT],
        dati.REVISED_CERT_DATE AS [DMCP_ADDITIONAL_INFO.REVISED_CERT_DATE],
        convert(date, dati.REVISED_CERT_DATE, 101) AS [DMCP_ADDITIONAL_INFO.REVISED_CERT_DATE_FORMATTED],
      from
          dbo.RateReviewBaseTBL as rrb
      join
          dbo.RRReviewTypeTBL as rrvt
          on rrb.REVIEW_TYPE_DD = rrvt.ReviewTypeDD
      join
          dbo.DMCPAdditionalTrackerInformationTBL as dati,
          on rrb.MMCID = dati.MMCID
      join
          dbo.ActuarialReviewStatusDisplay_TBL as arsd,
          on rrb.ACTUARIAL_REVIEW_STATUS_DD = arsd.ACTUARIAL_REVIEW_STATUS_DD
      join
          dbo.RRDMCPReviewStatusTBL as rrdrs
          on rrb.DMCP_REVIEW_STATUS_DD = rrdrs.DMCP_REVIEW_STATUS_DD
      left join
          dbo.USStateListTBL as usstate
          on rrb.STATE_ID = usstate.STATE_ID
      left join
      (
          select distinct
              acts.ACTUARIAL_FIRM_DD,
              actf.FIRM_NAME
          from
              dbo.ActuarySigningTBL as acts
          join
              dbo.ActuarialFirmTBL as actf
              on acts.ACTUARIAL_FIRM_DD = actf.FIRM_DD_NUMBER
      ) as ACTUARIAL_FIRM
          ON rrb.MMCID = acts.MMCID
      order by rrb.MMCID
      OFFSET (@StartingIndex - 1) ROWS
      FETCH NEXT @MaxRecords ROWS ONLY
      FOR JSON PATH, INCLUDE_NULL_VALUES
    ) as DATA,
    @StartingIndex as STARTING_INDEX,
    (select count(*) from dbo.RateReviewBaseTBL) as TOTAL_ROWS,
    @MaxRecords as MAX_RECORDS_PER_QUERY
  FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES
);
END;
  • 你也应该认真思考那些
    DISTINCT
    ,它们为什么会在那里?您是否收到重复的邮件,如果是,为什么?也许您的连接条件不正确,或者您需要某种行编号解决方案。
  • ORDER BY... OFFSET
    分页本身就是一个问题。按行号分页效率非常低,因为您需要扫描所有先前的行。考虑键集分页。
  • 获取总数是额外的工作,可能没有必要。
  • 您似乎没有正确的索引。您需要查看您的查询并决定哪些索引将支持该查询。查看加入和过滤条件。
© www.soinside.com 2019 - 2024. All rights reserved.