You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

替换SQL Server查询子查询以提升性能的方案咨询

SQL Server查询性能优化求助

我正在尝试优化SQL Server中的以下查询性能:

WITH s
AS (
    SELECT src.[ITEMID] AS [Item Code]
        ,po.[ORDERACCOUNT] AS [Order Account]
        ,vpsj.[INVOICEACCOUNT] AS [Invoice Account]
        ,vpsj.[PURCHID] AS [PO Number]
        ,src.[INVENTDIMID] AS [Inventory Dimension Id]
        ,po.[PURCHREQLINEREFID] AS [Reference Id]
        ,po.[PURCHPOOLID] AS [Purchase Pool Code]
        ,po.[ITEMBUYERGROUPID] AS [Inventory Buyer Group Code]
        ,cast(0 AS [bigint]) AS [Category Id]
        ,(
            SELECT TOP (1) lt.LEDGERDIMENSION
            FROM [dbo].[EALedgerTransactions] lt
            WHERE lt.[PARTITION] = src.[PARTITION]
                AND lt.[VOUCHERDATAAREAID] = src.[COMPANYCODE]
                AND lt.[VOUCHER] = src.[COSTLEDGERVOUCHER]
                AND lt.[POSTINGTYPE] IN (82,83)
            ) AS [Ledger Dimension Id]
        ,(
            SELECT TOP (1) lt.MAINACCOUNTID
            FROM [dbo].[EALedgerTransactions] lt
            WHERE lt.[PARTITION] = src.[PARTITION]
                AND lt.[VOUCHERDATAAREAID] = src.[COMPANYCODE]
                AND lt.[VOUCHER] = src.[COSTLEDGERVOUCHER]
                AND lt.[POSTINGTYPE] IN (82,83)
            ) AS [Main Account Id]
        ,src.[RECORDID] AS [Record Id]
        ,src.[COMPANYCODE] AS [Company Code]
        ,dateadd(day, datediff(day, 0, po.[CREATEDDATEANDTIME] - getutcdate() + getutcdate()), 0) AS [PO Date],
        po.[CURRENCYCODE] AS [Currency Code]
        ,src.[SOURCEDOCUMENTLINE] AS [Source Document Line Id]
        ,src.[ACCOUNTINGDATE] AS [Transaction Date]
        ,vpsj.[PACKINGSLIPID] AS [Product Receipt]
        ,cast(NULL AS [nvarchar](20)) AS [Invoice Number]
        ,src.[PURCHUNIT] AS [Purchase Unit]
        ,cast(NULL AS [nvarchar](20)) AS [Inventory Unit]
        ,po.[POAMOUNT] / nullif(po.[POQUANTITY], 0.0) AS [Purchase Price]
        ,src.[QTY] AS [Receipt Quantity]
        ,src.[INVENTQTY] AS [Receipt Inventory Quantity]
        ,src.[VALUEMST] AS [Receipt Amount Master]
        ,(
            SELECT sum(QTY) AS QTY
            FROM [dbo].[EAVendorPackingslipTransactions] pst
            WHERE pst.[PARTITION] = src.[PARTITION]
                AND pst.[COMPANYCODE] = src.[COMPANYCODE]
                AND pst.[COSTLEDGERVOUCHER] = src.[COSTLEDGERVOUCHER]
            ) AS [Voucher Quantity]
        ,(
            SELECT sum(ACCOUNTINGCURRENCYAMOUNT) AS ACCOUNTINGCURRENCYAMOUNT
            FROM [dbo].[EALedgerTransactions] lt
            WHERE lt.[PARTITION] = src.[PARTITION]
                AND lt.[VOUCHERDATAAREAID] = src.[COMPANYCODE]
                AND lt.[VOUCHER] = src.[COSTLEDGERVOUCHER]
                AND lt.[POSTINGTYPE] IN (82,83)
            ) AS [GRNI Amount Master]
        ,(
            SELECT sum(TRANSACTIONCURRENCYAMOUNT) AS TRANSACTIONCURRENCYAMOUNT
            FROM [dbo].[EALedgerTransactions] lt
            WHERE lt.[PARTITION] = src.[PARTITION]
                AND lt.[VOUCHERDATAAREAID] = src.[COMPANYCODE]
                AND lt.[VOUCHER] = src.[COSTLEDGERVOUCHER]
                AND lt.[POSTINGTYPE] IN (82,83)
            ) AS [GRNI Amount]
    FROM [dbo].[EAVendorPackingslipTransactions] src
    LEFT JOIN (
        SELECT [RECORDID]
            ,[INVOICEACCOUNT]
            ,[COMPANYCODE]
            ,[PURCHID]
            ,[PACKINGSLIPID]
            ,[ACCOUNTINGDATE]
            ,[LEDGERVOUCHER]
            ,[COSTLEDGERVOUCHER]
            ,rank() OVER (
                PARTITION BY [RECORDID] ORDER BY [VENDPACKINGSLIPVERSION_RECORDID] DESC
                ) AS [RANK]
        FROM [dbo].[EAVendorPackingslipJournals] vpsj
        ) vpsj ON vpsj.[RECORDID] = src.[VENDPACKINGSLIPJOUR]
        AND vpsj.[RANK] = 1
    LEFT JOIN [dbo].[EAPurchaseOrders] po ON po.[PARTITION] = src.[PARTITION]
        AND po.[COMPANYCODE] = src.[COMPANYCODE]
        AND po.[INVENTORYTRANSACTIONID] = src.[INVENTTRANSID]
    )

问题出在查询中的多个子查询上,我尝试用连接替换这些子查询,但始终无法得到预期结果。求替换子查询或其他优化的思路。

执行计划图示:
执行计划


优化思路

1. 合并重复子查询,改用预聚合CTE

原查询多次重复访问EALedgerTransactions和EAVendorPackingslipTransactions表,可通过预聚合CTE减少表扫描次数,同时保证逻辑与原查询一致:

WITH LedgerAgg AS (
    SELECT 
        lt.[PARTITION],
        lt.[VOUCHERDATAAREAID],
        lt.[VOUCHER],
        -- 用FIRST_VALUE模拟原TOP(1)逻辑(无排序时取任意第一条)
        FIRST_VALUE(lt.LEDGERDIMENSION) OVER (PARTITION BY lt.[PARTITION], lt.[VOUCHERDATAAREAID], lt.[VOUCHER] ORDER BY (SELECT NULL)) AS [Ledger Dimension Id],
        FIRST_VALUE(lt.MAINACCOUNTID) OVER (PARTITION BY lt.[PARTITION], lt.[VOUCHERDATAAREAID], lt.[VOUCHER] ORDER BY (SELECT NULL)) AS [Main Account Id],
        SUM(lt.ACCOUNTINGCURRENCYAMOUNT) AS [GRNI Amount Master],
        SUM(lt.TRANSACTIONCURRENCYAMOUNT) AS [GRNI Amount]
    FROM [dbo].[EALedgerTransactions] lt
    WHERE lt.[POSTINGTYPE] IN (82,83)
    GROUP BY lt.[PARTITION], lt.[VOUCHERDATAAREAID], lt.[VOUCHER]
),
PackingSlipAgg AS (
    SELECT 
        pst.[PARTITION],
        pst.[COMPANYCODE],
        pst.[COSTLEDGERVOUCHER],
        SUM(pst.QTY) AS [Voucher Quantity]
    FROM [dbo].[EAVendorPackingslipTransactions] pst
    GROUP BY pst.[PARTITION], pst.[COMPANYCODE], pst.[COSTLEDGERVOUCHER]
),
s AS (
    SELECT 
        src.[ITEMID] AS [Item Code],
        po.[ORDERACCOUNT] AS [Order Account],
        vpsj.[INVOICEACCOUNT] AS [Invoice Account],
        vpsj.[PURCHID] AS [PO Number],
        src.[INVENTDIMID] AS [Inventory Dimension Id],
        po.[PURCHREQLINEREFID] AS [Reference Id],
        po.[PURCHPOOLID] AS [Purchase Pool Code],
        po.[ITEMBUYERGROUPID] AS [Inventory Buyer Group Code],
        CAST(0 AS BIGINT) AS [Category Id],
        la.[Ledger Dimension Id],
        la.[Main Account Id],
        src.[RECORDID] AS [Record Id],
        src.[COMPANYCODE] AS [Company Code],
        -- 简化日期计算逻辑,与原代码效果一致
        CAST(po.[CREATEDDATEANDTIME] AS DATE) AS [PO Date],
        po.[CURRENCYCODE] AS [Currency Code],
        src.[SOURCEDOCUMENTLINE] AS [Source Document Line Id],
        src.[ACCOUNTINGDATE] AS [Transaction Date],
        vpsj.[PACKINGSLIPID] AS [Product Receipt],
        CAST(NULL AS NVARCHAR(20)) AS [Invoice Number],
        src.[PURCHUNIT] AS [Purchase Unit],
        CAST(NULL AS NVARCHAR(20)) AS [Inventory Unit],
        po.[POAMOUNT] / NULLIF(po.[POQUANTITY], 0.0) AS [Purchase Price],
        src.[QTY] AS [Receipt Quantity],
        src.[INVENTQTY] AS [Receipt Inventory Quantity],
        src.[VALUEMST] AS [Receipt Amount Master],
        psa.[Voucher Quantity],
        la.[GRNI Amount Master],
        la.[GRNI Amount]
    FROM [dbo].[EAVendorPackingslipTransactions] src
    LEFT JOIN (
        SELECT 
            [RECORDID],
            [INVOICEACCOUNT],
            [COMPANYCODE],
            [PURCHID],
            [PACKINGSLIPID],
            [ACCOUNTINGDATE],
            [LEDGERVOUCHER],
            [COSTLEDGERVOUCHER],
            RANK() OVER (
                PARTITION BY [RECORDID] ORDER BY [VENDPACKINGSLIPVERSION_RECORDID] DESC
            ) AS [RANK]
        FROM [dbo].[EAVendorPackingslipJournals] vpsj
    ) vpsj ON vpsj.[RECORDID] = src.[VENDPACKINGSLIPJOUR] AND vpsj.[RANK] = 1
    LEFT JOIN [dbo].[EAPurchaseOrders] po ON po.[PARTITION] = src.[PARTITION]
        AND po.[COMPANYCODE] = src.[COMPANYCODE]
        AND po.[INVENTORYTRANSACTIONID] = src.[INVENTTRANSID]
    -- 关联预聚合的台账数据
    LEFT JOIN LedgerAgg la 
        ON la.[PARTITION] = src.[PARTITION]
        AND la.[VOUCHERDATAAREAID] = src.[COMPANYCODE]
        AND la.[VOUCHER] = src.[COSTLEDGERVOUCHER]
    -- 关联预聚合的装箱单数量数据
    LEFT JOIN PackingSlipAgg psa
        ON psa.[PARTITION] = src.[PARTITION]
        AND psa.[COMPANYCODE] = src.[COMPANYCODE]
        AND psa.[COSTLEDGERVOUCHER] = src.[COSTLEDGERVOUCHER]
)
SELECT * FROM s;

2. 添加针对性覆盖索引

根据查询的筛选、关联和字段需求,创建以下覆盖索引可大幅提升性能:

  • EALedgerTransactions:CREATE NONCLUSTERED INDEX IX_EALedgerTransactions_Partition_Voucher ON [dbo].[EALedgerTransactions] ([PARTITION], [VOUCHERDATAAREAID], [VOUCHER], [POSTINGTYPE]) INCLUDE ([LEDGERDIMENSION], [MAINACCOUNTID], [ACCOUNTINGCURRENCYAMOUNT], [TRANSACTIONCURRENCYAMOUNT]);
  • EAVendorPackingslipTransactions:CREATE NONCLUSTERED INDEX IX_EAVendorPackingslipTransactions_Partition_CostVoucher ON [dbo].[EAVendorPackingslipTransactions] ([PARTITION], [COMPANYCODE], [COSTLEDGERVOUCHER]) INCLUDE ([QTY]);
  • EAVendorPackingslipJournals:CREATE NONCLUSTERED INDEX IX_EAVendorPackingslipJournals_RecordId_Version ON [dbo].[EAVendorPackingslipJournals] ([RECORDID], [VENDPACKINGSLIPVERSION_RECORDID]) INCLUDE ([INVOICEACCOUNT], [COMPANYCODE], [PURCHID], [PACKINGSLIPID], [ACCOUNTINGDATE], [LEDGERVOUCHER], [COSTLEDGERVOUCHER]);
  • EAPurchaseOrders:CREATE NONCLUSTERED INDEX IX_EAPurchaseOrders_Partition_TransId ON [dbo].[EAPurchaseOrders] ([PARTITION], [COMPANYCODE], [INVENTORYTRANSACTIONID]) INCLUDE ([ORDERACCOUNT], [PURCHREQLINEREFID], [PURCHPOOLID], [ITEMBUYERGROUPID], [CREATEDDATEANDTIME], [CURRENCYCODE], [POAMOUNT], [POQUANTITY]);

3. 验证连接类型必要性

检查vpsj和po的LEFT JOIN是否为业务必需:如果src对应的记录必然存在匹配的台账或采购单,可改为INNER JOIN,减少NULL值处理开销。


内容的提问来源于stack exchange,提问作者felchao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:50:37