Chapter 9: Data Aggregation & Charting in SwiftUI

Understanding Data Aggregation

  1. In-Memory Aggregation (The Swift Way): Fetch all relevant Transaction entities into memory and use Swift's native collection operations (like Dictionary(grouping:by:) and reduce) to calculate the totals.
  2. Database-Level Aggregation (The Core Data Pro Way): Delegate the mathematical heavy lifting to SQLite before returning the results to Swift. We achieve this using NSExpressionDescription and setting the fetch request's result type to NSDictionaryResultType.
graph TD A[Raw Transactions in SQLite] --> B{Aggregation Strategy} B -->|"In-Memory"| C[Fetch all NSManagedObjects] C -->|"Memory Spike"| D[Swift Dictionary Grouping & Reduce] D -->|"CPU Overhead"| G[Chart Data Models] B -->|"Database-Level"| E[NSExpressionDescription + GROUP BY] E -->|"Optimized SQL Execution"| F[Fetch lightweight NSDictionary] F -->|"Low Memory Footprint"| G G --> H[Swift Charts UI]

The Architecture: MVVM and Core Data

sequenceDiagram participant View as ChartView (SwiftUI) participant VM as ChartViewModel (@Observable) participant Repo as ExpenseRepository participant Context as NSManagedObjectContext participant Store as NSPersistentStore (SQLite) View->>VM: onAppear / fetchChartData() VM->>VM: Spawn Task (Concurrency) VM->>Repo: fetchCategoryTotalsAsync() Repo->>Context: performBackgroundTask Context->>Store: SELECT category, SUM(amount) ... GROUP BY category Store-->>Context: Raw SQLite Rows Context-->>Repo: [[String: Any]] (Lightweight Dictionaries) Repo-->>VM: [CategoryTotal] (Domain Models) VM-->>View: @Observable chartData updated (Main Thread) View->>View: Swift Charts renders UI & Animations
  • View: Only knows about ChartViewModel and the simple CategoryTotal struct. It handles UI and Swift Charts configuration.
  • ViewModel: Manages the state, handles date filtering logic, and coordinates asynchronous data fetching.
  • Repository: The boundary layer. It translates domain requirements into Core Data NSFetchRequest objects and maps Core Data results back into pure Swift structs.

Approach 1: In-Memory Aggregation

Defining the Domain Model

import Foundation
import CoreData
import SwiftUI
import Observation

/// A pure Swift domain model representing aggregated spending for a category.
struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

// Dummy Color extension to allow compilation
extension Color {
    init(hex: String) {
        self.init(UIColor.gray)
    }
}

Implementing the Repository Method

import Foundation
import CoreData
import SwiftUI
import Observation

/// A pure Swift domain model representing aggregated spending for a category.
struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

// Dummy Color extension to allow compilation
extension Color {
    init(hex: String) {
        self.init(UIColor.gray)
    }
}

// Dummy entities to allow code to compile
@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
    @NSManaged var date: Date?
    @NSManaged var category: CategoryEntity?
}

@objc(CategoryEntity)
class CategoryEntity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var colorHex: String?
}

class ExpenseRepository {
    static let shared = ExpenseRepository()
    let persistentContainer: NSPersistentContainer
    
    // Mock expensesStream for ChartViewModel compilation
    var expensesStream: AsyncStream<[TransactionEntity]> {
        AsyncStream { continuation in
            continuation.finish()
        }
    }
    
    init() {
        persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    }

    /// Fetches transactions for a date range and aggregates totals by category in-memory.
    /// - Warning: Can cause memory spikes and faulting overhead for large datasets.
    func fetchCategoryTotalsInMemory(startDate: Date, endDate: Date) -> [CategoryTotal] {
        let context = persistentContainer.viewContext
        let request: NSFetchRequest = NSFetchRequest(entityName: "TransactionEntity")
        
        // 1. Filter by date to minimize fetched data
        request.predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate as NSDate, endDate as NSDate)
        
        // 2. Optimization: Prefetch relationships to avoid faulting loops later
        request.relationshipKeyPathsForPrefetching = ["category"]
        
        do {
            // 3. Fetch all matching entities into memory
            let transactions = try context.fetch(request)
            
            // 4. Group by Category Name
            // If category is nil, we default to "Uncategorized"
            let grouped = Dictionary(grouping: transactions) { transaction in
                transaction.category?.name ?? "Uncategorized"
            }
            
            // 5. Map and reduce to our domain model
            let totals: [CategoryTotal] = grouped.compactMap { (categoryName, txs) in
                // Reduce the array of transactions into a single sum
                let total = txs.reduce(0.0) { $0 + $1.amount }
                
                // Exclude categories with zero spending if desired
                guard total > 0 else { return nil }
                
                // Grab color from the first transaction's category, or default to gray
                let colorHex = txs.first?.category?.colorHex ?? "#808080" 
                
                return CategoryTotal(
                    categoryName: categoryName,
                    totalAmount: total,
                    categoryColorHex: colorHex
                )
            }
            
            // 6. Sort by amount descending for a better visual chart presentation
            return totals.sorted { $0.totalAmount > $1.totalAmount }
            
        } catch {
            print("Error fetching for in-memory aggregation: \(error)")
            return []
        }
    }
}

The Hidden Cost of In-Memory: Faulting and Memory Spikes

  • Easy to read and debug.
  • You have full access to the NSManagedObject instances if you need to calculate complex, non-mathematical logic based on multiple properties.
  • Memory Intensive: Instantiating thousands of objects just to read one or two properties is highly inefficient.
  • Slower: Converting database rows to full object graphs takes CPU time.

Approach 2: Database-Level Aggregation (The Pro Way)

Step-by-Step Implementation

import Foundation
import CoreData
import SwiftUI
import Observation

/// A pure Swift domain model representing aggregated spending for a category.
struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

// Dummy Color extension to allow compilation
extension Color {
    init(hex: String) {
        self.init(UIColor.gray)
    }
}

// Dummy entities to allow code to compile
@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
    @NSManaged var date: Date?
    @NSManaged var category: CategoryEntity?
}

@objc(CategoryEntity)
class CategoryEntity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var colorHex: String?
}

class ExpenseRepository {
    static let shared = ExpenseRepository()
    let persistentContainer: NSPersistentContainer
    
    // Mock expensesStream for ChartViewModel compilation
    var expensesStream: AsyncStream<[TransactionEntity]> {
        AsyncStream { continuation in
            continuation.finish()
        }
    }
    
    init() {
        persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    }

    /// Fetches transactions for a date range and aggregates totals by category in-memory.
    /// - Warning: Can cause memory spikes and faulting overhead for large datasets.
    func fetchCategoryTotalsInMemory(startDate: Date, endDate: Date) -> [CategoryTotal] {
        let context = persistentContainer.viewContext
        let request: NSFetchRequest = NSFetchRequest(entityName: "TransactionEntity")
        
        // 1. Filter by date to minimize fetched data
        request.predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate as NSDate, endDate as NSDate)
        
        // 2. Optimization: Prefetch relationships to avoid faulting loops later
        request.relationshipKeyPathsForPrefetching = ["category"]
        
        do {
            // 3. Fetch all matching entities into memory
            let transactions = try context.fetch(request)
            
            // 4. Group by Category Name
            // If category is nil, we default to "Uncategorized"
            let grouped = Dictionary(grouping: transactions) { transaction in
                transaction.category?.name ?? "Uncategorized"
            }
            
            // 5. Map and reduce to our domain model
            let totals: [CategoryTotal] = grouped.compactMap { (categoryName, txs) in
                // Reduce the array of transactions into a single sum
                let total = txs.reduce(0.0) { $0 + $1.amount }
                
                // Exclude categories with zero spending if desired
                guard total > 0 else { return nil }
                
                // Grab color from the first transaction's category, or default to gray
                let colorHex = txs.first?.category?.colorHex ?? "#808080" 
                
                return CategoryTotal(
                    categoryName: categoryName,
                    totalAmount: total,
                    categoryColorHex: colorHex
                )
            }
            
            // 6. Sort by amount descending for a better visual chart presentation
            return totals.sorted { $0.totalAmount > $1.totalAmount }
            
        } catch {
            print("Error fetching for in-memory aggregation: \(error)")
            return []
        }
    }

    /// Aggregates category totals efficiently at the database level using a background context.
    func fetchCategoryTotalsAsync(startDate: Date, endDate: Date) async -> [CategoryTotal] {
        return await withCheckedContinuation { continuation in
            // performBackgroundTask ensures this runs off the main thread
            persistentContainer.performBackgroundTask { context in
                
                // 1. Create the base fetch request targeting NSDictionary
                let request = NSFetchRequest(entityName: "TransactionEntity")
                request.resultType = .dictionaryResultType
                
                // 2. Set the predicate (Date Range)
                request.predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate as NSDate, endDate as NSDate)
                
                // 3. Define the SUM expression
                let amountKeyPath = #keyPath(TransactionEntity.amount)
                let amountExpression = NSExpression(forKeyPath: amountKeyPath)
                
                // Use the 'sum:' SQL function to add up the values
                let sumExpression = NSExpression(forFunction: "sum:", arguments: [amountExpression])
                
                // 4. Create the Expression Description
                // This acts as a virtual property in our resulting dictionary
                let sumDescription = NSExpressionDescription()
                sumDescription.name = "totalAmount" // The key in the resulting dictionary
                sumDescription.expression = sumExpression
                sumDescription.expressionResultType = .doubleAttributeType
                
// Mock expensesStream for ChartViewModel compilation
    var expensesStream: AsyncStream<[TransactionEntity]> {
        AsyncStream { continuation in
            continuation.finish()
        }
    }
    
    init() {
        persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    }

    /// Fetches transactions for a date range and aggregates totals by category in-memory.
    /// - Warning: Can cause memory spikes and faulting overhead for large datasets.
    func fetchCategoryTotalsInMemory(startDate: Date, endDate: Date) -> [CategoryTotal] {
        let context = persistentContainer.viewContext
        let request: NSFetchRequest = NSFetchRequest(entityName: "TransactionEntity")
        
        // 1. Filter by date to minimize fetched data
        request.predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate as NSDate, endDate as NSDate)
        
        // 2. Optimization: Prefetch relationships to avoid faulting loops later
        request.relationshipKeyPathsForPrefetching = ["category"]
        
        do {
            // 3. Fetch all matching entities into memory
            let transactions = try context.fetch(request)
            
            // 4. Group by Category Name
            // If category is nil, we default to "Uncategorized"
            let grouped = Dictionary(grouping: transactions) { transaction in
                transaction.category?.name ?? "Uncategorized"
            }
            
            // 5. Map and reduce to our domain model
            let totals: [CategoryTotal] = grouped.compactMap { (categoryName, txs) in
                // Reduce the array of transactions into a single sum
                let total = txs.reduce(0.0) { $0 + $1.amount }
                
                // Exclude categories with zero spending if desired
                guard total > 0 else { return nil }
                
                // Grab color from the first transaction's category, or default to gray
                let colorHex = txs.first?.category?.colorHex ?? "#808080" 
                
                return CategoryTotal(
                    categoryName: categoryName,
                    totalAmount: total,
                    categoryColorHex: colorHex
                )
            }
            
            // 6. Sort by amount descending for a better visual chart presentation
            return totals.sorted { $0.totalAmount > $1.totalAmount }
            
        } catch {
            print("Error fetching for in-memory aggregation: \(error)")
            return []
        }
    }
}
import Foundation
import CoreData

@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
}

class ExpenseRepository {
    func advancedFetch(request: NSFetchRequest, dict: [String: Any], categoryNameKeyPath: String, categoryColorKeyPath: String) {
        // 1. Create Count Expression
        let countExpression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: #keyPath(TransactionEntity.amount))])
        
        let countDescription = NSExpressionDescription()
        countDescription.name = "transactionCount"
        countDescription.expression = countExpression
        countDescription.expressionResultType = .integer32AttributeType
        
        // Mock sumDescription for compilation
        let sumDescription = NSExpressionDescription()
        
        // 2. Add to propertiesToFetch
        request.propertiesToFetch = [categoryNameKeyPath, categoryColorKeyPath, sumDescription, countDescription]
        
        // 3. Map in results loop
        let count = dict["transactionCount"] as? Int ?? 0
    }
}
// 5. Define grouping and properties to fetch // We want to group by the category's name and color. let categoryNameKeyPath = #keyPath(TransactionEntity.category.name) let categoryColorKeyPath = #keyPath(TransactionEntity.category.colorHex) request.propertiesToFetch = [categoryNameKeyPath, categoryColorKeyPath, sumDescription] request.propertiesToGroupBy = [categoryNameKeyPath, categoryColorKeyPath] do { // 6. Execute the fetch (this translates to SELECT ... GROUP BY in SQL) let results = try context.fetch(request)
import SwiftUI
import Charts
import Observation
import CoreData

struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

extension Color {
    init(hex: String) { self.init(UIColor.gray) }
}

@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
    @NSManaged var date: Date?
    @NSManaged var category: CategoryEntity?
}

@objc(CategoryEntity)
class CategoryEntity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var colorHex: String?
}

class ExpenseRepository {
    static let shared = ExpenseRepository()
    let persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    var expensesStream: AsyncStream<[TransactionEntity]> { AsyncStream { $0.finish() } }
    func fetchCategoryTotalsAsync(startDate: Date, endDate: Date) async -> [CategoryTotal] { [] }
}

@MainActor
@Observable class ChartViewModel {
    var chartData: [CategoryTotal] = []
    var selectedMonth: Date = Date()
    var isLoading: Bool = false
    var totalSpent: Double = 0
}

struct ExpenseBarChartView: View {
    @Bindable var viewModel: ChartViewModel
    
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Monthly Breakdown")
                .font(.title2)
                .bold()
            
            if viewModel.isLoading && viewModel.chartData.isEmpty {
                ProgressView()
                    .frame(height: 300)
                    .frame(maxWidth: .infinity)
            } else if viewModel.chartData.isEmpty {
                Text("No data for this month.")
                    .foregroundColor(.secondary)
                    .frame(height: 300)
                    .frame(maxWidth: .infinity, alignment: .center)
            } else {
                Chart(viewModel.chartData) { item in
                    BarMark(
                        x: .value("Amount", item.totalAmount),
                        y: .value("Category", item.categoryName)
                    )
                    // Customize the bar color based on our domain model
                    .foregroundStyle(Color(hex: item.categoryColorHex))
                    // Add annotations to the end of each bar
                    .annotation(position: .trailing) {
                        Text(item.totalAmount, format: .currency(code: "USD"))
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                    // Corner radius for a polished look
                    .cornerRadius(4)
                }
                .frame(height: 300)
                // Animate changes when the user switches months
                .animation(.easeInOut, value: viewModel.chartData)
            }
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(12)
        .shadow(color: .black.opacity(0.1), radius: 5, x: 0, y: 2)
    }
}
import SwiftUI
import Charts
import Observation
import CoreData

struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

extension Color {
    init(hex: String) { self.init(UIColor.gray) }
}

@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
    @NSManaged var date: Date?
    @NSManaged var category: CategoryEntity?
}

@objc(CategoryEntity)
class CategoryEntity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var colorHex: String?
}

class ExpenseRepository {
    static let shared = ExpenseRepository()
    let persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    var expensesStream: AsyncStream<[TransactionEntity]> { AsyncStream { $0.finish() } }
    func fetchCategoryTotalsAsync(startDate: Date, endDate: Date) async -> [CategoryTotal] { [] }
}

@MainActor
@Observable class ChartViewModel {
    var chartData: [CategoryTotal] = []
    var selectedMonth: Date = Date()
    var isLoading: Bool = false
    var totalSpent: Double = 0
}

struct ExpenseDonutChartView: View {
    @Bindable var viewModel: ChartViewModel
    
    var body: some View {
        VStack {
            Text("Spending Distribution")
                .font(.headline)
            
            Chart(viewModel.chartData) { item in
                SectorMark(
                    angle: .value("Amount", item.totalAmount),
                    innerRadius: .ratio(0.6), // Makes it a donut instead of a pie
                    angularInset: 1.5 // Adds a small gap between slices for visual clarity
                )
                .foregroundStyle(Color(hex: item.categoryColorHex))
                .cornerRadius(4)
            }
            .frame(height: 250)
            // chartBackground allows us to overlay a view perfectly in the plot area
            .chartBackground { chartProxy in
                GeometryReader { geometry in
                    // We use the proxy to get the exact frame of the pie area
                    if let plotFrame = chartProxy.plotFrame {
                        let frame = geometry[plotFrame]
                        VStack {
                            Text("Total")
                                .font(.caption)
                                .foregroundColor(.secondary)
                            Text(viewModel.totalSpent, format: .currency(code: "USD"))
                                .font(.title2.bold())
                                .foregroundColor(.primary)
                                // Scale text to fit if the total gets too large
                                .minimumScaleFactor(0.5)
                        }
                        // Position exactly in the center of the donut hole
                        .position(x: frame.midX, y: frame.midY)
                    }
                }
            }
            .animation(.spring(), value: viewModel.chartData)
            
            // Custom Legend since standard charts legend can look cluttered with many categories
            LazyVGrid(columns: [GridItem(.adaptive(minimum: 120))], alignment: .leading, spacing: 8) {
                ForEach(viewModel.chartData) { item in
                    HStack {
                        Circle()
                            .fill(Color(hex: item.categoryColorHex))
                            .frame(width: 8, height: 8)
                        Text(item.categoryName)
                            .font(.caption)
                            .lineLimit(1)
                    }
                }
            }
            .padding(.top, 10)
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(12)
        .shadow(color: .black.opacity(0.1), radius: 5, x: 0, y: 2)
    }
}
import SwiftUI
import Charts
import Observation
import CoreData

struct CategoryTotal: Identifiable, Hashable {
    let id = UUID()
    let categoryName: String
    let totalAmount: Double
    let categoryColorHex: String
}

extension Color {
    init(hex: String) { self.init(UIColor.gray) }
}

@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
    @NSManaged var date: Date?
    @NSManaged var category: CategoryEntity?
}

@objc(CategoryEntity)
class CategoryEntity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var colorHex: String?
}

class ExpenseRepository {
    static let shared = ExpenseRepository()
    let persistentContainer = NSPersistentContainer(name: "ExpenseTracker")
    var expensesStream: AsyncStream<[TransactionEntity]> { AsyncStream { $0.finish() } }
    func fetchCategoryTotalsAsync(startDate: Date, endDate: Date) async -> [CategoryTotal] { [] }
}

@MainActor
@Observable class ChartViewModel {
    var chartData: [CategoryTotal] = []
    var selectedMonth: Date = Date()
    var isLoading: Bool = false
    var totalSpent: Double = 0
    init(repository: ExpenseRepository) {}
}

struct MonthPickerView: View {
    @Binding var selectedDate: Date
    var body: some View { EmptyView() }
}

struct ExpenseDonutChartView: View {
    var viewModel: ChartViewModel
    var body: some View { EmptyView() }
}

struct ExpenseBarChartView: View {
    var viewModel: ChartViewModel
    var body: some View { EmptyView() }
}

struct StatisticsDashboardView: View {
    // In a real app with proper DI, inject the repository into the view environment
    @State private var viewModel = ChartViewModel(repository: ExpenseRepository.shared)
    
    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 24) {
                    // Month Selector
                    MonthPickerView(selectedDate: $viewModel.selectedMonth)
                    
                    // Charts
                    ExpenseDonutChartView(viewModel: viewModel)
                    ExpenseBarChartView(viewModel: viewModel)
                }
                .padding()
            }
            .navigationTitle("Insights")
            .background(Color(.systemGroupedBackground))
        }
    }
}

Advanced Aggregation Considerations

Multiple Expressions (Count, Average)

import Foundation
import CoreData

@objc(TransactionEntity)
class TransactionEntity: NSManagedObject {
    @NSManaged var amount: Double
}

class ExpenseRepository {
    func advancedFetch(request: NSFetchRequest, dict: [String: Any], categoryNameKeyPath: String, categoryColorKeyPath: String) {
        // 1. Create Count Expression
        let countExpression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: #keyPath(TransactionEntity.amount))])
        
        let countDescription = NSExpressionDescription()
        countDescription.name = "transactionCount"
        countDescription.expression = countExpression
        countDescription.expressionResultType = .integer32AttributeType
        
        // Mock sumDescription for compilation
        let sumDescription = NSExpressionDescription()
        
        // 2. Add to propertiesToFetch
        request.propertiesToFetch = [categoryNameKeyPath, categoryColorKeyPath, sumDescription, countDescription]
        
        // 3. Map in results loop
        let count = dict["transactionCount"] as? Int ?? 0
    }
}

The "Gotcha": Grouping by Date

Reviewing the "UIKit Way" in a SwiftUI World

  1. Separation of Concerns: Our SwiftUI views remain pure UI components. They don't know where the data comes from, making them easier to preview, test, and reuse.
  2. Performance Control: @FetchRequest triggers UI updates whenever any relevant data changes. While convenient, it can cause severe over-rendering. Our ViewModel approach allows us to debounce updates, aggregate data off the main thread, and precisely control when the UI redraws.
  3. Complex Queries: Try writing a complex GROUP BY and SUM query using just @FetchRequest parameters. It's often impossible or incredibly convoluted. Hand-rolling the NSFetchRequest gives us access to the full power of SQLite while keeping the UI layer clean.
graph LR subgraph UI_Layer__SwiftUI_View_ ["UI Layer (SwiftUI View)"] Chart[Chart View] end subgraph Presentation_Layer__ViewModel_ ["Presentation Layer (ViewModel)"] VM[ChartViewModel] Model[Domain Models] end subgraph Data_Access_Layer__Repository_ ["Data Access Layer (Repository)"] Repo[ExpenseRepository] Fetch[NSFetchRequest] Expr[NSExpression] end subgraph Storage__Core_Data_ ["Storage (Core Data)"] Context[NSManagedObjectContext] SQL[(SQLite Store)] end Chart -->|"Observes @Observable"| VM VM -->|"Maps to"| Model VM -->|"Spawns Task"| Repo Repo -->|"Constructs"| Fetch Fetch -->|"Contains"| Expr Repo -->|"Executes async"| Context Context --> SQL

Conclusion