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

475 lines (423 loc) 14.7 kB
# SIFT Technology Fundamentals - AL Code Samples ## Basic SIFT Table Configuration ### Standard Implementation ```al table 50100 "Sales Analytics Data" { Caption = 'Sales Analytics Data'; DataClassification = CustomerContent; fields { field(1; "Customer No."; Code[20]) { Caption = 'Customer No.'; TableRelation = Customer; } field(2; "Item No."; Code[20]) { Caption = 'Item No.'; TableRelation = Item; } field(3; "Posting Date"; Date) { Caption = 'Posting Date'; } field(10; "Sales Amount"; Decimal) { Caption = 'Sales Amount'; DecimalPlaces = 2 : 2; } field(11; "Quantity"; Decimal) { Caption = 'Quantity'; DecimalPlaces = 0 : 5; } field(12; "Profit Amount"; Decimal) { Caption = 'Profit Amount'; DecimalPlaces = 2 : 2; } } keys { key(PK; "Customer No.", "Item No.", "Posting Date") { Clustered = true; } // Basic SIFT key for customer totals key(CustomerSIFT; "Customer No.") { SumIndexFields = "Sales Amount", Quantity, "Profit Amount"; MaintainSIFTIndex = true; } // Item-based aggregations key(ItemSIFT; "Item No.") { SumIndexFields = "Sales Amount", Quantity; MaintainSIFTIndex = true; } } } ``` ## SumIndexFields Property Usage Patterns ### Multiple Aggregation Strategies ```al table 50101 "Order Performance Metrics" { Caption = 'Order Performance Metrics'; DataClassification = CustomerContent; fields { field(1; "Salesperson Code"; Code[20]) { } field(2; "Customer No."; Code[20]) { } field(3; "Order Date"; Date) { } field(10; "Order Amount"; Decimal) { } field(11; "Discount Amount"; Decimal) { } field(12; "Commission Amount"; Decimal) { } field(13; "Processing Time"; Integer) { } } keys { key(PK; "Salesperson Code", "Customer No.", "Order Date") { Clustered = true; } // Comprehensive salesperson performance SIFT key(SalespersonPerformance; "Salesperson Code") { SumIndexFields = "Order Amount", "Discount Amount", "Commission Amount"; MaintainSIFTIndex = true; } // Customer relationship metrics SIFT key(CustomerMetrics; "Customer No.") { SumIndexFields = "Order Amount", "Discount Amount"; MaintainSIFTIndex = true; } // Time-based analysis SIFT key(DateAnalysis; "Order Date") { SumIndexFields = "Order Amount", "Processing Time"; MaintainSIFTIndex = false; // On-demand for reporting } } } // Usage in AL code codeunit 50100 "SIFT Usage Examples" { procedure GetSalespersonTotals(SalespersonCode: Code[20]; var TotalAmount: Decimal; var TotalCommission: Decimal) var OrderMetrics: Record "Order Performance Metrics"; begin OrderMetrics.Reset(); OrderMetrics.SetRange("Salesperson Code", SalespersonCode); // Leverages SIFT for instant calculation OrderMetrics.CalcSums("Order Amount", "Commission Amount"); TotalAmount := OrderMetrics."Order Amount"; TotalCommission := OrderMetrics."Commission Amount"; end; } ``` ## FlowField Integration with SIFT ### Customer Card with SIFT-Optimized FlowFields ```al tableextension 50100 "Customer SIFT Extension" extends Customer { fields { // FlowField automatically leverages SIFT when available field(50100; "Total Sales Amount"; Decimal) { Caption = 'Total Sales Amount'; FieldClass = FlowField; CalcFormula = sum("Sales Analytics Data"."Sales Amount" where("Customer No." = field("No."))); Editable = false; } field(50101; "Total Quantity Sold"; Decimal) { Caption = 'Total Quantity Sold'; FieldClass = FlowField; CalcFormula = sum("Sales Analytics Data".Quantity where("Customer No." = field("No."))); Editable = false; } field(50102; "Average Order Value"; Decimal) { Caption = 'Average Order Value'; FieldClass = FlowField; CalcFormula = average("Sales Analytics Data"."Sales Amount" where("Customer No." = field("No."))); Editable = false; } } } // Page implementation with automatic FlowField calculation pageextension 50100 "Customer Card SIFT" extends "Customer Card" { layout { addafter("Name") { group("Sales Analytics") { Caption = 'Sales Analytics (SIFT-Optimized)'; field("Total Sales Amount"; Rec."Total Sales Amount") { ApplicationArea = All; // Automatically calculated using SIFT when page opens } field("Total Quantity Sold"; Rec."Total Quantity Sold") { ApplicationArea = All; } field("Average Order Value"; Rec."Average Order Value") { ApplicationArea = All; } } } } trigger OnAfterGetRecord() begin // Force calculation of FlowFields (leverages SIFT) Rec.CalcFields("Total Sales Amount", "Total Quantity Sold", "Average Order Value"); end; } ``` ## Performance Comparison: SIFT vs Non-SIFT ### Performance Testing Framework ```al codeunit 50101 "SIFT Performance Comparison" { procedure ComparePerformance(CustomerNo: Code[20]) var SalesData: Record "Sales Analytics Data"; StartTime: DateTime; EndTime: DateTime; SIFTDuration: Duration; ManualDuration: Duration; TotalAmount: Decimal; begin // Test 1: SIFT-optimized calculation StartTime := CurrentDateTime; TotalAmount := CalculateUsingSIFT(CustomerNo); EndTime := CurrentDateTime; SIFTDuration := EndTime - StartTime; // Test 2: Manual calculation without SIFT StartTime := CurrentDateTime; TotalAmount := CalculateManually(CustomerNo); EndTime := CurrentDateTime; ManualDuration := EndTime - StartTime; // Performance comparison results Message('SIFT Calculation: %1ms\Manual Calculation: %2ms\Performance Improvement: %3x faster', SIFTDuration, ManualDuration, ManualDuration / SIFTDuration); end; local procedure CalculateUsingSIFT(CustomerNo: Code[20]): Decimal var SalesData: Record "Sales Analytics Data"; begin // Leverages SIFT index for instant aggregation SalesData.Reset(); SalesData.SetRange("Customer No.", CustomerNo); SalesData.CalcSums("Sales Amount"); exit(SalesData."Sales Amount"); end; local procedure CalculateManually(CustomerNo: Code[20]): Decimal var SalesData: Record "Sales Analytics Data"; TotalAmount: Decimal; begin // Forces row-by-row calculation without SIFT SalesData.Reset(); SalesData.SetRange("Customer No.", CustomerNo); if SalesData.FindSet() then repeat TotalAmount += SalesData."Sales Amount"; until SalesData.Next() = 0; exit(TotalAmount); end; } // Benchmark results table for tracking table 50102 "SIFT Performance Benchmark" { fields { field(1; "Test Date"; DateTime) { } field(2; "Customer No."; Code[20]) { } field(3; "Record Count"; Integer) { } field(4; "SIFT Duration (ms)"; Integer) { } field(5; "Manual Duration (ms)"; Integer) { } field(6; "Performance Ratio"; Decimal) { } } keys { key(PK; "Test Date", "Customer No.") { } } } ``` ## Common SIFT Configuration Mistakes (Anti-Patterns) ### Anti-Pattern Examples with Corrections #### ❌ Anti-Pattern 1: Excessive SIFT Keys ```al // BAD: Too many SIFT keys with MaintainSIFTIndex = true table 50103 "Over-SIFT Example (Bad)" { keys { key(PK; ID) { } key(SIFT1; Field1) { SumIndexFields = Amount; MaintainSIFTIndex = true; } key(SIFT2; Field2) { SumIndexFields = Amount; MaintainSIFTIndex = true; } key(SIFT3; Field3) { SumIndexFields = Amount; MaintainSIFTIndex = true; } key(SIFT4; Field4) { SumIndexFields = Amount; MaintainSIFTIndex = true; } key(SIFT5; Field5) { SumIndexFields = Amount; MaintainSIFTIndex = true; } // Problem: Every data modification updates 5 SIFT indexes // Impact: Severe write performance degradation } } ``` #### ✅ Corrected Pattern 1: Selective SIFT Usage ```al // GOOD: Strategic SIFT key selection table 50104 "Optimized SIFT Example (Good)" { keys { key(PK; ID) { } // Only critical aggregations maintain real-time SIFT key(CriticalSIFT; "Customer No.") { SumIndexFields = Amount; MaintainSIFTIndex = true; // Dashboard requirement } // Secondary aggregations use on-demand calculation key(ReportingSIFT; "Item No.") { SumIndexFields = Amount; MaintainSIFTIndex = false; // Monthly reporting only } } } ``` #### ❌ Anti-Pattern 2: SIFT Without SumIndexFields ```al // BAD: SIFT configuration without aggregation fields table 50105 "Empty SIFT Example (Bad)" { keys { key(PK; ID) { } key(BadSIFT; "Customer No.") { MaintainSIFTIndex = true; // No SumIndexFields specified! // Problem: Wastes storage and processing with no benefit } } } ``` #### ✅ Corrected Pattern 2: Proper SIFT Configuration ```al // GOOD: SIFT with appropriate aggregation fields table 50106 "Proper SIFT Example (Good)" { keys { key(PK; ID) { } key(GoodSIFT; "Customer No.") { SumIndexFields = "Sales Amount", Quantity, "Profit Amount"; MaintainSIFTIndex = true; } } } ``` #### ❌ Anti-Pattern 3: Wrong MaintainSIFTIndex Setting ```al // BAD: Real-time SIFT for batch processing scenario codeunit 50102 "Batch Import with Bad SIFT" { procedure ImportLargeDataset() var DataRecord: Record "Over-SIFT Example (Bad)"; i: Integer; begin // Importing 100,000 records for i := 1 to 100000 do begin DataRecord.Init(); DataRecord.ID := i; DataRecord.Amount := Random(1000); DataRecord.Insert(); // Each insert updates multiple SIFT indexes! // Performance impact: 10x slower than necessary end; end; } ``` #### ✅ Corrected Pattern 3: Appropriate SIFT for Usage Pattern ```al // GOOD: On-demand SIFT for batch scenarios table 50107 "Batch-Optimized SIFT (Good)" { keys { key(PK; ID) { } key(BatchSIFT; "Data Type") { SumIndexFields = Amount; MaintainSIFTIndex = false; // Calculated after batch import } } } codeunit 50103 "Batch Import with Good SIFT" { procedure ImportLargeDataset() var DataRecord: Record "Batch-Optimized SIFT (Good)"; i: Integer; begin // Fast import without real-time SIFT maintenance for i := 1 to 100000 do begin DataRecord.Init(); DataRecord.ID := i; DataRecord.Amount := Random(1000); DataRecord.Insert(); // No SIFT overhead during import end; // Calculate aggregations on-demand after import DataRecord.CalcSums(Amount); // Uses SIFT for efficient calculation Message('Total imported amount: %1', DataRecord.Amount); end; } ``` ## Best Practice Decision Matrix ### SIFT Configuration Guidelines ```al // Decision framework for SIFT configuration codeunit 50104 "SIFT Decision Framework" { // Use this pattern to determine optimal SIFT settings procedure RecommendSIFTSettings(ReadFrequency: Text; WriteFrequency: Text; DataVolume: Text): Text begin case true of (ReadFrequency = 'High') and (WriteFrequency = 'Low'): exit('MaintainSIFTIndex = true - Optimal for dashboards and real-time reporting'); (ReadFrequency = 'Low') and (WriteFrequency = 'High'): exit('MaintainSIFTIndex = false - Optimal for transaction processing systems'); (ReadFrequency = 'Medium') and (WriteFrequency = 'Medium'): exit('Test both settings and measure performance - Consider time-based switching'); DataVolume = 'Very Large': exit('MaintainSIFTIndex = false - Avoid real-time maintenance on large datasets'); else exit('Start with MaintainSIFTIndex = false and optimize based on actual usage patterns'); end; end; } /* SIFT Performance Guidelines: 1. Real-time SIFT (MaintainSIFTIndex = true): - Best for: Dashboards, frequently accessed reports, user interface calculations - Trade-off: Slower writes, faster reads - Limit: Maximum 3-5 real-time SIFT keys per table 2. On-demand SIFT (MaintainSIFTIndex = false): - Best for: Periodic reporting, batch processing, large data imports - Trade-off: Faster writes, acceptable read performance - Benefit: No maintenance overhead during data modifications 3. Monitoring and Optimization: - Measure actual performance in production-like environments - Monitor SIFT calculation times and data modification impact - Consider data growth patterns when choosing SIFT strategy - Review SIFT usage patterns quarterly and adjust as needed */ ```