建議創建表或設計儲存過程以刪除 while 循環
我目前工作的公司有一個場景,我們必須在 SQL 中使用 while 循環或游標。在我們的案例中,這會對性能產生巨大影響,因此,我想對其進行優化。
場景是我們有一個名為 的表
DocType
,將其視為主表。此表中的一列Name
包含類似的數據PO_Invoice, NON-PO_Invoice, GRN
。現在,還有其他表,例如XXX_PO_Invoices, XXX-PO_Invoices, XXX_GRN
. 所以基本上,在DocType
表上執行查詢,獲取名稱,然後我們必須將XXX
部分添加到這些名稱中,並在表中查詢XXX_PO_Invoices, XXX-PO_Invoices, XXX_GRN
。在表上執行的查詢DocType
會根據要求給出不同的結果,因此我們無法對其進行硬編碼。為了查詢XXX
表,我們使用 While 循環。有沒有更好的方法來實現這一目標?就像創建一個新表一樣?或者可以在查詢本身中完成一些事情。DocType 的表定義 -
[Id] [int] IDENTITY(1,1) NOT NULL, [IsActive] [bit] NOT NULL, [CreatedOn] [datetime] NOT NULL, [LastUpdated] [datetime] NOT NULL, [Name] [nvarchar](50) NOT NULL, [IsAP] [bit] NOT NULL, [ImportType] [int] NULL, [ImportTypeConfig] [int] NULL, [IndexingType] [nvarchar](50) NOT NULL, [ExtractionType] [nvarchar](50) NOT NULL, [OutgoingEmailAddress] [nvarchar](max) NULL, [MonitorEmail] [bit] NOT NULL, [InputFolder] [nvarchar](200) NULL, [ArchiveFolder] [nvarchar](200) NULL, [OutputFolder] [nvarchar](200) NULL, [StorageFolder] [nvarchar](200) NULL, [ExportFolder] [nvarchar](200) NULL, [FTP_URL] [nvarchar](200) NULL, [ExternalValidationEnabled] [bit] NOT NULL, [ApprovalEnabled] [bit] NOT NULL, [CodingEnabled] [bit] NOT NULL, [ManualVerificationEnabled] [bit] NOT NULL, [PostApprovalEnabled] [bit] NOT NULL, [PostEnabled] [bit] NOT NULL, [InputFolder_Conversion] [nvarchar](256) NULL, [IsRealIndexExtraction] [bit] NOT NULL, [StoreSupplierTemplate] [bit] NOT NULL, [AcceptHardCopy] [bit] NOT NULL, [DestructionDate] [datetime] NULL, [DisplayName] [nvarchar](550) NULL, [RenameFile] [bit] NOT NULL, [CreateDocumentRecord] [bit] NOT NULL, [SendInvalidAttachmentNotification] [bit] NOT NULL, [SendReassignNotification] [bit] NOT NULL, [AddEmailMetadataToComment] [bit] NOT NULL, [AddEmailAttachmentToDocument] [bit] NOT NULL, [SendCodingNotification] [bit] NOT NULL, [SendApprovalNotification] [bit] NOT NULL, [InputFolder_HTML_Conversion] [nvarchar](200) NULL, [ManualSeperationEnabled] [bit] NOT NULL, [WizardStatus] [int] NOT NULL, [CreateDefaultLine] [bit] NOT NULL, [IsGRN] [bit] NOT NULL, [IsPO] [bit] NOT NULL, [POMatchingEnabled] [bit] NOT NULL, [RequestParkReason] [bit] NOT NULL, [RequestDeleteReason] [bit] NOT NULL, [RequestReverifyReason] [bit] NOT NULL, [IsManualMonitor] [bit] NOT NULL, [ManualMonitorFolder] [nvarchar](200) NULL, [SplitFolder] [nvarchar](200) NULL, [BaseCurrency] [nvarchar](50) NULL, [AutomaticCodingEnabled] [bit] NOT NULL, [DisplayLineTotals] [bit] NOT NULL, [hasRelatedDocuments] [bit] NOT NULL, [HardDeleteInMonths] [int] NULL, [CopyPOLinesInVerification] [bit] NOT NULL, [IsBudget] [bit] NOT NULL, [HasBudget] [bit] NOT NULL, [POLineMatching] [bit] NOT NULL, [IsSupplierReconciliation] [bit] NOT NULL, [SendDocumentRejectedNotification] [bit] NOT NULL, [GenerateOnTheFly] [bit] NOT NULL, [LogoFile] [varbinary](max) NULL, [RequestRecreateReason] [bit] NOT NULL, [DocumentTypeGroup] [int] NULL, [POOriginatorAccessEnabled] [bit] NOT NULL, CONSTRAINT [PK_DocumentType] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [PostApprovalEnabled] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsRealIndexExtraction] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [StoreSupplierTemplate] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [AcceptHardCopy] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [RenameFile] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [CreateDocumentRecord] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [SendInvalidAttachmentNotification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [SendReassignNotification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [AddEmailMetadataToComment] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [AddEmailAttachmentToDocument] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [SendCodingNotification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((1)) FOR [SendApprovalNotification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [ManualSeperationEnabled] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((2)) FOR [WizardStatus] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [CreateDefaultLine] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsGRN] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsPO] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [POMatchingEnabled] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [RequestParkReason] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [RequestDeleteReason] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [RequestReverifyReason] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsManualMonitor] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [AutomaticCodingEnabled] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [DisplayLineTotals] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [hasRelatedDocuments] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [CopyPOLinesInVerification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsBudget] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [HasBudget] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [POLineMatching] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [IsSupplierReconciliation] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [SendDocumentRejectedNotification] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [GenerateOnTheFly] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [RequestRecreateReason] GO ALTER TABLE [dbo].[DocumentType] ADD DEFAULT ((0)) FOR [POOriginatorAccessEnabled] GO
XXX_table 之一的表定義
CREATE TABLE [dbo].[Doc_IE10_POInvoices]( [Id] [INT] IDENTITY(1,1) NOT NULL, [IsActive] [BIT] NOT NULL, [CreatedOn] [DATETIME] NOT NULL, [LastUpdated] [DATETIME] NOT NULL, [Supplier_Name] [NVARCHAR](MAX) NULL, [Supplier_Code] [NVARCHAR](MAX) NULL, [Invoice_Number] [NVARCHAR](MAX) NULL, [Debit_Credit] [NVARCHAR](MAX) NULL, [PO_NUMBER] [NVARCHAR](MAX) NULL, [Invoice_Date] [DATETIME] NULL, [Currency] [NVARCHAR](MAX) NULL, [Net_Total] [DECIMAL](18, 4) NULL, [Tax_Total] [DECIMAL](18, 4) NULL, [Invoice_Total] [DECIMAL](18, 4) NULL, [Template_Type] [NVARCHAR](MAX) NULL, [SAP_Reference] [NVARCHAR](MAX) NULL, [SAP_PaymentDueDate] [DATETIME] NULL, [SAP_Status] [NVARCHAR](MAX) NULL, [Description] [NVARCHAR](MAX) NULL, [Company_Code] [NVARCHAR](MAX) NULL, [Debit_Credit_Type] [NVARCHAR](MAX) NULL, [ParkReason] [NVARCHAR](MAX) NULL, [PaymentTermsDate] [DATETIME] NULL, [Invoice_Comment] [NVARCHAR](MAX) NULL, [Original_PO_NUMBER] [NVARCHAR](MAX) NULL, CONSTRAINT [PK_Doc_IE10_POInvoices_636945689932953689] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO
XXX_
實際上'Doc_'
只是。我不好讓它更混亂。以下是使用循環的查詢之一。ALTER PROCEDURE [dbo].[GetAllPONumberInInvoices_OLD] @invoice_table NVARCHAR(MAX) = NULL AS BEGIN IF OBJECT_ID('tempdb..#PONumbersInInvoices') IS NOT NULL DROP TABLE #PONumbersInInvoices; CREATE TABLE #PONumbersInInvoices ( ID INT, PO_NUMBER NVARCHAR(MAX), Supplier_Name NVARCHAR(MAX), Supplier_Code NVARCHAR(MAX), Net_Total decimal(18,4), Tax_Total decimal(18,4) ) IF OBJECT_ID('tempdb..#MultiPONumbersInInvoices') IS NOT NULL DROP TABLE #MultiPONumbersInInvoices; CREATE TABLE #MultiPONumbersInInvoices ( ID INT, PO_NUMBER NVARCHAR(MAX), Supplier_Name NVARCHAR(MAX), Supplier_Code NVARCHAR(MAX), Net_Total decimal(18,4), Tax_Total decimal(18,4) ) DECLARE @QueryToBeExecuted NVARCHAR(MAX) SET @QueryToBeExecuted = ' INSERT INTO #PONumbersInInvoices SELECT [Id],[PO_NUMBER],[Supplier_Name], [Supplier_Code], [Net_Total], [Tax_Total] FROM ' + @invoice_table + ' WHERE [PO_NUMBER] NOT LIKE ''%,%'' AND IsActive = 1' EXECUTE sp_executesql @QueryToBeExecuted SET @QueryToBeExecuted = ' INSERT INTO #MultiPONumbersInInvoices SELECT [Id],[PO_NUMBER], [Supplier_Name], [Supplier_Code], [Net_Total], [Tax_Total] FROM ' + @invoice_table + ' WHERE [PO_NUMBER] LIKE ''%,%'' AND IsActive = 1' EXECUTE sp_executesql @QueryToBeExecuted DECLARE @MultiPOIndex INT= 0; DECLARE @MultiPOCount INT; SELECT @MultiPOCount = Count(*) FROM #MultiPONumbersInInvoices; WHILE(@MultiPOIndex < @MultiPOCount) BEGIN DECLARE @InvoiceId Int; DECLARE @PONumber NVARCHAR(MAX) DECLARE @Supplier_Name NVARCHAR(MAX) DECLARE @Supplier_Code NVARCHAR(MAX) DECLARE @Net_Total decimal(18,4) DECLARE @Tax_Total decimal(18,4) SELECT TOP(1) @InvoiceId = Id, @PONumber = PO_NUMBER, @Supplier_Name = Supplier_Name, @Supplier_Code = Supplier_Code, @Net_Total = Net_Total, @Tax_Total = Tax_Total FROM #MultiPONumbersInInvoices INSERT INTO #PONumbersInInvoices SELECT @InvoiceId AS Id, [Value] AS PO_NUMBER, @Supplier_Name AS Supplier_Name, @Supplier_Code AS Supplier_Code, @Net_Total AS Net_Total, @Tax_Total AS Tax_Total FROM dbo.Split(@PONumber,',') DELETE FROM #MultiPONumbersInInvoices WHERE Id = @InvoiceId; SET @MultiPOIndex += 1; END SELECT * FROM #PONumbersInInvoices IF OBJECT_ID('tempdb..#PONumbersInInvoices') IS NOT NULL DROP TABLE #PONumbersInInvoices; IF OBJECT_ID('tempdb..#MultiPONumbersInInvoices') IS NOT NULL DROP TABLE #MultiPONumbersInInvoices; END;
然後@invoice_table 由另一個過程提供,該過程執行以下操作
SELECT Id, 'Doc_'+Name AS TableName INTO ##DocumentTypes_with_POMatchingEnabled FROM DocType WHERE(POMatchingEnabled = 1) AND IsActive = 1 and IsAP=1; WHILE(@Index < @DocumentTypes_with_POMatchingEnabledCount) BEGIN DECLARE @TableName NVARCHAR(150), @PoTable NVARCHAR(MAX), @GrnTable NVARCHAR(MAX), @PO_NUMBER NVARCHAR(MAX); DECLARE @DocumentTypeId INT, @DocumentRecordId INT, @DocumentTypePoId INT, @DocumentTypeGrnId INT, @DocumentId INT; SELECT TOP (1) @TableName = TableName, @DocumentTypeId = Id FROM ##DocumentTypes_with_POMatchingEnabled; -- GET PO NUMBERS IN INVOICES IF OBJECT_ID('tempdb..#PONumbersInInvoicesTemp') IS NOT NULL DROP TABLE #PONumbersInInvoicesTemp; CREATE TABLE #PONumbersInInvoicesTemp (ID INT, PO_NUMBER NVARCHAR(MAX), Supplier_Name NVARCHAR(MAX), Supplier_Code NVARCHAR(MAX), Net_Total DECIMAL(18, 4), Tax_Total DECIMAL(18, 4) ); INSERT INTO #PONumbersInInvoicesTemp EXEC GetAllPONumberInInvoices @invoice_table = @TableName; -- GET DOCUMENT TYPE ID OF PO SELECT @DocumentTypePoId = dt.Id, @PoTable = 'Doc_'+dt.Name FROM DocumentTypeAssociation dta INNER JOIN DocumentType dt ON dt.Id = dta.DocumentTypeId2 WHERE DocumentTypeId1 = @DocumentTypeId AND dt.IsPO = 1; -- GET DOCUMENT TYPE ID OF GRN SELECT @DocumentTypeGrnId = dt.Id, @GrnTable = 'Doc_'+dt.Name FROM DocumentTypeAssociation dta INNER JOIN DocumentType dt ON dt.Id = dta.DocumentTypeId2 WHERE DocumentTypeId1 = @DocumentTypeId AND dt.IsGRN = 1; DECLARE @SqlStatement NVARCHAR(MAX); SELECT @SqlStatement = (' INSERT INTO ##InvoicesWithPoAndWithoutGrnReportResults SELECT docPO.Id AS DocumentId, docInvoice.PO_NUMBER AS PO_NUMBER, '''+@TableName+''' AS INVOICE_TABLE, '''+@PoTable+''' AS PO_TABLE, '''+@GrnTable+''' AS GRN_TABLE, docInvoice.Supplier_Name, docInvoice.Supplier_Code, CASE WHEN docPO.Originator LIKE ''%_@__%.__%'' THEN docPO.Originator WHEN '+CONVERT(NVARCHAR, @userMappingTableId)+' > 0 THEN ( SELECT TOP 1 [Value] FROM LookupDataCustom WHERE DocumentTypeId = '+CONVERT(NVARCHAR, @DocumentTypeId)+' AND IsActive = 1 AND LookupDataTableId = '+CONVERT(NVARCHAR, @userMappingTableId)+' AND [Value] LIKE ''%_@__%.__%'' AND [Name] = docPO.Originator) ELSE '''' END AS Originator, case when DocumentRecord.Status =5 then '''+@loginUrl+'Document/Verify/''+CONVERT(NVARCHAR, DocumentRecord.Id) else '''+@loginUrl+'Document/View/''+CONVERT(NVARCHAR, DocumentRecord.Id) end, docInvoice.Net_Total, docInvoice.Tax_Total FROM #PONumbersInInvoicesTemp docInvoice inner join DocumentRecord on DocumentRecord.DocId=docInvoice.Id and DocumentRecord.DocumentTypeId='+CONVERT(NVARCHAR, @DocumentTypeId)+' INNER JOIN '+@PoTable+' docPO ON docPO.PO_NUMBER = docInvoice.PO_NUMBER and docPO.Supplier_Code = docInvoice.Supplier_Code and docPO.IsActive = 1 LEFT JOIN '+@GrnTable+' docGRN ON docGRN.PO_NUMBER = docInvoice.PO_NUMBER and docGRN.Supplier_Code = docInvoice.Supplier_Code and docGRN.IsActive = 1 WHERE docGRN.Id IS NULL and DocumentRecord.Status in (5,24,28) ' + @grnNotificationDelay + ' Order by docPO.Originator,docInvoice.Supplier_Name; '); EXEC sp_executesql @SqlStatement; DELETE FROM ##DocumentTypes_with_POMatchingEnabled WHERE Id = @DocumentTypeId; SELECT @Index = (@Index + 1); END;
沒有分享整個查詢,因為它可能更令人困惑。
請在您的文章中提供您目前使用的查詢範例以及您的表定義。就目前而言,您的問題不是很清楚,尤其是關於循環在此處甚至是如何相關的。
話雖如此,根據您提供的目前資訊,聽起來你們可能會通過利用系統儲存過程從動態 SQL
sp_executesql
中受益。使用動態 SQL,您可以根據儲存在表中的表名建構要執行的查詢的 SQL 字元串DocType
。然後你可以用sp_executesql
.您還可以使用系統視圖
sys.tables
作為獲取“XXX”前綴表的實際表名列表的一種方式。從技術上講,您可以在它和您的DocType
表之間進行萬用字元連接,以幫助建構動態 SQL,如下所示:SELECT T.[name] FROM sys.tables AS T INNER JOIN DocType AS D ON T.[name] LIKE '%' + D.[Name]
這是假設當您說“XXX”時,您在問題中使用它作為佔位符,但實際上它是一組不同的值。但是,如果您的意思是它們確實都以三個 X 開頭,那麼您甚至不需要上述查詢。
DocType
相反,您可以在從表中選擇時自己添加“XXX”,如下所示:SELECT 'XXX_' + [Name] FROM DocType
如果確實有兩個名稱都帶有下劃線前綴而其他名稱帶有破折號,則可以將上述兩個查詢組合起來,以確保僅使用 a 選擇有效的表名,
UNION ALL
如下所示:SELECT 'XXX_' + D.[Name] FROM DocType AS D INNER JOIN sys.tables AS T ON 'XXX_' + D.[Name] = T.[name] UNION ALL SELECT 'XXX-' + D.[Name] FROM DocType AS D INNER JOIN sys.tables AS T ON 'XXX-' + D.[Name] = T.[name]
如果您像我最初提到的那樣更新您的文章,我們可能會為您提供更具體的建議。