Skip to content

Latest commit

 

History

History
575 lines (484 loc) · 16.4 KB

File metadata and controls

575 lines (484 loc) · 16.4 KB

Analytics Engine - Complete Tutorial

Version: 1.7.0

Table of Contents

  1. Setup & Initialization
  2. Data Preparation
  3. Aggregate Functions Deep Dive
  4. Window Functions Deep Dive
  5. Statistical Analysis
  6. Real-World Examples
  7. Performance Tuning

Setup & Initialization

Project Configuration

Create a new console application:

dotnet new console -n AnalyticsDemo
cd AnalyticsDemo
dotnet add package SharpCoreDB --version 1.7.0
dotnet add package SharpCoreDB.Analytics --version 1.7.0

Dependency Injection Setup

using Microsoft.Extensions.DependencyInjection;
using SharpCoreDB;
using SharpCoreDB.Analytics;

var services = new ServiceCollection();

// Register SharpCoreDB
services.AddSharpCoreDB();

// Add analytics services
services.AddAnalyticsEngine();

var provider = services.BuildServiceProvider();
var database = provider.GetRequiredService<IDatabase>();

// Initialize database with sample data
await InitializeSampleDataAsync(database);

Data Preparation

Create Sample Tables

private static async Task InitializeSampleDataAsync(IDatabase database)
{
    // Create employees table
    await database.ExecuteAsync(@"
        CREATE TABLE IF NOT EXISTS employees (
            id INT PRIMARY KEY,
            name TEXT NOT NULL,
            department TEXT NOT NULL,
            hire_date TEXT NOT NULL,
            salary DECIMAL NOT NULL,
            manager_id INT,
            performance_score FLOAT
        )
    ");

    // Create sales table
    await database.ExecuteAsync(@"
        CREATE TABLE IF NOT EXISTS sales (
            id INT PRIMARY KEY,
            employee_id INT NOT NULL,
            sale_date TEXT NOT NULL,
            amount DECIMAL NOT NULL,
            region TEXT NOT NULL
        )
    ");

    // Insert sample data
    var employees = new[]
    {
        "INSERT INTO employees VALUES (1, 'Alice Johnson', 'Engineering', '2020-03-15', 95000, NULL, 4.8)",
        "INSERT INTO employees VALUES (2, 'Bob Smith', 'Engineering', '2021-06-20', 85000, 1, 4.5)",
        "INSERT INTO employees VALUES (3, 'Carol Davis', 'Sales', '2019-01-10', 75000, NULL, 4.3)",
        "INSERT INTO employees VALUES (4, 'David Wilson', 'Sales', '2022-04-05', 65000, 3, 4.0)",
        "INSERT INTO employees VALUES (5, 'Emma Brown', 'Marketing', '2023-08-15', 70000, NULL, 4.2)",
        "INSERT INTO employees VALUES (6, 'Frank Miller', 'Engineering', '2023-02-28', 80000, 1, 3.9)",
    };

    foreach (var stmt in employees)
    {
        await database.ExecuteAsync(stmt);
    }

    // Insert sales data
    var sales = new[]
    {
        "INSERT INTO sales VALUES (1, 1, '2026-01-01', 50000, 'North')",
        "INSERT INTO sales VALUES (2, 1, '2026-01-02', 45000, 'North')",
        "INSERT INTO sales VALUES (3, 2, '2026-01-01', 30000, 'North')",
        "INSERT INTO sales VALUES (4, 3, '2026-01-01', 60000, 'South')",
        "INSERT INTO sales VALUES (5, 3, '2026-01-02', 55000, 'South')",
        "INSERT INTO sales VALUES (6, 4, '2026-01-01', 25000, 'South')",
    };

    foreach (var stmt in sales)
    {
        await database.ExecuteAsync(stmt);
    }

    await database.FlushAsync();
}

Aggregate Functions Deep Dive

Understanding COUNT

COUNT aggregates can work in different ways:

// Example 1: Count all rows
var totalEmps = await database.QuerySingleAsync(
    "SELECT COUNT(*) as total FROM employees"
);
Console.WriteLine($"Total employees: {totalEmps["total"]}"); // 6

// Example 2: Count non-NULL values
var managed = await database.QuerySingleAsync(
    "SELECT COUNT(manager_id) as managed_count FROM employees"
);
Console.WriteLine($"Employees with managers: {managed["managed_count"]}"); // 2

// Example 3: Count distinct departments
var depts = await database.QuerySingleAsync(
    "SELECT COUNT(DISTINCT department) as dept_count FROM employees"
);
Console.WriteLine($"Departments: {depts["dept_count"]}"); // 3

SUM and AVG Examples

// Total and average salary
var salaryStats = await database.QuerySingleAsync(@"
    SELECT 
        SUM(salary) as total_payroll,
        AVG(salary) as avg_salary,
        COUNT(*) as employee_count
    FROM employees
");

var totalPayroll = (decimal)salaryStats["total_payroll"];
var avgSalary = (decimal)salaryStats["avg_salary"];

Console.WriteLine($"Payroll: ${totalPayroll:N2}");
Console.WriteLine($"Average: ${avgSalary:N2}");

// By department
var byDept = await database.QueryAsync(@"
    SELECT 
        department,
        COUNT(*) as count,
        SUM(salary) as total,
        AVG(salary) as average,
        MIN(salary) as minimum,
        MAX(salary) as maximum
    FROM employees
    GROUP BY department
    ORDER BY total DESC
");

foreach (var row in byDept)
{
    Console.WriteLine($"\n{row["department"]}:");
    Console.WriteLine($"  Employees: {row["count"]}");
    Console.WriteLine($"  Total: ${row["total"]}");
    Console.WriteLine($"  Avg: ${row["average"]}");
    Console.WriteLine($"  Range: ${row["minimum"]} - ${row["maximum"]}");
}

Statistical Functions

// Analyze salary distribution
var distribution = await database.QuerySingleAsync(@"
    SELECT 
        COUNT(*) as total,
        AVG(salary) as mean,
        MIN(salary) as min_val,
        MAX(salary) as max_val,
        STDDEV(salary) as std_dev,
        VARIANCE(salary) as variance,
        PERCENTILE(salary, 0.25) as q1,
        PERCENTILE(salary, 0.50) as median,
        PERCENTILE(salary, 0.75) as q3
    FROM employees
");

var mean = (decimal)distribution["mean"];
var stdDev = (decimal)distribution["std_dev"];
var q1 = (decimal)distribution["q1"];
var median = (decimal)distribution["median"];
var q3 = (decimal)distribution["q3"];

Console.WriteLine("Salary Distribution:");
Console.WriteLine($"  Mean: ${mean:N2}");
Console.WriteLine($"  Std Dev: ${stdDev:N2}");
Console.WriteLine($"  Q1 (25%): ${q1:N2}");
Console.WriteLine($"  Median (50%): ${median:N2}");
Console.WriteLine($"  Q3 (75%): ${q3:N2}");
Console.WriteLine($"  IQR: ${q3 - q1:N2}");

Window Functions Deep Dive

ROW_NUMBER() - Sequential Numbering

// Rank employees by salary within each department
var ranked = await database.QueryAsync(@"
    SELECT 
        name,
        department,
        salary,
        ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as rank_in_dept,
        ROW_NUMBER() OVER (ORDER BY salary DESC) as overall_rank
    FROM employees
    ORDER BY department, overall_rank
");

Console.WriteLine("Department Rankings:");
foreach (var emp in ranked)
{
    Console.WriteLine(
        $"{emp["name"],-20} | {emp["department"],-12} | " +
        $"${emp["salary"],-8} | Dept Rank: {emp["rank_in_dept"]} | Overall: {emp["overall_rank"]}"
    );
}

RANK() vs DENSE_RANK()

// Show difference between RANK and DENSE_RANK
var rankings = await database.QueryAsync(@"
    SELECT 
        name,
        performance_score,
        RANK() OVER (ORDER BY performance_score DESC) as rank_with_gaps,
        DENSE_RANK() OVER (ORDER BY performance_score DESC) as dense_rank
    FROM employees
    ORDER BY performance_score DESC
");

Console.WriteLine("Performance Rankings:");
Console.WriteLine("Name                 | Score | RANK | DENSE_RANK");
Console.WriteLine(new string('-', 55));

foreach (var row in rankings)
{
    Console.WriteLine(
        $"{(string)row["name"],-20} | {(float)row["performance_score"],5:F1} | " +
        $"{(int)row["rank_with_gaps"],4} | {(int)row["dense_rank"],10}"
    );
}

Partitioning - Running Totals

// Running total of sales by date and region
var running = await database.QueryAsync(@"
    SELECT 
        sale_date,
        region,
        amount,
        SUM(amount) OVER (
            PARTITION BY region 
            ORDER BY sale_date 
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) as running_total
    FROM sales
    ORDER BY region, sale_date
");

Console.WriteLine("Running Totals by Region:");
foreach (var sale in running)
{
    Console.WriteLine(
        $"{sale["sale_date"]} | {sale["region"],-8} | " +
        $"${sale["amount"]:N0} | Running: ${sale["running_total"]:N0}"
    );
}

Statistical Analysis

Identify Outliers

// Find employees earning more than 2 standard deviations from mean
var outliers = await database.QueryAsync(@"
    SELECT 
        name,
        salary,
        (SELECT AVG(salary) FROM employees) as company_avg,
        (SELECT STDDEV(salary) FROM employees) as std_dev,
        salary - (SELECT AVG(salary) FROM employees) as diff_from_avg,
        ROUND(
            (salary - (SELECT AVG(salary) FROM employees)) / 
            (SELECT STDDEV(salary) FROM employees), 
            2
        ) as z_score,
        CASE 
            WHEN ABS(salary - (SELECT AVG(salary) FROM employees)) > 
                 2 * (SELECT STDDEV(salary) FROM employees)
            THEN 'OUTLIER'
            ELSE 'Normal'
        END as classification
    FROM employees
");

Console.WriteLine("Outlier Analysis:");
foreach (var emp in outliers)
{
    Console.WriteLine(
        $"{(string)emp["name"],-20} | Salary: ${(decimal)emp["salary"],8:N0} | " +
        $"Z-Score: {(decimal)emp["z_score"],6:F2} | {(string)emp["classification"]}"
    );
}

Correlation Analysis

// Analyze correlation between performance and salary
var correlation = await database.QuerySingleAsync(@"
    SELECT CORRELATION(salary, performance_score) as corr_salary_perf
    FROM employees
");

var corr = (double)correlation["corr_salary_perf"];

Console.WriteLine($"Correlation (Salary vs Performance): {corr:F3}");
Console.WriteLine($"Interpretation: ", corr switch
{
    > 0.7 => "Strong positive correlation",
    > 0.3 => "Moderate positive correlation",
    > 0 => "Weak positive correlation",
    0 => "No correlation",
    > -0.3 => "Weak negative correlation",
    > -0.7 => "Moderate negative correlation",
    _ => "Strong negative correlation"
});

Real-World Examples

Sales Performance Dashboard

public class SalesAnalytics
{
    public async Task GenerateDashboardAsync(IDatabase database)
    {
        var dashboard = await database.QueryAsync(@"
            SELECT 
                e.name,
                e.department,
                COUNT(s.id) as transactions,
                SUM(s.amount) as total_sales,
                AVG(s.amount) as avg_transaction,
                MAX(s.amount) as largest_sale,
                MIN(s.amount) as smallest_sale,
                ROW_NUMBER() OVER (ORDER BY SUM(s.amount) DESC) as sales_rank
            FROM employees e
            LEFT JOIN sales s ON e.id = s.employee_id
            GROUP BY e.id, e.name, e.department
            ORDER BY total_sales DESC
        ");

        Console.WriteLine("Sales Performance Dashboard:");
        Console.WriteLine(new string('=', 100));
        
        foreach (var emp in dashboard)
        {
            Console.WriteLine(
                $"#{(int)emp["sales_rank"]} | {(string)emp["name"],-20} | " +
                $"Dept: {(string)emp["department"],-12} | " +
                $"Transactions: {(int)emp["transactions"]} | " +
                $"Total: ${(decimal)emp["total_sales"]:N0} | " +
                $"Avg: ${(decimal)emp["avg_transaction"]:N0}"
            );
        }
    }
}

Department Performance Report

public class DepartmentAnalytics
{
    public async Task GenerateReportAsync(IDatabase database)
    {
        var report = await database.QueryAsync(@"
            SELECT 
                department,
                COUNT(*) as headcount,
                ROUND(AVG(salary), 2) as avg_salary,
                MIN(salary) as min_salary,
                MAX(salary) as max_salary,
                ROUND(STDDEV(salary), 2) as salary_stddev,
                ROUND(AVG(performance_score), 2) as avg_performance,
                COUNT(CASE WHEN performance_score >= 4.5 THEN 1 END) as high_performers
            FROM employees
            GROUP BY department
            HAVING COUNT(*) > 0
            ORDER BY avg_salary DESC
        ");

        Console.WriteLine("\nDepartment Performance Report:");
        Console.WriteLine(new string('=', 120));

        foreach (var dept in report)
        {
            Console.WriteLine(
                $"\n{(string)dept["department"]}\n" +
                $"  Headcount: {(int)dept["headcount"]}\n" +
                $"  Avg Salary: ${(decimal)dept["avg_salary"]:N2}\n" +
                $"  Salary Range: ${(decimal)dept["min_salary"]:N0} - ${(decimal)dept["max_salary"]:N0}\n" +
                $"  Salary Std Dev: ${(decimal)dept["salary_stddev"]:N2}\n" +
                $"  Avg Performance: {(decimal)dept["avg_performance"]:F2}/5.0\n" +
                $"  High Performers (≥4.5): {(int)dept["high_performers"]}"
            );
        }
    }
}

Compensation Equity Analysis

public async Task AnalyzeCompensationEquityAsync(IDatabase database)
{
    var equity = await database.QueryAsync(@"
        SELECT 
            name,
            department,
            salary,
            AVG(salary) OVER (PARTITION BY department) as dept_avg,
            salary - AVG(salary) OVER (PARTITION BY department) as variance_from_dept_avg,
            ROUND(
                100.0 * (salary - AVG(salary) OVER (PARTITION BY department)) / 
                AVG(salary) OVER (PARTITION BY department),
                2
            ) as pct_variance,
            PERCENTILE(salary, 0.5) OVER (PARTITION BY department) as dept_median,
            CASE 
                WHEN salary < AVG(salary) OVER (PARTITION BY department) - STDDEV(salary) OVER (PARTITION BY department)
                THEN 'Significantly Below Market'
                WHEN salary < AVG(salary) OVER (PARTITION BY department)
                THEN 'Below Market'
                WHEN salary < AVG(salary) OVER (PARTITION BY department) + STDDEV(salary) OVER (PARTITION BY department)
                THEN 'Market Rate'
                ELSE 'Above Market'
            END as market_position
        FROM employees
        ORDER BY department, salary DESC
    ");

    Console.WriteLine("\nCompensation Equity Analysis:");
    foreach (var emp in equity)
    {
        Console.WriteLine(
            $"{(string)emp["name"],-20} | Dept Avg: ${(decimal)emp["dept_avg"]:N0} | " +
            $"Variance: {(decimal)emp["pct_variance"]:+0.0%;-0.0%} | " +
            $"{(string)emp["market_position"]}"
        );
    }
}

Performance Tuning

Indexing Strategy

// Create indexes on columns used in aggregation/partitioning
await database.ExecuteAsync(
    "CREATE INDEX idx_employees_department ON employees(department)"
);
await database.ExecuteAsync(
    "CREATE INDEX idx_sales_employee_date ON sales(employee_id, sale_date)"
);
await database.ExecuteAsync(
    "CREATE INDEX idx_sales_region ON sales(region)"
);

Query Optimization Patterns

// Pattern 1: Pre-filter before aggregation
var optimized = await database.QueryAsync(@"
    SELECT 
        department,
        COUNT(*) as count,
        AVG(salary) as avg_salary
    FROM employees
    WHERE hire_date >= '2023-01-01'
    GROUP BY department
");

// Pattern 2: Use partitioning instead of subqueries
var efficient = await database.QueryAsync(@"
    SELECT 
        name,
        salary,
        AVG(salary) OVER (PARTITION BY department) as dept_avg,
        salary - AVG(salary) OVER (PARTITION BY department) as diff
    FROM employees
");

// Pattern 3: Combine aggregates in single query
var combined = await database.QueryAsync(@"
    SELECT 
        department,
        COUNT(*) as emp_count,
        SUM(salary) as total_salary,
        AVG(salary) as avg_salary,
        STDDEV(salary) as salary_stddev
    FROM employees
    GROUP BY department
");

Summary

The Analytics Engine in SharpCoreDB v1.3.5 provides:

Basic Aggregates - COUNT, SUM, AVG, MIN, MAX ✅ Statistical Functions - STDDEV, VARIANCE, PERCENTILE, CORRELATION ✅ Window Functions - ROW_NUMBER, RANK, DENSE_RANK with PARTITION BY ✅ Performance - 150-680x faster than SQLite ✅ Production Ready - Fully tested and optimized

For more information, see:


Last Updated: February 19, 2026 | Phase 9.2