Skip to content

Conversation

@authanram
Copy link

Problem

When building time-series analytics, yearly aggregations, or conditional counts based on years, developers currently need to fall back to DB::raw() with database-specific SQL:

// MySQL
User::select(DB::raw('YEAR(created_at) as year'))->groupBy(DB::raw('YEAR(created_at)'));

// PostgreSQL
User::select(DB::raw('EXTRACT(YEAR FROM created_at)::int as year'))->groupBy(DB::raw('EXTRACT(YEAR FROM created_at)'));

// SQLite
User::select(DB::raw("CAST(strftime('%Y', created_at) AS INTEGER) as year"))->groupBy(DB::raw("strftime('%Y', created_at)"));

This defeats the purpose of the query-expressions package and forces developers to write database-specific code or maintain multiple query variants.

Solution

Add an ExtractYear expression that generates the appropriate SQL for each supported database:

use Tpetry\QueryExpressions\Function\Time\ExtractYear;

// Works on all supported databases
User::select([
    new Alias(new ExtractYear('created_at'), 'year'),
    new Count('*'),
])->groupBy(new ExtractYear('created_at'))->get();

// Combine with CountFilter for multi-year statistics in a single query
Movie::select([
    new Alias(new CountFilter(new Equal(new ExtractYear('released_at'), new Value(2023))), 'count_2023'),
    new Alias(new CountFilter(new Equal(new ExtractYear('released_at'), new Value(2024))), 'count_2024'),
])->first();
Database Generated SQL
MySQL/MariaDB year(column)
PostgreSQL extract(year from column)::int
SQLite cast(strftime('%Y', column) as integer)
SQL Server year(column)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant