会计软件发票清单中的sql表现

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

我的数据库在每个发票表中都包含 FinancialYears,问题是当我调用查询来显示发票列表(例如 2024 年),然后将应用程序更改为 2023 年以获取去年的查询时,它会给我一个超时错误 这是我的发票标题表架构

CREATE TABLE [Sales].[InvoiceHeader](
    [InvoiceID] [int] IDENTITY(1,1) NOT NULL,
    [InvoiceNumber] [int] NOT NULL,
    [InvoiceNumber1] [nvarchar](50) NULL,
    [OnlineInvoiceFlag] [bit] NULL,
    [RecordType] [smallint] NULL,
    [InvoiceKindFK] [int] NOT NULL,
    [StoreFK] [int] NULL,
    [IsOther] [bit] NULL,
    [OtherName] [nvarchar](100) NULL,
    [OtherNationalNo] [nvarchar](50) NULL,
    [AccountGroupFK] [int] NULL,
    [AccountFK] [int] NULL,
    [PaymentTermFK] [int] NULL,
    [DeliverAddress] [nvarchar](256) NULL,
    [Date] [char](10) NULL,
    [Time] [datetime] NULL,
    [Description] [nvarchar](256) NULL,
    [SubTotal] [decimal](18, 0) NULL,
    [Reduction] [decimal](18, 0) NULL,
    [Extra] [decimal](18, 0) NULL,
    [Discount] [decimal](18, 0) NULL,
    [ProjectFK] [int] NULL,
    [CostCenterFK] [nvarchar](10) NULL,
    [MarketerAccountFK] [nvarchar](50) NULL,
    [MarketingCost] [decimal](18, 0) NULL,
    [DriverAccountFK] [nvarchar](50) NULL,
    [DriverWages] [decimal](18, 0) NULL,
    [SettelmentDate] [char](10) NULL,
    [DueDate] [char](10) NULL,
    [PrintCount] [tinyint] NULL,
    [InvoiceFK] [int] NULL,
    [LetterFK] [int] NULL,
    [FinancialPeriodFK] [tinyint] NOT NULL,
    [CompanyInfoFK] [tinyint] NULL,
    [OldSysDoc] [nvarchar](50) NULL,
 CONSTRAINT [PK_InvoiceHeader] PRIMARY KEY CLUSTERED 
(
    [FinancialPeriodFK] ASC,
    [InvoiceKindFK] ASC,
    [InvoiceID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Sales].[InvoiceHeader] ADD  CONSTRAINT [DF_InvoiceHeader_Time]  DEFAULT (getdate()) FOR [Time]
GO

以下是我的详细发票表架构

CREATE TABLE [Sales].[InvoiceDetail](
    [InvoiceFK] [int] NOT NULL,
    [InvoiceDetailID] [int] IDENTITY(1,1) NOT NULL,
    [InvoiceNumberFK] [int] NOT NULL,
    [InvoiceKindFK] [int] NOT NULL,
    [RecordType] [smallint] NULL,
    [ItemDescription] [nvarchar](256) NULL,
    [Date] [char](10) NULL,
    [Time] [datetime] NULL,
    [StoreFK] [int] NOT NULL,
    [ProductFK] [int] NULL,
    [OrderQty] [float] NULL,
    [UnitPrice] [decimal](18, 0) NULL,
    [BackPrice] [decimal](18, 0) NULL,
    [UnitPriceDiscountPercent] [decimal](18, 0) NULL,
    [DiscountAmount] [decimal](18, 0) NULL,
    [UnitPriceVatPercent] [decimal](18, 0) NULL,
    [VatAmount] [decimal](18, 0) NULL,
    [UnitPriceTaxPercent] [decimal](18, 0) NULL,
    [TaxAmount] [decimal](18, 0) NULL,
    [TransportCost] [decimal](18, 0) NULL,
    [LineTotal] [decimal](18, 0) NULL,
    [WayBillNumber] [nvarchar](20) NULL,
    [ContractNumber] [nvarchar](20) NULL,
    [VehicleNo] [nvarchar](20) NULL,
    [DeliverFK] [int] NULL,
    [NTSW] [nvarchar](512) NULL,
    [FinancialPeriodFK] [tinyint] NOT NULL,
    [CompanyInfoFK] [tinyint] NULL,
 CONSTRAINT [PK_InvoiceDetail] PRIMARY KEY CLUSTERED 
(
    [FinancialPeriodFK] ASC,
    [InvoiceKindFK] ASC,
    [InvoiceDetailID] ASC,
    [InvoiceFK] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Sales].[InvoiceDetail] ADD  CONSTRAINT [DF_InvoiceDetail_Time]  DEFAULT (getdate()) FOR [Time]
GO

ALTER TABLE [Sales].[InvoiceDetail]  WITH CHECK ADD  CONSTRAINT [FK_InvoiceDetail_InvoiceHeader] FOREIGN KEY([FinancialPeriodFK], [InvoiceKindFK], [InvoiceFK])
REFERENCES [Sales].[InvoiceHeader] ([FinancialPeriodFK], [InvoiceKindFK], [InvoiceID])
ON UPDATE CASCADE
ON DELETE CASCADE
GO

ALTER TABLE [Sales].[InvoiceDetail] CHECK CONSTRAINT [FK_InvoiceDetail_InvoiceHeader]
GO

注意:2023 年,我的表格中插入了 6600 张发票。 从 2018 年到 2024 年,我总共为发票插入了 106000 条记录。 以下是我的查询:

SELECT M.InvoiceID,
       M.InvoiceNumber,
       M.InvoiceNumber1,
       M.IsOther,
       M.OtherName,
       M.OtherNationalNo,
       M.Date,
       (SUM(ISNULL(D.LineTotal, 0)) + SUM(ISNULL(M.Extra, 0)) - SUM(ISNULL(M.Reduction, 0)) + SUM(ISNULL(D.DiscountAmount, 0))) AS SumTotal,
       (SUM(ISNULL(D.DiscountAmount, 0))) AS Discount,
       (SUM(ISNULL(D.VatAmount, 0))) AS Vat,
       (SUM(ISNULL(D.TaxAmount, 0))) AS Tax,
       (SUM(ISNULL(D.LineTotal, 0)) - SUM(ISNULL(D.VatAmount, 0)) - SUM(ISNULL(D.TaxAmount, 0)) - SUM(ISNULL(M.Extra, 0)) - SUM(ISNULL(M.Reduction, 0))) AS TotalNet,
       M.OnlineInvoiceFlag,
       M.RecordType,
       M.InvoiceKindFK,
       M.StoreFK,
       M.AccountFK,
       M.PaymentTermFK,
       M.DeliverAddress,
       (SELECT MAX(DocumentFK)
        FROM Accounting.DocumentDetail
        WHERE ItemFK = @Item + CAST(M.InvoiceNumber AS nvarchar(10))
          AND documenttypeid = @DocumentTypeFK
          AND financialPeriodFK = @FinancialPeriodFK) AS DocumentNumber,
       M.Time,
       M.Description,
       M.SubTotal,
       M.Reduction,
       M.Extra,
       M.ProjectFK,
       M.CostCenterFK,
       M.MarketerAccountFK,
       M.MarketingCost,
       M.DriverAccountFK,
       M.DriverWages,
       M.SettelmentDate,
       M.DueDate,
       M.FinancialPeriodFK,
       M.CompanyInfoFK,
       M.PrintCount,
       M.LetterFK,
       M.InvoiceFK,
       dbo.getname(M.AccountFK, M.AccountGroupFK, M.FinancialPeriodFK) AS AccountTopic,
       AccountGroupFK,
       SUM(Banking.ReceivedCash.Price) AS ReceivedCash,
       SUM(Banking.ReceivedCheque.Price) AS ReceivedCheque
FROM Sales.InvoiceHeader M
    LEFT JOIN Sales.InvoiceDetail D ON M.InvoiceID = D.InvoiceFK
                                   AND M.InvoiceKindFK = D.InvoiceKindFK
                                   AND D.FinancialPeriodFK = M.FinancialPeriodFK
    LEFT JOIN Banking.ReceivedCash ON M.InvoiceNumber = Banking.ReceivedCash.SalesInvoiceHeaderFK
                                  AND Banking.ReceivedCash.FinancialPeriodFK = M.FinancialPeriodFK
    LEFT JOIN Banking.ReceivedCheque ON M.InvoiceNumber = Banking.ReceivedCheque.SalesInvoiceHeaderFK
                                    AND Banking.ReceivedCheque.FinancialPeriodFK = M.FinancialPeriodFK
WHERE ( (M.InvoiceKindFK = @InvoiceKindFK)
    AND (M.FinancialPeriodFK = @FinancialPeriodFK))
GROUP BY M.InvoiceID,
         M.InvoiceNumber,
         M.InvoiceNumber1,
         M.IsOther,
         M.OtherName,
         M.OtherNationalNo,
         M.Date,
         M.OnlineInvoiceFlag,
         M.RecordType,
         M.InvoiceKindFK,
         M.InvoiceNumber,
         M.StoreFK,
         M.AccountFK,
         M.PaymentTermFK,
         M.DeliverAddress,
         M.Time,
         M.Description,
         M.SubTotal,
         M.Reduction,
         M.Extra,
         M.ProjectFK,
         M.CostCenterFK,
         M.MarketerAccountFK,
         M.MarketingCost,
         M.DriverAccountFK,
         M.DriverWages,
         M.SettelmentDate,
         M.DueDate,
         M.FinancialPeriodFK,
         M.OldSysDoc,
         M.CompanyInfoFK,
         M.PrintCount,
         M.LetterFK,
         M.InvoiceFK,
         M.AccountGroupFK
ORDER BY M.StoreFK,
         M.InvoiceNumber;
sql-server timeout accounting
1个回答
1
投票

您的

GROUP BY
似乎正在对每个
InvoiceHeader
行的整体进行分组,而其他表引用仅用于计算详细信息和付款的总和。在这种情况下,我相信仅在顶层直接从
InvoiceHeader
选择并使用
CROSS APPLY
子查询来计算各种总和会更简单。

我看到的另一件事,这是一个“大红旗”,是您发布的查询似乎有“多个独立的一对多连接”。这几乎永远不会产生正确的结果。如果发票包含三个详细信息(两次现金付款和两次支票付款),则详细信息将被夸大 4 倍,每笔付款将被夸大 6 倍。解决方法是将每个一对多关系隔离为一个单独的 CROSS APPLY 并独立计算每个的总数。

DocumentNumber
也可以(可选)移动到

CROSS APPLY

,仅仅是为了减少主选择列表中的混乱。

这些 
CROSS APPLY
结果可以在顶级选择列表中引用。

更新后的查询将类似于:

SELECT M.InvoiceID,
       M.InvoiceNumber,
       M.InvoiceNumber1,
       M.IsOther,
       M.OtherName,
       M.OtherNationalNo,
       M.Date,
       DSUM.LineTotal + DSUM.Extra - DSUM.Reduction + DSUM.Discount AS SumTotal,
       DSUM.Discount,
       DSUM.Vat,
       DSUM.Tax,
       DSUM.LineTotal - DSUM.Vat - DSUM.Tax - DSUM.Extra - DSUM.Reduction AS TotalNet,
       M.OnlineInvoiceFlag,
       M.RecordType,
       M.InvoiceKindFK,
       M.StoreFK,
       M.AccountFK,
       M.PaymentTermFK,
       M.DeliverAddress,
       DN.DocumentNumber,
       M.Time,
       M.Description,
       M.SubTotal,
       M.Reduction,
       M.Extra,
       M.ProjectFK,
       M.CostCenterFK,
       M.MarketerAccountFK,
       M.MarketingCost,
       M.DriverAccountFK,
       M.DriverWages,
       M.SettelmentDate,
       M.DueDate,
       M.FinancialPeriodFK,
       M.CompanyInfoFK,
       M.PrintCount,
       M.LetterFK,
       M.InvoiceFK,
       dbo.getname(M.AccountFK, M.AccountGroupFK, M.FinancialPeriodFK) AS AccountTopic,
       M.AccountGroupFK,
       RCSUM.ReceivedCash,
       RQSUM.ReceivedCheque
FROM Sales.InvoiceHeader M
CROSS APPLY (
    SELECT
        ISNULL(SUM(ISNULL(D.LineTotal, 0)), 0) AS LineTotal,
        ISNULL(SUM(ISNULL(M.Extra, 0)), 0) AS Extra,
        ISNULL(SUM(ISNULL(M.Reduction, 0)), 0) AS Reduction,
        ISNULL((SUM(ISNULL(D.DiscountAmount, 0)), 0) AS Discount,
        ISNULL((SUM(ISNULL(D.VatAmount, 0)), 0) AS Vat,
        ISNULL((SUM(ISNULL(D.TaxAmount, 0)), 0) AS Tax
    FROM Sales.InvoiceDetail D
    WHERE D.InvoiceFK = M.InvoiceID
    AND D.InvoiceKindFK = M.InvoiceKindFK
    AND D.FinancialPeriodFK = M.FinancialPeriodFK
) DSUM
CROSS APPLY (
    SELECT ISNULL(SUM(RC.Price), 0) AS ReceivedCash
    FROM Banking.ReceivedCash RC
    WHERE RC.SalesInvoiceHeaderFK = M.InvoiceNumber
    AND RC.FinancialPeriodFK = M.FinancialPeriodFK
) RCSUM
CROSS APPLY (
    SELECT ISNULL(SUM(Banking.ReceivedCheque.Price), 0) AS ReceivedCheque
    FROM Banking.ReceivedCheque RQ
    WHERE RQ.SalesInvoiceHeaderFK = M.InvoiceNumber
    AND RQ.FinancialPeriodFK = M.FinancialPeriodFK
) RQSUM
CROSS APPLY (
    SELECT MAX(DocumentFK) AS DocumentNumber
    FROM Accounting.DocumentDetail DD
    WHERE DD.ItemFK = @Item + CAST(M.InvoiceNumber AS nvarchar(10))
    AND DD.documenttypeid = @DocumentTypeFK
    AND DD.financialPeriodFK = @FinancialPeriodFK
) DN
WHERE ( (M.InvoiceKindFK = @InvoiceKindFK)
    AND (M.FinancialPeriodFK = @FinancialPeriodFK))
ORDER BY M.StoreFK,
         M.InvoiceNumber;

A

CROSS APPLY
 就像子选择的 
INNER JOIN

一样。对于上述每种用法,聚合函数将始终生成单个标量结果,因此每个函数都应该生成一行。 (如果不是这种情况,则

OUTER APPLY
是合适的 - 相当于子选择的
LEFT JOIN
。)
我将 
SUM()
包装在额外的

ISNULL()

函数中,以确保在未找到匹配行时结果为零。

我认为:

每张发票应至少有一个或多个详细信息行。

每张发票可能有零、一个或多个现金支付行。
  • 每张发票可能有零、一个或多个支票付款行。
  • 请务必使用上述条件的所有组合来测试最终查询,仔细检查计算出的总和是否正确。
  • 另请检查您的
InvoiceDetail

加入条件。引用的列与您的

FK_InvoiceDetail_InvoiceHeader

外键约束中定义的列不同。 (其他三个表定义没有发布,但也可能值得回顾一下。)

    

© www.soinside.com 2019 - 2024. All rights reserved.