I have database that include FinancialYears in each table for Invoices, the problem is that when I call query to show the Invoice list for example for 2024 and then change the app to 2023 to get query on last year it gives me a time out error this is my invoice header table schema
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
and following is my detail invoice table schema
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
Note: in 2023 I have 6600 invoice inserted in my table. and in total from 2018 to 2024 I have 106000 record inserted for my invoices. following is my query :
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;
It appears that your
GROUP BYis grouping the entirety of eachInvoiceHeaderrow, while the other table references are only used to calculate sums for details and payments. In that case, I believe that is would be simpler to select directly fromInvoiceHeaderonly at the top level and useCROSS APPLYsubqueries to calculate the various sums.The other thing I see, and this is a big red flag, is that your posted query appears to have multiple independent one-to-many joins. This will almost never yield the correct result. If an invoice had three details, two cash payments and two cheque payments, the details would be overstated by a factor of 4 and payments would each be overstated by a factor of 6. The fix is to isolate each one-to-many relationship into a separate
CROSS APPLYand calculate the totals of each independently.The
DocumentNumbercan also (optionally) be moved to aCROSS APPLY, solely for the purpose of reducing clutter in the main select list.These
CROSS APPLYresults can then be referenced in the top level select list.The updated query would be something like:
A
CROSS APPLYis like anINNER JOINto a subselect. For each usage above, the aggregate functions will always produce a single scalar result, so each should produce exactly one row. (If that was not the case, anOUTER APPLYwould have been appropriate - equivalent to aLEFT JOINto a subselect.)I wrapped the
SUM()s up in additionalISNULL()functions to ensure a zero result if no matching rows were found. The innerISNULL()function references could be eliminated if you don't mind the "Null value is eliminated by an aggregate or other SET operation" warnings.As siggemannen already pointed out in an earlier comment, the
dbo.getname()function call may be significantly impacting performance. I suggest testing your query both with and without this function call. If significantly different, review the function to see if its performance can be improved or if its logic can be merged into the above query. Another option might be to addAccountTopicas a persistent computed column in theInvoiceHeadertable.I presume that:
Be sure to test the final query using all combinations of the above conditions, carefully checking that the calculated sums are correct.
Also, check your
InvoiceDetailjoin conditions. The referenced columns are not the same as defined in yourFK_InvoiceDetail_InvoiceHeaderforeign key constraint. (The other three table definitions were not posted, but might also be worth a review.)