- Error number
- 8623
- Severity
- 16 (user-correctable error)
- State
- Documented by Microsoft as 1 (plan too complex) or 2 (out of memory); not reproduced here (see the state table below)
- Product
- Microsoft SQL Server Database Engine
- Versions
- All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
- Category
- Query Processing & Performance
- Last reviewed
- 24 October 2026
On this page
What it means
Error 8623 means the Query Optimizer could not finish building a plan for one statement, because it was too complex to optimise in time or because compiling it needed more memory than was available.
Before a statement runs, the optimiser explores possible plans. For a statement with a huge number of tables, partitions, expressions or literal values, that search can exceed the optimiser's internal limits. Microsoft documents two states: state 1, the optimisation timed out because the plan is too complex, and state 2, it ran out of memory. Either way the statement never starts, so nothing was changed.
The error is about one statement, not the server: other queries keep working. The usual culprits are generated SQL (an ORM or report tool that writes an IN list with many thousands of values or hundreds of OR conditions), views stacked on views that expand into a very large join, and queries over tables with thousands of partitions. Microsoft notes that moving part of the query into a subquery, function or common table expression does not help, because the compiler combines them into one query again.
| State | Meaning |
|---|---|
| 1 | The query timed out during optimisation because the plan was too complex (Microsoft Learn). |
| 2 | The query ran out of memory during optimisation (Microsoft Learn). |
Common causes
- An
INlist with many thousands of literal values, which Microsoft names as a cause of 8623 and 8632. - Generated SQL with hundreds of joins,
ORbranches orUNIONparts, often from views nested inside views. - A query that touches a very large number of tables or partitions.
- Compile memory pressure on the server, so a large compilation cannot get the memory it needs (state 2).
Diagnose it
Read-only These queries only read metadata and dynamic management views. They do not change data, permissions or configuration, so they are safe to run first.
Are compilations waiting for optimiser memory?
SELECT pool_id, name, max_count, active_count, waiter_count, threshold
FROM sys.dm_exec_query_optimizer_memory_gateways; The gateways limit how many large compilations run at once. A waiter_count above 0 on the Medium or Big gateway means compilations are queuing for memory, which fits state 2.
How often does the optimiser time out?
SELECT counter, occurrence, value
FROM sys.dm_exec_query_optimizer_info
WHERE counter IN (N'optimizations', N'timeout', N'memory limit exceeded'); Counters since the instance started. timeout counts optimisations that stopped early (most still return a plan); a rising memory limit exceeded points at memory limits during compilation.
How much memory does the optimiser use now?
SELECT type, name, pages_kb
FROM sys.dm_os_memory_clerks
WHERE type IN (N'MEMORYCLERK_SQLOPTIMIZER', N'MEMORYCLERK_SQLQUERYCOMPILE')
ORDER BY pages_kb DESC; Large values while the error occurs show memory being spent on compiling. Compare with other memory clerks if the server is under general memory pressure.
Which tables have the most partitions?
SELECT TOP (10) OBJECT_SCHEMA_NAME(p.object_id) AS schema_name,
OBJECT_NAME(p.object_id) AS table_name, COUNT(*) AS partitions
FROM sys.partitions AS p
WHERE p.index_id IN (0, 1)
GROUP BY p.object_id
ORDER BY partitions DESC; Thousands of partitions on tables in the failing query increase optimisation work. Filter on the partitioning column so the optimiser can eliminate partitions.
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
Put long value lists in a table
Client or network sideMicrosoft's workaround for very long IN lists: load the values into a table (a temporary table or a table-valued parameter) and join to it, or use IN (SELECT ...).
CREATE TABLE #wanted (customer_id INT PRIMARY KEY);
INSERT INTO #wanted (customer_id) VALUES (1), (2), (3); -- or bulk load, or a TVP
SELECT o.order_id, o.total
FROM dbo.Orders AS o
JOIN #wanted AS w ON w.customer_id = o.customer_id;
Split the statement along its largest dimension
Client or network sideRemove anything the result does not need, then materialise an intermediate result in a temporary table and finish the query from there. Moving parts into a CTE, view or inline function is not enough, because they are expanded into the same statement.
SELECT o.customer_id, SUM(o.total) AS revenue
INTO #customer_revenue
FROM dbo.Orders AS o
WHERE o.order_date >= '2026-01-01'
GROUP BY o.customer_id;
SELECT c.name, r.revenue
FROM #customer_revenue AS r
JOIN dbo.Customers AS c ON c.customer_id = r.customer_id;
Give the optimiser less to explore
Client or network sideMicrosoft suggests forcing a plan shape earlier, for example with OPTION (FORCE ORDER), which keeps the join order as written. Test the resulting plan: a forced order can be slower.
SELECT ...
FROM dbo.BigView AS v
JOIN dbo.Customers AS c ON c.customer_id = v.customer_id
OPTION (FORCE ORDER);
Examples
A generated query with an enormous IN list
Shown as documented by Microsoft (IN list remarks and MSSQLSERVER_8623); not reproduced here. On SQL Server 2025 a query with 30,000 integer literals in an IN list compiled and ran without error, so whether a given list fails depends on its size, data types and the server. The state and raised level were not observed, so no message header is shown.
-- Generated by an application: many thousands of literals
SELECT order_id, total
FROM dbo.Orders
WHERE customer_id IN (1, 2, 3, 4, 5, /* ... tens of thousands more ... */ 99999);
The query processor ran out of internal resources and could not produce a query plan. This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions. Please simplify the query. If you believe you have received this message in error, contact Customer Support Services for more information.
How to prevent it
Do not let applications build statements whose size grows with the data: pass value lists as table-valued parameters or stage them in a table, and limit how many filters a report tool can combine. Avoid deep stacks of views over views; each layer is expanded into the final statement.
For heavily partitioned tables, make sure queries filter on the partitioning column so most partitions are eliminated before optimisation becomes expensive.
Common questions
Is 8623 a sign that the server is broken?
No. It concerns one statement that is too large or complex to optimise. The rest of the workload continues. If it happens for simple queries, check for memory pressure, which matches state 2.
Will moving part of the query into a CTE or view help?
No. CTEs, views and inline functions are expanded into the same statement before optimisation. Use a temporary table to split the work into separate statements.
How many values can an IN list have?
There is no fixed number. Microsoft warns that many thousands of values can cause 8623 or 8632, depending on the query and resources. Use a table or table-valued parameter for long lists.
Learn more on CodeWithSQL
Official documentation
- MSSQLSERVER_8623 (Microsoft Learn)
- IN (Transact-SQL): remarks on very long lists
- sys.dm_exec_query_optimizer_memory_gateways
- Query hints (Transact-SQL)
Last technically reviewed 24 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.