Fix Slow Prisma Queries With Direct SQL Execution

Love Prisma's DX but need lower read overhead on supported query shapes? Current benchmark JSON shows average speedups across Prisma v6, v7, and v8 for supported PostgreSQL and SQLite reads; individual query shapes vary.

Speed Up Prisma Now

  • Faster Supported Reads
  • No Query Changes
  • Current Benchmark JSON
  • PostgreSQL & SQLite
Your Prisma calluser.findMany
const users = await prisma.user.findMany({
  where: { status: 'ACTIVE' },
  include: { posts: true },
})
SQL that prisma-sql runs
SELECT user_t.id, user_t.email, user_t.status,  COALESCE((    SELECT COALESCE(json_agg(json_build_object(      'id', posts_0.id,      'title', posts_0.title,      'authorId', posts_0."authorId"    )), '[]'::json)    FROM "public"."Post" posts_0    WHERE posts_0."authorId" = user_t.id  ), '[]'::json) AS postsFROM "public"."User" user_tWHERE user_t.status = $1
params ["ACTIVE"]1 statement
Output of createToSQL for a two-model schema, line breaks added.
Prisma Coverage
v6–v8
PostgreSQL and SQLite benchmark matrix
Query Changes
0
Keep your existing Prisma calls
Benchmark Files
6
Prisma v6/v7/v8 × PG/SQLite
Type Safety
100%
Same Prisma API

Why Is Prisma Slow?

Understanding Prisma's evolution and performance characteristics helps explain why this extension exists.

The Prisma Journey: DX First, Performance Matters at Scale

💡 Why This Extension Exists

Prisma made the right architectural choices for its goals: type safety, developer experience, and cross-database behavior. But those choices create overhead that's noticeable at scale. This extension doesn't replace Prisma—it optimizes reads for teams that want Prisma's DX plus faster execution where it matters.

  1. 🎯 2019: The Birth of Modern Prisma

    Prisma 2 launched in 2019, changing the TypeScript ORM landscape with type-safe database access and generated types from your schema. Prisma introduced an engine-based architecture to translate queries, validate them, and provide strong guarantees that were hard to achieve with traditional JavaScript ORMs.

  2. ⚡ 2020–2022: Rapid Growth & Feature Expansion

    Prisma added powerful features like nested writes, transactions, and middleware. The feature set expanded, but every query still paid an architectural cost: queries are represented, validated, executed, and results are shaped to match the Prisma API.

  3. 📊 2023: Overhead Becomes Noticeable at Scale

    As more teams deployed Prisma in high-traffic workloads, the fixed per-query overhead became measurable. This is most visible on read-heavy endpoints, analytics, aggregations, and large result sets. The overhead is not a bug; it's the cost of Prisma’s guarantees and API behavior.

  4. 🚀 2024–2025: Prisma Performance Work Continues

    Prisma shipped major updates focused on performance and engine changes. Even with improvements, there is still an unavoidable cost to parsing, validating, planning, and shaping results compared to executing raw SQL directly.

  5. 🎯 2026: prisma-sql Extension Released

    This extension focuses on read performance. It bypasses Prisma’s read execution path for findMany, findFirst, findUnique, count, aggregate, and groupBy, while keeping Prisma for writes, migrations, schema management, and type generation. Validate compatibility with your Prisma version before rollout.

Query Translation Layer

Prisma translates your query inputs into database-specific SQL. This enables cross-database behavior and Prisma's API semantics, but adds processing time before the database sees the SQL.

Validation & Type Guarantees

Prisma validates queries against the schema and enforces API-level guarantees. These safeguards prevent classes of bugs, but they also add overhead to each query.

Result Shaping

Results are shaped to match Prisma's API behavior. This is great for DX and consistency, but it adds latency, especially on large result sets and complex includes.

This extension complements Prisma by offering a faster path for supported read queries. You keep Prisma for schema, migrations, writes, and generated types while generated SQL handles selected hot reads through database-native clients.

How Much Faster? Real Benchmarks

Latest benchmark JSON comparison across Prisma v6, v7, v8, Drizzle ORM, and prisma-sql

PostgreSQL

95 query cases per run
  • Prisma v6 (6.19.3)1.56× vs Drizzle
    1.99×

    Prisma v6 (6.19.3): 1.99× average speedup versus Prisma

    95 measured query cases, average vs PrismaSep 30, 2026
  • Prisma v7 (7.10.0)1.51× vs Drizzle
    1.47×

    Prisma v7 (7.10.0): 1.47× average speedup versus Prisma

    95 measured query cases, average vs PrismaSep 30, 2026
  • Prisma v8 (8.1.0-dev.1)1.63× vs Drizzle
    1.54×

    Prisma v8 (8.1.0-dev.1): 1.54× average speedup versus Prisma

    95 measured query cases, average vs PrismaSep 30, 2026

SQLite

84 query cases per run
  • Prisma v6 (6.19.3)1.79× vs Drizzle
    4.80×

    Prisma v6 (6.19.3): 4.80× average speedup versus Prisma

    84 measured query cases, average vs PrismaSep 30, 2026
  • Prisma v7 (7.10.0)1.84× vs Drizzle
    2.29×

    Prisma v7 (7.10.0): 2.29× average speedup versus Prisma

    84 measured query cases, average vs PrismaSep 30, 2026
  • Prisma v8 (8.1.0-dev.1)1.63× vs Drizzle
    2.10×

    Prisma v8 (8.1.0-dev.1): 2.10× average speedup versus Prisma

    84 measured query cases, average vs PrismaSep 30, 2026

Generated at build time from the latest benchmark-results/*-latest.json files. Bars share one scale across both databases; amber marks the best run per database. Individual query shapes vary.Per-query tableView complete benchmark data

Detailed Benchmark Results

Statistical comparison with Welch's t-test and a 1ms practical significance threshold

These results reflect runtime mode, where queries are converted to SQL on each request. In prerendered mode, SQL is generated at build time — runtime executes raw parameterized queries with zero conversion overhead.

PostgreSQLPrisma v6 (6.19.3)

Avg 1.99x vs PrismaAvg 1.56x vs Drizzle35/95 significant wins60 within noise floor
Sep 30, 2026
TestPrismaprisma-sqlDrizzleSpeedupSig.CV%n
findMany basic0.529ms±0.0640.231ms±0.2340.271ms~1.00x≈198.0%50
findMany where =0.501ms±0.00690.208ms±0.00940.347ms~1.00x≈15.6%50
findMany where >=16.51ms±0.2675.67ms±0.3387.43ms2.91x1.31x D***21.0%50
findMany where IN0.657ms±0.1420.292ms±0.0170.492ms~1.00x≈20.3%50
findMany where null0.280ms±0.0200.167ms±0.0120.177ms~1.00x≈25.7%50
findMany ILIKE0.297ms±0.00970.168ms±0.00890.278ms~1.00x≈18.6%50
findMany AND2.72ms±0.1611.11ms±0.1111.45ms2.45x1.31x D***31.5%50
findMany OR13.98ms±0.2864.69ms±0.2215.88ms2.98x1.25x D***17.0%50
findMany NOT0.639ms±0.0130.274ms±0.00910.478ms~1.00x≈12.2%50
findMany orderBy4.50ms±0.2191.48ms±0.0751.76ms3.03x1.18x D***18.5%50
findMany pagination0.342ms±0.0320.171ms±0.0260.295ms~1.00x≈43.2%50
findMany select0.294ms±0.00940.181ms±0.00720.176ms~1.00x≈14.0%50
findMany relation some1.69ms±0.0980.999ms±0.064—~1.00x≈22.1%50
findMany relation every2.17ms±0.0961.47ms±0.078—~1.00x≈18.9%50
findMany relation none26.02ms±0.51710.06ms±0.511—2.59x***18.0%50
findMany nested relation1.76ms±0.0761.24ms±0.060—~1.00x≈18.0%50
findMany complex2.94ms±0.1091.46ms±0.0681.73ms2.01x1.18x D***17.2%50
findFirst0.378ms±0.0320.175ms±0.0140.350ms~1.00x≈28.0%50
findFirst skip0.328ms±0.1070.147ms±0.00640.301ms~1.00x≈15.2%50
findUnique id0.232ms±0.00970.102ms±0.00550.207ms~1.00x≈18.6%50
findUnique email0.210ms±0.00720.090ms±0.00440.179ms~1.00x≈17.1%50
count0.109ms±0.00330.063ms±0.00300.064ms~1.00x≈16.2%50
count where0.469ms±0.0150.247ms±0.0160.287ms~1.00x≈22.2%50
aggregate count0.215ms±0.00780.155ms±0.0042—~1.00x≈9.2%50
aggregate sum/avg0.305ms±0.0140.261ms±0.0053—~1.00x≈7.1%50
aggregate where0.446ms±0.01000.239ms±0.0058—~1.00x≈8.7%50
aggregate min/max0.305ms±0.0150.268ms±0.010—~1.00x≈13.2%50
aggregate complete0.381ms±0.00610.292ms±0.0072—~1.00x≈8.8%50
groupBy0.419ms±0.0240.344ms±0.013—~1.00x≈12.8%50
groupBy count0.422ms±0.01000.358ms±0.0050—~1.00x≈4.8%50
groupBy multi0.544ms±0.0130.442ms±0.052—~1.00x≈38.9%50
groupBy having0.505ms±0.0110.375ms±0.0086—~1.00x≈8.2%50
groupBy + where0.550ms±0.0110.294ms±0.0064—~1.00x≈7.8%50
_count via include0.679ms±0.0130.511ms±0.013—~1.00x≈8.9%50
_count via include + relations1.55ms±0.0370.666ms±0.017—~1.00x≈9.3%50
groupBy aggregates0.557ms±0.00970.465ms±0.0091—~1.00x≈6.9%50
groupBy min/max0.531ms±0.00800.441ms±0.0053—~1.00x≈4.2%50
include list + nested to-one1.46ms±0.0290.337ms±0.032—4.32x***30.2%50
include list + nullable to-one2.59ms±0.0811.65ms±0.056—~1.00x≈11.9%50
include posts4.25ms±0.2162.35ms±0.1205.77ms1.81x2.45x D***18.9%50
include profile0.635ms±0.8710.540ms±0.2410.648ms~1.00x≈104.7%50
include 3 levels4.48ms±0.1484.03ms±0.1474.58ms~1.00x≈13.6%50
include 4 levels5.61ms±0.1823.39ms±0.1324.65ms1.65x1.37x D***13.9%50
include + where4.04ms±0.1762.88ms±0.3995.51ms~1.00x≈46.7%50
include + select nested2.52ms±0.1391.96ms±0.1854.42ms~1.00x≈31.8%50
findMany startsWith0.270ms±0.0280.140ms±0.0140.314ms~1.00x≈35.4%50
findMany endsWith0.563ms±0.0150.216ms±0.00690.369ms~1.00x≈11.1%50
findMany NOT contains0.627ms±0.0220.250ms±0.1040.447ms~1.00x≈121.8%50
findMany LIKE0.245ms±0.00940.111ms±0.00690.250ms~1.00x≈21.9%50
findMany <25.55ms±0.3987.55ms±0.29210.60ms3.39x1.40x D***13.4%50
findMany <=26.43ms±0.3717.78ms±0.22010.97ms3.40x1.41x D***10.2%50
findMany >15.55ms±0.5995.21ms±0.3566.78ms2.99x1.30x D***24.2%50
findMany NOT IN0.644ms±0.0130.282ms±0.0110.487ms~1.00x≈13.4%50
findMany isNot null0.642ms±0.0140.336ms±0.0170.362ms~1.00x≈18.6%50
orderBy multi-field4.99ms±0.1901.55ms±0.0761.88ms3.22x1.21x D***17.0%50
orderBy relation to-one4.10ms±0.1353.08ms±0.090—1.33x***10.2%50
orderBy relation + scalar5.02ms±0.2583.63ms±0.205—1.39x***20.0%50
orderBy nullable relation4.08ms±0.3002.94ms±0.181—1.39x***21.1%50
orderBy deep relation5.43ms±0.1864.04ms±0.174—1.34x***14.9%50
orderBy deep relation + scalar6.34ms±0.1354.62ms±0.100—1.37x***7.8%50
distinct status8.84ms±0.3761.56ms±0.181—5.66x***36.9%50
distinct multi12.04ms±0.1712.50ms±0.073—4.81x***10.3%50
cursor composite tuple1.61ms±0.0251.13ms±0.017—~1.00x≈5.3%50
cursor composite desc1.75ms±0.0371.31ms±0.021—~1.00x≈5.8%50
cursor prefix of orderBy2.70ms±0.1881.66ms±0.082—1.63x***18.1%50
select + include1.60ms±0.1010.309ms±0.0210.568ms5.16x1.84x D***24.6%50
_count relation1.74ms±0.3411.67ms±0.781—~1.00x≈123.8%50
_count multi-relation0.257ms±0.0290.218ms±0.045—~1.00x≈62.4%50
_count inside include2.32ms±0.1261.55ms±0.124—~1.00x≈32.0%50
_count inside nested select4.64ms±0.2023.69ms±0.266—~1.00x≈24.3%50
_count deep include5.49ms±0.2473.98ms±0.158—1.38x***14.1%50
ILIKE special chars0.425ms±0.0330.193ms±0.022—~1.00x≈36.5%50
LIKE case sensitive0.263ms±0.0100.110ms±0.0078—~1.00x≈24.0%50
findMany Date range0.424ms±0.0120.190ms±0.0120.321ms~1.00x≈21.1%50
count Date range1.14ms±0.0470.925ms±0.0520.995ms~1.00x≈19.8%50
findMany Date gte0.403ms±0.0250.178ms±0.00580.303ms~1.00x≈11.6%50
depth-1 low-fan0.566ms±0.0210.447ms±0.102—~1.00x≈73.7%50
depth-1 mid-fan4.21ms±0.1102.34ms±0.178—1.80x***26.3%50
depth-1 high-fan4.16ms±0.1222.25ms±0.130—1.85x***20.4%50
depth-1 wide3.11ms±0.1141.78ms±0.166—1.75x***31.3%50
depth-1 unbound39.65ms±3.789.43ms±0.884—4.21x***29.1%50
depth-24.78ms±0.1533.54ms±0.187—1.35x***18.6%50
depth-2 paginated Project→tasks3.16ms±0.1492.68ms±0.161—~1.00x≈20.6%50
depth-2 high-fan3.84ms±0.1852.86ms±0.229—~1.00x≈28.7%50
depth-2 wide4.90ms±0.1763.08ms±0.176—1.59x***20.6%50
depth-2 unbound65.60ms±1.5217.60ms±0.479—3.73x***4.4%10
depth-3 unbound15.32ms±0.2818.38ms±0.354—1.83x***15.8%50
depth-3 paginated2.44ms±0.1562.34ms±0.253—~1.00x≈32.7%50
depth-4 unbound9.96ms±0.2686.14ms±0.266—1.62x***15.7%50
depth-4 paginated3.42ms±0.1612.42ms±0.175—~1.00x≈23.6%50
findFirst depth-22.91ms±0.1142.30ms±0.173—~1.00x≈25.9%50
findUnique depth-2 Project→tasks→comment3.27ms±0.1132.16ms±0.173—1.52x***27.5%50
complex nested select4.51ms±0.4283.01ms±0.777—~1.00x≈65.7%50
ultra deep query15.94ms±0.5465.83ms±0.361—2.73x***22.0%50
transaction: (3 operations)3.00ms±0.2751.41ms±0.246—2.14x***53.2%50
*** p < 0.001** p < 0.01*  p < 0.05ns not significant≈ within 1ms noise floor± 95% CICV% > 15% = high variance
Runtime mode benchmarked. Prerendered mode skips query-to-SQL conversion entirely at runtime — expect lower latency than shown here, especially for complex queries where conversion cost is proportionally higher.

Methodology

  • –Reproduce with: npm run bench (BENCHMARK_JSON_OUTPUT=1 writes JSON). Results land in benchmark-results/summary-.json; v--latest.json feeds this page.
  • –Every query runs against the same database for all clients: Prisma, prisma-sql, Drizzle. Per-test sample size is shown in the n column.
  • –Reported per side: mean, median, stdDev, p95, p99, CV%. Significance uses Welch's t-test with a 1ms practical threshold; error bars are 95% CI.
  • –Runtime mode measured: SQL is converted per request. Prerendered (@optimize) queries skip conversion entirely and are faster than shown.
  • –Hardware and versions vary between runs - each JSON file carries its own timestamp. Compare runs from the same machine.

What runs faster, and what stays in Prisma

Every Prisma Client feature we checked, on PostgreSQL and SQLite. Writes stay in Prisma; reads are compiled to SQL where the shape is supported.

PostgreSQL
SQLite
Model methodsQuery optionsFiltersOrderingAggregatesClient APIResult types

25 of 73 checked features are fully accelerated on PostgreSQL. 19 can return a different result than Prisma; the full map lists each one with the reason.

Feature count by status
StatusPostgreSQLSQLite
Accelerated2521
Partial66
Falls back97
Runs in Prisma77
Can differ1917
Not available24
prisma-sql only52
Not in Prisma06
Not checked03
Open the full parity map
ValidatedHow do we ensure correctness?Supported queries are validated against Prisma across PostgreSQL and SQLite, with benchmark reports covering Prisma v6, v7, v8, Drizzle, and generated SQL.View Testing Methodology

How It Optimizes Prisma

Bypass Prisma's read execution path while keeping Prisma's API and types

  1. Intercept Prisma Queries

    Extension catches read operations (findMany, findFirst, findUnique, count, aggregate, groupBy) before they execute

    generated/sql, excerpt
    query: {
      $allModels: {
        async $allOperations({ model, operation, args, query }) {
          if (!ACCELERATED_METHODS.has(operation)) {
            return query(args)
          }
          return executeAccelerated(model, operation, args, query)
        },
      },
    }
  2. Generate Optimized SQL

    Convert Prisma queries into fast, parameterized SQL with optimized JOINs

    SQL for PostgreSQL
    SELECT user_t.id, user_t.email, user_t.status,
      COALESCE((
        SELECT COALESCE(json_agg(json_build_object(
          'id', posts_0.id,
          'title', posts_0.title,
          'authorId', posts_0."authorId"
        )), '[]'::json)
        FROM "public"."Post" posts_0
        WHERE posts_0."authorId" = user_t.id
      ), '[]'::json) AS posts
    FROM "public"."User" user_t
    WHERE user_t.status = $1
    
    -- params ["ACTIVE"]
  3. Execute Directly

    Run queries through postgres.js or better-sqlite3, bypassing Prisma read overhead

    generated/sql, excerpt
    if (DIALECT === 'postgres') {
      await client.unsafe(sql, normalizedParams).forEach((row) => {
        results.push(row)
      })
      return results
    }
    
    const stmt = getOrPrepareStatement(client, sql)
    return stmt.all(...normalizedParams)
  4. Return Compatible Results

    Results match Prisma's expected shape. Types, IntelliSense, and existing query code remain unchanged

    users.ts
    const users = await prisma.user.findMany({
      where: { status: 'ACTIVE' },
      include: { posts: true },
    })
    
    users[0].email
    users[0].posts[0].title
    
    // Same inferred types as Prisma Client.
    // Decimal, BigInt and DateTime values can
    // differ; the parity map lists each case.
Verified SecureHow do we prevent SQL injection?Multi-layer validation with parameter binding, schema-based field checks, and the current security test suiteView Security Details

Why Choose This Prisma Extension?

Get raw SQL execution speed for reads while keeping Prisma's developer experience

Instant Prisma Speedup
One-time setup accelerates Prisma reads. No refactoring, no migration, no downtime.
Keep Your Prisma Types
Full TypeScript support maintained. Type inference, autocomplete, and compile-time safety preserved while accelerating reads.
Production-Tested Solution
Current parity tests validate supported reads across recent Prisma versions. Use it for production apps after checking compatibility with your Prisma version and workload.
Multiple Database Support
Optimize Prisma reads on PostgreSQL (including Neon, Supabase) and SQLite.
Pre-Compiled Option
Optional generator creates build-time SQL, reducing overhead to microseconds for your hottest queries.
Serverless Ready (Node Runtimes)
Works in serverless Node runtimes. Edge runtime support depends on runtime constraints and the driver you use.

Beyond faster reads

prisma-sql adds capabilities Prisma Client does not have. Same types, same code style.

$batch

N dashboard queries, one round trip

Combine independent reads into a single PostgreSQL round trip. Typed object result, no Promise.all juggling.

const dash = await prisma.$batch((b) => ({
  users: b.user.count(),
  posts: b.post.findMany({ take: 10 }),
  stats: b.task.aggregate({ _count: true }),
}))

@optimize

Prebake your hottest queries

Annotate a model in schema.prisma. At generate time the exact SQL is baked; matching runtime shapes skip conversion completely.

/// @optimize {
///   "method": "findMany",
///   "query": {
///     "where": { "status": "$status" },
///     "take": "$take"
///   }
/// }
model User { ... }

Cardinality planner

Strategy chosen by data, not by guess

Collected relation stats drive strategy switching between flat joins, correlated subqueries and where-in segmented loading. Large child tables never trigger per-parent scans.

prisma-sql-collect-stats \
  --output dist/planner.generated.js \
  --prisma-client dist/client/index.js

When Prisma Performance Matters Most

Common scenarios where this extension makes a real difference

Analytics & Reporting

Prisma aggregations and groupBy operations benefit significantly from direct SQL execution

  • 5× faster groupBy accelerates reports
  • Faster aggregate queries
  • Real-time metrics with lower latency

High-Traffic APIs

Per-query overhead compounds under load, especially on read-heavy endpoints

  • Lower API response times
  • Handle more requests per instance
  • Reduce infrastructure costs

Serverless Functions

Every millisecond matters in serverless: reduce read latency where it counts

  • Better p95/p99 on reads
  • Lower costs through faster reads
  • Faster reads without refactors

Mobile Backends

Users notice latency: faster reads improve perceived UX immediately

  • Faster feed loading
  • Faster pagination
  • More responsive interactions

Optimize Prisma in 3 Steps

Accelerate Prisma reads in under 60 seconds

  1. Install the Prisma Extension

    Terminal
    # PostgreSQL
    npm install prisma-sql postgres
    
    # SQLite
    npm install prisma-sql better-sqlite3
  2. Add Extension to Prisma Client

    schema.prisma
    generator client {
      provider = "prisma-client"
    }
    
    generator sql {
      provider = "prisma-sql-generator"
    }
    Terminal
    npx prisma generate
    db.ts
    import { PrismaClient } from '@prisma/client'
    import { speedExtension, type SpeedClient } from './generated/sql'
    import postgres from 'postgres'
    
    const sql = postgres(process.env.DATABASE_URL!)
    const basePrisma = new PrismaClient()
    
    export const prisma = basePrisma.$extends(
      speedExtension({ postgres: sql }),
    ) as SpeedClient<typeof basePrisma>
  3. Use Prisma Normally

    users.ts
    const users = await prisma.user.findMany({
      where: { status: 'ACTIVE' },
      include: { posts: true },
    })

SQLite: pass speedExtension({ sqlite: db }) with a better-sqlite3 database.Full setup guide

Prisma Performance FAQ

Common questions about optimizing Prisma reads

Why does Prisma have overhead?

Prisma adds overhead because it implements API guarantees like schema-based validation, consistent query behavior, and result shaping. Those layers provide a great developer experience but cost time compared to executing raw SQL directly.

How do I optimize Prisma queries?

Optimize read-heavy Prisma workloads by adding this extension. It executes read operations via direct SQL using postgres.js or better-sqlite3 while keeping Prisma's API and types. Setup is a small initialization change and does not require refactoring your existing queries.

Is Prisma slower than raw SQL?

For many read workloads, yes. There is architectural overhead compared to raw SQL execution. This extension aims to keep Prisma's DX while reducing read latency by executing SQL directly.

Can I speed up Prisma without changing existing queries?

Yes. Add the extension once during Prisma Client initialization and keep your existing Prisma query code unchanged. Read operations run faster while your Prisma API, types, and schema remain the same.

Does this work in production?

Yes, it is designed for production use on supported read paths. Always verify compatibility with your Prisma version and run your own regression tests before rollout.

What causes slower Prisma aggregations?

Aggregations and groupBy often amplify fixed overhead (query processing and result shaping) and can involve larger intermediate result sets. This extension optimizes those reads by generating SQL directly, which typically reduces latency on aggregation-heavy endpoints.

Ready to Speed Up Prisma?

Check the current benchmark table for supported PostgreSQL and SQLite reads