UNPKG

bc-code-intelligence-mcp

Version:

BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows

298 lines (256 loc) 10.2 kB
# DeleteAll SQL Translation and Performance - Examples ## Performance Comparison: DeleteAll vs Loop ```al // High-Performance Approach: DeleteAll for bulk operations // Translates to single SQL DELETE statement codeunit 50200 "Order Cleanup Manager" { procedure CleanupOldOrders(CutoffDate: Date): Integer var SalesHeader: Record "Sales Header"; DeletedCount: Integer; begin // Single SQL operation - optimal for large datasets SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Quote); SalesHeader.SetFilter("Document Date", '<%1', CutoffDate); SalesHeader.SetRange(Status, SalesHeader.Status::Open); // Count before deletion for reporting DeletedCount := SalesHeader.Count(); // Execute as single SQL DELETE statement // SQL: DELETE FROM "Sales Header" WHERE "Document Type" = 1 // AND "Document Date" < @CutoffDate AND Status = 0 SalesHeader.DeleteAll(); exit(DeletedCount); end; procedure CleanupByStatus(DocumentType: Enum "Sales Document Type"; StatusFilter: Text) var SalesHeader: Record "Sales Header"; begin SalesHeader.SetRange("Document Type", DocumentType); SalesHeader.SetFilter(Status, StatusFilter); // Leverages database indexing for optimal performance // SQL engine handles WHERE clause optimization SalesHeader.DeleteAll(); end; } ``` ## Event Bypass Behavior ```al // Understanding Event Bypass: DeleteAll skips AL table events codeunit 50201 "Archive vs Delete Demo" { procedure SafeBulkDeletion(var SalesHeader: Record "Sales Header") var TempSalesHeader: Record "Sales Header" temporary; ArchiveManagement: Codeunit ArchiveManagement; begin // Collect records for event processing if needed if SalesHeader.FindSet() then repeat TempSalesHeader := SalesHeader; TempSalesHeader.Insert(); until SalesHeader.Next() = 0; // Process AL events manually if required if TempSalesHeader.FindSet() then repeat // Execute necessary validations/archiving before bulk delete ArchiveManagement.StoreSalesDocument(TempSalesHeader, false); until TempSalesHeader.Next() = 0; // Now safe to use DeleteAll - events already processed SalesHeader.DeleteAll(); end; procedure CompareEventBehavior() var SalesLine: Record "Sales Line"; SingleLine: Record "Sales Line"; begin // DELETEALL APPROACH: Events bypassed SalesLine.SetRange("Document No.", 'ORDER001'); SalesLine.DeleteAll(); // OnDelete triggers NOT called // ITERATIVE APPROACH: Events execute SingleLine.SetRange("Document No.", 'ORDER002'); if SingleLine.FindSet() then repeat SingleLine.Delete(); // OnDelete triggers ARE called until SingleLine.Next() = 0; end; } ``` ## SQL Translation Patterns ```al // Filter optimization impacts SQL DELETE performance codeunit 50202 "SQL Optimization Examples" { procedure OptimizedFilters() var ItemLedgerEntry: Record "Item Ledger Entry"; begin // GOOD: Uses indexed fields for optimal SQL performance ItemLedgerEntry.SetRange("Item No.", 'ITEM001'); ItemLedgerEntry.SetRange("Posting Date", 20240101D, 20241231D); ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Sale); // SQL: DELETE FROM "Item Ledger Entry" // WHERE "Item No." = 'ITEM001' // AND "Posting Date" BETWEEN '2024-01-01' AND '2024-12-31' // AND "Entry Type" = 1 // Database can use compound index efficiently ItemLedgerEntry.DeleteAll(); end; procedure SuboptimalFilters() var ItemLedgerEntry: Record "Item Ledger Entry"; begin // SUBOPTIMAL: Complex filters may require full table scan ItemLedgerEntry.SetFilter("Description", '@*SAMPLE*'); ItemLedgerEntry.SetFilter("Quantity", '>0&<100'); // SQL: DELETE FROM "Item Ledger Entry" // WHERE "Description" LIKE '%SAMPLE%' // AND "Quantity" > 0 AND "Quantity" < 100 // May require table scan without proper indexing ItemLedgerEntry.DeleteAll(); end; } ``` ## Transaction Boundary Considerations ```al // Managing large DeleteAll operations within transaction limits codeunit 50203 "Batch Delete Manager" { procedure LargeBatchDeletion(TableID: Integer; FilterText: Text): Boolean var RecRef: RecordRef; TotalRecords: Integer; BatchSize: Integer; ProcessedCount: Integer; begin RecRef.Open(TableID); RecRef.SetView(FilterText); TotalRecords := RecRef.Count(); BatchSize := 10000; // Adjust based on transaction log limits if TotalRecords <= BatchSize then begin // Single DeleteAll for smaller datasets RecRef.DeleteAll(); exit(true); end; // Process in batches for very large datasets repeat RecRef.SetView(FilterText); if RecRef.FindSet() then begin ProcessedCount := 0; repeat RecRef.Delete(); ProcessedCount += 1; if ProcessedCount >= BatchSize then begin Commit(); // Release transaction log space RecRef.SetView(FilterText); RecRef.FindFirst(); ProcessedCount := 0; end; until (RecRef.Next() = 0) or (ProcessedCount >= BatchSize); end; until RecRef.Count() = 0; exit(true); end; } ``` ## Performance Monitoring ```al // Measuring DeleteAll performance characteristics codeunit 50204 "Delete Performance Monitor" { procedure BenchmarkDeletionMethods(RecordCount: Integer) var TempInteger: Record "Integer" temporary; StartTime: Time; DeleteAllTime: Duration; IterativeTime: Duration; i: Integer; begin // Setup test data for i := 1 to RecordCount do begin TempInteger.Number := i; TempInteger.Insert(); end; // Benchmark DeleteAll approach StartTime := Time; TempInteger.DeleteAll(); DeleteAllTime := Time - StartTime; // Recreate test data for iterative test for i := 1 to RecordCount do begin TempInteger.Number := i; TempInteger.Insert(); end; // Benchmark iterative deletion StartTime := Time; if TempInteger.FindSet() then repeat TempInteger.Delete(); until TempInteger.Next() = 0; IterativeTime := Time - StartTime; // Log performance comparison Message('DeleteAll: %1ms, Iterative: %2ms, Ratio: %3:1', DeleteAllTime, IterativeTime, Round(IterativeTime / DeleteAllTime, 0.1)); end; } ``` ## Anti-Pattern: Mixed Deletion Approaches ```al // ANTI-PATTERN: Inconsistent deletion methods in same operation codeunit 50299 "Deletion Anti-Patterns" // DON'T DO THIS { procedure InconsistentCleanup(OrderNo: Code[20]) // BAD EXAMPLE var SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; SalesCommentLine: Record "Sales Comment Line"; begin // PROBLEM: Mixing deletion methods without understanding implications // Uses DeleteAll (bypasses events) SalesLine.SetRange("Document No.", OrderNo); SalesLine.DeleteAll(); // OnDelete events NOT called // Uses iterative deletion (processes events) SalesCommentLine.SetRange("Document Type", SalesCommentLine."Document Type"::Order); SalesCommentLine.SetRange("No.", OrderNo); if SalesCommentLine.FindSet() then repeat SalesCommentLine.Delete(); // OnDelete events ARE called until SalesCommentLine.Next() = 0; // Manual deletion (bypasses AL Delete method entirely) SalesHeader.Get(SalesHeader."Document Type"::Order, OrderNo); SalesHeader.Delete(); // OnDelete events ARE called // PROBLEMS: // 1. Inconsistent event processing across related tables // 2. Potential data integrity issues from partial event execution // 3. Unpredictable behavior for consuming code end; procedure UnoptimizedBulkDeletion() // BAD EXAMPLE var Item: Record Item; begin // ANTI-PATTERN: Using loop when DeleteAll would be more efficient Item.SetRange(Blocked, true); Item.SetFilter("Last Date Modified", '<%1', CalcDate('-1Y', Today)); // INEFFICIENT: Creates individual SQL DELETE statements if Item.FindSet() then repeat Item.Delete(); // Each iteration = separate SQL statement until Item.Next() = 0; // BETTER APPROACH: Single SQL operation // Item.DeleteAll(); // One SQL DELETE statement end; } /* CORRECT DELETEALL USAGE PRINCIPLES: 1. Consistency: Use same deletion method for related operations 2. Event Awareness: Understand which events are bypassed 3. Performance Priority: Choose DeleteAll for bulk operations 4. Documentation: Clearly document event bypass behavior 5. Validation: Ensure database constraints handle integrity 6. Testing: Verify behavior matches expectations in all scenarios DECISION MATRIX: - Large datasets + No critical AL events = DeleteAll - Small datasets + Complex AL logic = Iterative deletion - Mixed requirements = Separate phases or manual event processing */ ```