我的数据库在每个发票表中都包含 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;
您的
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
外键约束中定义的列不同。 (其他三个表定义没有发布,但也可能值得回顾一下。)