diff --git a/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/README.md b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/README.md new file mode 100644 index 0000000000..453cf8be93 --- /dev/null +++ b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/README.md @@ -0,0 +1,44 @@ +# GlideRecord Performance Optimization Techniques + +This collection provides advanced techniques for optimizing GlideRecord queries and database operations in ServiceNow. + +## Overview + +Performance optimization is crucial for maintaining responsive ServiceNow applications, especially when dealing with large datasets or complex queries. These snippets demonstrate best practices for efficient GlideRecord usage. + +## Key Performance Principles + +- **Minimize Database Roundtrips**: Use efficient query patterns +- **Proper Indexing**: Leverage indexed fields in queries +- **Batch Operations**: Process multiple records efficiently +- **Query Optimization**: Use appropriate query methods +- **Memory Management**: Handle large datasets responsibly + +## Snippets Included + +1. **optimized_batch_processing.js** - Efficient batch processing techniques +2. **indexed_field_queries.js** - Leveraging database indexes +3. **chunked_data_processing.js** - Processing large datasets in chunks +4. **query_performance_comparison.js** - Performance comparison examples +5. **memory_efficient_operations.js** - Memory-conscious GlideRecord usage + +## Performance Monitoring + +Always measure performance using: +- `gs.log()` with timestamps for execution time +- Database query metrics in Developer Tools +- Performance Analytics for production monitoring + +## Best Practices Summary + +1. Always query on indexed fields when possible +2. Use `setLimit()` for large result sets +3. Avoid using `getRowCount()` on large tables +4. Use `chooseWindow()` for pagination +5. Consider using `GlideAggregate` for statistical queries +6. Implement proper error handling and logging + +## Related Documentation + +- [ServiceNow GlideRecord API Documentation](https://developer.servicenow.com/dev.do#!/reference/api/tokyo/server/no-namespace/c_GlideRecordScopedAPI) +- [Query Performance Best Practices](https://docs.servicenow.com/bundle/tokyo-platform-administration/page/administer/managing-data/concept/query-performance.html) diff --git a/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/indexed_field_queries.js b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/indexed_field_queries.js new file mode 100644 index 0000000000..d108f267ef --- /dev/null +++ b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/indexed_field_queries.js @@ -0,0 +1,278 @@ +/** + * Indexed Field Query Optimization + * + * This snippet demonstrates how to leverage database indexes for optimal query performance + * in ServiceNow GlideRecord operations. + * + * Use Case: High-performance queries on large tables + * Performance Benefits: Faster query execution, reduced database load + * + * @author ServiceNow Community + * @version 1.0 + */ + +// Method 1: Using Indexed Fields for Optimal Performance +function queryWithIndexedFields() { + var gr = new GlideRecord('incident'); + + // GOOD: Query on indexed fields (state, priority, assignment_group are typically indexed) + gr.addQuery('state', 'IN', '2,3'); // Work in Progress, On Hold + gr.addQuery('priority', '<=', '3'); // High priority and above + gr.addQuery('assignment_group', '!=', ''); // Has assignment group + + // GOOD: Use sys_created_on for date ranges (indexed timestamp) + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(7)); + gr.addQuery('sys_created_on', '<=', gs.daysAgoEnd(1)); + + // GOOD: Order by indexed field + gr.orderBy('sys_created_on'); + + gr.query(); + + var results = []; + while (gr.next()) { + results.push({ + number: gr.getDisplayValue('number'), + state: gr.getDisplayValue('state'), + priority: gr.getDisplayValue('priority') + }); + } + + return results; +} + +// Method 2: Avoiding Non-Indexed Field Queries +function avoidNonIndexedQueries() { + // BAD EXAMPLE - Don't do this on large tables + function badQueryExample() { + var gr = new GlideRecord('incident'); + + // BAD: Contains query on non-indexed text field + gr.addQuery('description', 'CONTAINS', 'network issue'); + + // BAD: Complex wildcard search + gr.addQuery('short_description', 'STARTSWITH', 'Unable'); + + gr.query(); + return gr.getRowCount(); // Also bad on large tables + } + + // GOOD ALTERNATIVE - Use indexed fields and full-text search + function goodQueryAlternative() { + var gr = new GlideRecord('incident'); + + // GOOD: Use category/subcategory (often indexed) + gr.addQuery('category', 'network'); + + // GOOD: Use indexed fields first to narrow results + gr.addQuery('state', 'IN', '1,2,3'); + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30)); + + // Then filter on text if needed (smaller result set) + gr.addQuery('short_description', 'CONTAINS', 'unable'); + + gr.query(); + + var results = []; + while (gr.next()) { + results.push(gr.getUniqueValue()); + } + + return results; + } + + return goodQueryAlternative(); +} + +// Method 3: Compound Index Optimization +function optimizeCompoundIndexes() { + var gr = new GlideRecord('task'); + + // GOOD: Query fields in order that matches compound indexes + // Many tables have compound indexes on (table, state, assigned_to) + gr.addQuery('state', '!=', '7'); // Not closed + gr.addQuery('assigned_to', '!=', ''); // Has assignee + + // Add additional filters after indexed ones + gr.addQuery('priority', '<=', '3'); + + // Order by indexed field for better performance + gr.orderBy('sys_updated_on'); + + gr.query(); + + return gr.getRowCount(); +} + +// Method 4: Reference Field Optimization +function optimizeReferenceQueries() { + // GOOD: Query reference fields by sys_id (indexed) + function queryByReferenceId() { + var groupSysId = getAssignmentGroupSysId('Hardware'); + + var gr = new GlideRecord('incident'); + gr.addQuery('assignment_group', groupSysId); // Uses index + gr.addQuery('state', '!=', '7'); // Not closed + gr.query(); + + return gr.getRowCount(); + } + + // LESS OPTIMAL: Query by reference field display value + function queryByDisplayValue() { + var gr = new GlideRecord('incident'); + gr.addQuery('assignment_group.name', 'Hardware'); // Less efficient + gr.addQuery('state', '!=', '7'); + gr.query(); + + return gr.getRowCount(); + } + + // BEST: Combine both approaches + function optimizedReferenceQuery() { + // First get the sys_id using indexed query + var groupGR = new GlideRecord('sys_user_group'); + groupGR.addQuery('name', 'Hardware'); + groupGR.query(); + + if (groupGR.next()) { + var groupSysId = groupGR.getUniqueValue(); + + // Then use sys_id in main query (indexed) + var gr = new GlideRecord('incident'); + gr.addQuery('assignment_group', groupSysId); + gr.addQuery('state', '!=', '7'); + gr.query(); + + return gr.getRowCount(); + } + + return 0; + } + + return optimizedReferenceQuery(); +} + +// Method 5: Date Range Optimization +function optimizeDateRangeQueries() { + // GOOD: Use built-in date functions with indexed timestamps + function efficientDateQuery() { + var gr = new GlideRecord('incident'); + + // Use sys_created_on (indexed) with built-in functions + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30)); + gr.addQuery('sys_created_on', '<=', gs.daysAgoEnd(1)); + + // Add other indexed filters + gr.addQuery('state', '!=', '7'); + + gr.query(); + return gr.getRowCount(); + } + + // LESS OPTIMAL: Complex date calculations + function lessOptimalDateQuery() { + var gr = new GlideRecord('incident'); + + // Complex date calculation (harder to optimize) + var thirtyDaysAgo = new GlideDateTime(); + thirtyDaysAgo.addDaysUTC(-30); + + gr.addQuery('sys_created_on', '>=', thirtyDaysAgo); + gr.query(); + + return gr.getRowCount(); + } + + return efficientDateQuery(); +} + +// Method 6: Query Performance Analysis +function analyzeQueryPerformance() { + var queries = [ + { + name: 'Indexed Query', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '2'); + gr.addQuery('priority', '1'); + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'Non-Indexed Query', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('description', 'CONTAINS', 'test'); + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + } + ]; + + queries.forEach(function(queryTest) { + var startTime = new Date().getTime(); + var result = queryTest.query(); + var endTime = new Date().getTime(); + var executionTime = endTime - startTime; + + gs.log(queryTest.name + ':'); + gs.log(' Execution time: ' + executionTime + 'ms'); + gs.log(' Result count: ' + result); + gs.log(' Performance rating: ' + (executionTime < 100 ? 'Good' : executionTime < 500 ? 'Fair' : 'Poor')); + }); +} + +// Helper function +function getAssignmentGroupSysId(groupName) { + var gr = new GlideRecord('sys_user_group'); + gr.addQuery('name', groupName); + gr.query(); + + if (gr.next()) { + return gr.getUniqueValue(); + } + + return ''; +} + +// Method 7: Index-Aware Pagination +function indexAwarePagination(pageSize, pageNumber) { + pageSize = pageSize || 50; + pageNumber = pageNumber || 0; + + var gr = new GlideRecord('incident'); + + // Use indexed fields for filtering + gr.addQuery('state', 'IN', '1,2,3'); + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(90)); + + // Order by indexed field for consistent pagination + gr.orderBy('sys_created_on'); + gr.orderByDesc('sys_id'); // Secondary sort for tie-breaking + + // Use chooseWindow for efficient pagination + gr.chooseWindow(pageNumber * pageSize, (pageNumber + 1) * pageSize); + + gr.query(); + + var results = []; + while (gr.next()) { + results.push({ + sys_id: gr.getUniqueValue(), + number: gr.getDisplayValue('number'), + short_description: gr.getDisplayValue('short_description'), + state: gr.getDisplayValue('state') + }); + } + + return { + data: results, + page: pageNumber, + pageSize: pageSize, + hasMore: results.length === pageSize + }; +} diff --git a/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/optimized_batch_processing.js b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/optimized_batch_processing.js new file mode 100644 index 0000000000..76b9f8fc7e --- /dev/null +++ b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/optimized_batch_processing.js @@ -0,0 +1,230 @@ +/** + * Optimized Batch Processing with GlideRecord + * + * This snippet demonstrates efficient techniques for processing large numbers of records + * while maintaining good performance and avoiding timeout issues. + * + * Use Case: Bulk updates, data migration, or mass record processing + * Performance Benefits: Reduced memory usage, better transaction management, timeout prevention + * + * @author ServiceNow Community + * @version 1.0 + */ + +// Method 1: Chunked Processing with Limit +function processRecordsInChunks() { + var tableName = 'incident'; + var chunkSize = 500; // Adjust based on your needs and system performance + var processedCount = 0; + var totalProcessed = 0; + + // Log start time for performance monitoring + var startTime = new Date().getTime(); + gs.log('Starting batch processing at: ' + new Date()); + + do { + var gr = new GlideRecord(tableName); + + // Use indexed fields for better performance + gr.addQuery('state', 'IN', '1,2,3'); // Open states + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30)); // Last 30 days + + // Set limit for this chunk + gr.setLimit(chunkSize); + gr.orderBy('sys_created_on'); // Consistent ordering for pagination + + // Skip already processed records + gr.chooseWindow(totalProcessed, totalProcessed + chunkSize); + + gr.query(); + + processedCount = 0; + + while (gr.next()) { + try { + // Your processing logic here + updateIncidentPriority(gr); + processedCount++; + totalProcessed++; + + // Log progress every 100 records + if (processedCount % 100 === 0) { + gs.log('Processed ' + totalProcessed + ' records so far...'); + } + + } catch (error) { + gs.error('Error processing record ' + gr.getUniqueValue() + ': ' + error.message); + // Continue processing other records + } + } + + // Yield execution to prevent timeout (if in scheduled job) + gs.sleep(100); // Brief pause between chunks + + } while (processedCount === chunkSize); // Continue if we got a full chunk + + // Log completion statistics + var endTime = new Date().getTime(); + var executionTime = (endTime - startTime) / 1000; + + gs.log('Batch processing completed:'); + gs.log('- Total records processed: ' + totalProcessed); + gs.log('- Execution time: ' + executionTime + ' seconds'); + gs.log('- Average records per second: ' + (totalProcessed / executionTime).toFixed(2)); +} + +// Method 2: Optimized Bulk Update with Batch Commits +function optimizedBulkUpdate() { + var gr = new GlideRecord('task'); + + // Use compound query with indexed fields + gr.addQuery('state', 'IN', '1,2'); + gr.addQuery('priority', '4'); + gr.addQuery('sys_updated_on', '<', gs.daysAgoStart(7)); + + // Set reasonable limit to prevent memory issues + gr.setLimit(1000); + gr.query(); + + var updateCount = 0; + var batchSize = 50; + + // Start transaction for batch processing + var transaction = new GlideTransaction(); + + try { + while (gr.next()) { + // Update the record + gr.priority = '3'; // Increase priority + gr.comments = 'Priority auto-updated due to age'; + gr.update(); + + updateCount++; + + // Commit in batches to manage transaction size + if (updateCount % batchSize === 0) { + transaction.commit(); + transaction = new GlideTransaction(); // Start new transaction + gs.log('Committed batch of ' + batchSize + ' updates. Total: ' + updateCount); + } + } + + // Commit remaining records + if (updateCount % batchSize !== 0) { + transaction.commit(); + } + + gs.log('Bulk update completed. Total records updated: ' + updateCount); + + } catch (error) { + transaction.rollback(); + gs.error('Bulk update failed and rolled back: ' + error.message); + throw error; + } +} + +// Method 3: Memory-Efficient Large Dataset Processing +function processLargeDatasetEfficiently() { + var tableName = 'cmdb_ci'; + var processedTotal = 0; + var hasMoreRecords = true; + var lastSysId = ''; + + while (hasMoreRecords) { + var gr = new GlideRecord(tableName); + + // Use sys_id for cursor-based pagination (most efficient) + if (lastSysId) { + gr.addQuery('sys_id', '>', lastSysId); + } + + // Add your business logic filters + gr.addQuery('install_status', 'IN', '1,6'); // Installed or Reserved + + gr.orderBy('sys_id'); // Consistent ordering + gr.setLimit(200); // Smaller chunks for large tables + gr.query(); + + var currentBatchCount = 0; + + while (gr.next()) { + try { + // Your processing logic + processConfigurationItem(gr); + + currentBatchCount++; + processedTotal++; + lastSysId = gr.getUniqueValue(); + + } catch (error) { + gs.error('Error processing CI ' + gr.getUniqueValue() + ': ' + error.message); + } + } + + // Check if we have more records to process + hasMoreRecords = (currentBatchCount === 200); + + gs.log('Processed batch: ' + currentBatchCount + ' records. Total: ' + processedTotal); + + // Small delay to prevent overwhelming the system + gs.sleep(50); + } + + gs.log('Large dataset processing completed. Total records: ' + processedTotal); +} + +// Helper function example +function updateIncidentPriority(incidentGR) { + // Example business logic + if (incidentGR.getValue('business_impact') == '1' && incidentGR.getValue('urgency') == '1') { + incidentGR.priority = '1'; // Critical + incidentGR.update(); + } +} + +function processConfigurationItem(ciGR) { + // Example CI processing logic + ciGR.last_discovered = new GlideDateTime(); + ciGR.update(); +} + +// Method 4: Performance Monitoring Wrapper +function monitoredBatchOperation(operationName, operationFunction) { + var startTime = new Date().getTime(); + var memoryBefore = gs.getProperty('glide.script.heap.size', 'Unknown'); + + gs.log('Starting operation: ' + operationName); + gs.log('Memory before: ' + memoryBefore); + + try { + var result = operationFunction(); + + var endTime = new Date().getTime(); + var executionTime = (endTime - startTime) / 1000; + var memoryAfter = gs.getProperty('glide.script.heap.size', 'Unknown'); + + gs.log('Operation completed: ' + operationName); + gs.log('Execution time: ' + executionTime + ' seconds'); + gs.log('Memory after: ' + memoryAfter); + + return result; + + } catch (error) { + var endTime = new Date().getTime(); + var executionTime = (endTime - startTime) / 1000; + + gs.error('Operation failed: ' + operationName); + gs.error('Execution time before failure: ' + executionTime + ' seconds'); + gs.error('Error details: ' + error.message); + + throw error; + } +} + +// Example usage of performance monitoring +function exampleMonitoredOperation() { + monitoredBatchOperation('Incident Priority Update', function() { + processRecordsInChunks(); + return 'Success'; + }); +} diff --git a/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/query_performance_comparison.js b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/query_performance_comparison.js new file mode 100644 index 0000000000..dc12866238 --- /dev/null +++ b/Core ServiceNow APIs/GlideRecord/Performance Optimization Techniques/query_performance_comparison.js @@ -0,0 +1,424 @@ +/** + * Query Performance Comparison and Analysis + * + * This snippet provides tools to compare different query approaches and measure their performance + * to help developers choose the most efficient methods. + * + * Use Case: Performance testing, query optimization, development best practices + * Performance Benefits: Data-driven optimization decisions, performance monitoring + * + * @author ServiceNow Community + * @version 1.0 + */ + +// Performance Testing Framework +var QueryPerformanceTester = Class.create(); +QueryPerformanceTester.prototype = { + + initialize: function(tableName) { + this.tableName = tableName || 'incident'; + this.results = []; + }, + + // Method to test different query approaches + testQuery: function(testName, queryFunction, iterations) { + iterations = iterations || 1; + var times = []; + var results = []; + + gs.log('Starting performance test: ' + testName); + + for (var i = 0; i < iterations; i++) { + var startTime = new Date().getTime(); + + try { + var result = queryFunction(); + var endTime = new Date().getTime(); + var executionTime = endTime - startTime; + + times.push(executionTime); + results.push(result); + + } catch (error) { + gs.error('Error in test "' + testName + '", iteration ' + (i + 1) + ': ' + error.message); + return null; + } + } + + var avgTime = times.reduce(function(a, b) { return a + b; }) / times.length; + var minTime = Math.min.apply(null, times); + var maxTime = Math.max.apply(null, times); + + var testResult = { + name: testName, + iterations: iterations, + averageTime: avgTime, + minimumTime: minTime, + maximumTime: maxTime, + resultCount: results[0] || 0, + allTimes: times + }; + + this.results.push(testResult); + + gs.log('Test completed: ' + testName); + gs.log('Average time: ' + avgTime + 'ms'); + gs.log('Result count: ' + (results[0] || 0)); + + return testResult; + }, + + // Compare multiple query approaches + compareQueries: function(queryTests, iterations) { + iterations = iterations || 3; + + gs.log('Starting query performance comparison with ' + iterations + ' iterations each'); + + var self = this; + queryTests.forEach(function(test) { + self.testQuery(test.name, test.query, iterations); + }); + + this.printComparison(); + return this.results; + }, + + // Print comparison results + printComparison: function() { + if (this.results.length === 0) { + gs.log('No test results to compare'); + return; + } + + gs.log('\n=== Query Performance Comparison Results ==='); + + // Sort by average time + var sortedResults = this.results.slice().sort(function(a, b) { + return a.averageTime - b.averageTime; + }); + + sortedResults.forEach(function(result, index) { + var rank = index + 1; + var performance = rank === 1 ? 'BEST' : rank === sortedResults.length ? 'WORST' : 'GOOD'; + + gs.log('\nRank #' + rank + ' (' + performance + '): ' + result.name); + gs.log(' Average Time: ' + result.averageTime.toFixed(2) + 'ms'); + gs.log(' Min/Max Time: ' + result.minimumTime + 'ms / ' + result.maximumTime + 'ms'); + gs.log(' Result Count: ' + result.resultCount); + + if (index > 0) { + var percentSlower = ((result.averageTime - sortedResults[0].averageTime) / sortedResults[0].averageTime * 100); + gs.log(' Performance: ' + percentSlower.toFixed(1) + '% slower than best'); + } + }); + + gs.log('\n=== Recommendations ==='); + gs.log('Use "' + sortedResults[0].name + '" for best performance'); + if (sortedResults.length > 1) { + gs.log('Avoid "' + sortedResults[sortedResults.length - 1].name + '" if possible'); + } + } +}; + +// Example 1: Comparing Different Query Approaches +function compareBasicQueryMethods() { + var tester = new QueryPerformanceTester('incident'); + + var queryTests = [ + { + name: 'Indexed Field Query (Optimal)', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '2'); // Work in Progress (indexed) + gr.addQuery('priority', '1'); // Critical (indexed) + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'Multiple OR Conditions', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', 'IN', '1,2,3'); // Multiple states + gr.addQuery('priority', 'IN', '1,2'); // High priorities + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'Text Search (Non-Indexed)', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('short_description', 'CONTAINS', 'network'); // Text search + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'Reference Field Display Value', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('caller_id.name', 'CONTAINS', 'John'); // Reference display value + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + } + ]; + + return tester.compareQueries(queryTests, 3); +} + +// Example 2: Date Query Performance Comparison +function compareDateQueryMethods() { + var tester = new QueryPerformanceTester('incident'); + + var queryTests = [ + { + name: 'Built-in Date Functions (Optimal)', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30)); + gr.addQuery('sys_created_on', '<=', gs.daysAgoEnd(1)); + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'GlideDateTime Calculations', + query: function() { + var gr = new GlideRecord('incident'); + var thirtyDaysAgo = new GlideDateTime(); + thirtyDaysAgo.addDaysUTC(-30); + var oneDayAgo = new GlideDateTime(); + oneDayAgo.addDaysUTC(-1); + + gr.addQuery('sys_created_on', '>=', thirtyDaysAgo); + gr.addQuery('sys_created_on', '<=', oneDayAgo); + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'String Date Comparison', + query: function() { + var gr = new GlideRecord('incident'); + var today = new Date(); + var thirtyDaysAgo = new Date(today.getTime() - (30 * 24 * 60 * 60 * 1000)); + + gr.addQuery('sys_created_on', '>=', thirtyDaysAgo.toISOString()); + gr.setLimit(100); + gr.query(); + return gr.getRowCount(); + } + } + ]; + + return tester.compareQueries(queryTests, 5); +} + +// Example 3: Pagination Method Comparison +function comparePaginationMethods() { + var tester = new QueryPerformanceTester('incident'); + var pageSize = 50; + var pageNumber = 2; + + var queryTests = [ + { + name: 'chooseWindow() Method (Optimal)', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '!=', '7'); + gr.orderBy('sys_created_on'); + gr.chooseWindow(pageNumber * pageSize, (pageNumber + 1) * pageSize); + gr.query(); + + var count = 0; + while (gr.next()) { count++; } + return count; + } + }, + { + name: 'setLimit() with Offset Simulation', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '!=', '7'); + gr.orderBy('sys_created_on'); + gr.setLimit(pageSize * (pageNumber + 1)); + gr.query(); + + var count = 0; + var skip = pageSize * pageNumber; + while (gr.next()) { + if (skip > 0) { + skip--; + continue; + } + count++; + } + return count; + } + } + ]; + + return tester.compareQueries(queryTests, 3); +} + +// Example 4: Aggregate vs Manual Counting +function compareCountingMethods() { + var tester = new QueryPerformanceTester('incident'); + + var queryTests = [ + { + name: 'GlideAggregate COUNT (Optimal)', + query: function() { + var ga = new GlideAggregate('incident'); + ga.addQuery('state', '!=', '7'); + ga.addAggregate('COUNT'); + ga.query(); + + if (ga.next()) { + return parseInt(ga.getAggregate('COUNT')); + } + return 0; + } + }, + { + name: 'GlideRecord getRowCount()', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '!=', '7'); + gr.query(); + return gr.getRowCount(); + } + }, + { + name: 'Manual Counting with Loop', + query: function() { + var gr = new GlideRecord('incident'); + gr.addQuery('state', '!=', '7'); + gr.query(); + + var count = 0; + while (gr.next()) { count++; } + return count; + } + } + ]; + + return tester.compareQueries(queryTests, 3); +} + +// Example 5: Complex Query Optimization +function compareComplexQueries() { + var tester = new QueryPerformanceTester('incident'); + + var queryTests = [ + { + name: 'Optimized Complex Query', + query: function() { + var gr = new GlideRecord('incident'); + + // Start with most selective indexed fields + gr.addQuery('state', 'IN', '1,2,3'); + gr.addQuery('priority', '<=', '3'); + gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30)); + + // Add less selective filters after + gr.addQuery('assignment_group', '!=', ''); + + gr.orderBy('sys_created_on'); + gr.setLimit(100); + gr.query(); + + return gr.getRowCount(); + } + }, + { + name: 'Non-Optimized Complex Query', + query: function() { + var gr = new GlideRecord('incident'); + + // Start with less selective fields + gr.addQuery('short_description', 'CONTAINS', 'issue'); + gr.addQuery('assignment_group', '!=', ''); + gr.addQuery('state', 'IN', '1,2,3'); + gr.addQuery('priority', '<=', '3'); + + gr.setLimit(100); + gr.query(); + + return gr.getRowCount(); + } + } + ]; + + return tester.compareQueries(queryTests, 3); +} + +// Comprehensive Performance Analysis +function runCompletePerformanceAnalysis() { + gs.log('=== Starting Comprehensive Query Performance Analysis ==='); + + var allResults = []; + + gs.log('\n1. Basic Query Methods Comparison:'); + allResults.push(compareBasicQueryMethods()); + + gs.log('\n2. Date Query Methods Comparison:'); + allResults.push(compareDateQueryMethods()); + + gs.log('\n3. Pagination Methods Comparison:'); + allResults.push(comparePaginationMethods()); + + gs.log('\n4. Counting Methods Comparison:'); + allResults.push(compareCountingMethods()); + + gs.log('\n5. Complex Query Optimization:'); + allResults.push(compareComplexQueries()); + + gs.log('\n=== Performance Analysis Complete ==='); + + return { + basicQueries: allResults[0], + dateQueries: allResults[1], + pagination: allResults[2], + counting: allResults[3], + complexQueries: allResults[4] + }; +} + +// Quick Performance Check for Development +function quickPerformanceCheck(tableName, testQuery) { + var startTime = new Date().getTime(); + + var gr = new GlideRecord(tableName); + testQuery(gr); // Apply query function + gr.query(); + + var count = 0; + while (gr.next()) { count++; } + + var endTime = new Date().getTime(); + var executionTime = endTime - startTime; + + var performance = { + executionTime: executionTime, + resultCount: count, + performance: executionTime < 100 ? 'Excellent' : + executionTime < 500 ? 'Good' : + executionTime < 1000 ? 'Fair' : 'Poor' + }; + + gs.log('Quick Performance Check Results:'); + gs.log('- Execution Time: ' + executionTime + 'ms'); + gs.log('- Result Count: ' + count); + gs.log('- Performance Rating: ' + performance.performance); + + return performance; +}