Can an SQL Query Return Zero Columns? Exploring the Limits of SQL Syntax and Relational Theory
Is It Possible to Run a SELECT Query That Returns No Columns?
In SQL, querying an empty dataset is trivial: a simple WHERE 1 = 0 or LIMIT 0 yields zero rows. But what if your goal is the opposite axis—returning zero columns?
Whether driven by academic curiosity, dynamic query generation, or testing edge cases, developers often wonder if modern SQL engines like Snowflake, PostgreSQL, or DuckDB allow a projection with no attributes. Let’s dive into why this happens, how various database engines handle it, and the deep-rooted architectural reasons behind the limitation.
The Core Problem: Testing the "Zero Column" Hypothesis
Consider a simple table in a cloud data warehouse like Snowflake:
CREATE OR REPLACE TABLE t1(col1 INT, col2 TEXT) AS
SELECT 1, 'A';
If you want zero columns, intuitive attempts usually look like this:
Attempt 1: Leaving the SELECT List Blank
SELECT
FROM t1;
-- SQL compilation error: syntax error unexpected 'FROM'.
Attempt 2: Dynamic Column Exclusion (Snowflake / DuckDB)
Modern dialects feature powerful dynamic column exclusions, such as EXCLUDE or regex matching:
-- Using pattern matching to filter out all columns
SELECT * ILIKE 'non_existent_column'
FROM t1;
-- SQL compilation error: SELECT with no columns
-- Explicitly excluding every column
SELECT * EXCLUDE (col1, col2)
FROM t1;
-- SQL compilation error: SELECT with no columns
In every attempt, the database engine halts query execution at parse/compilation time with an explicit refusal: SELECT with no columns.
Why SQL Forbids Zero-Column Results
The inability to return an empty column list is not an accidental oversight; it is hardcoded into the relational database paradigm at three critical layers:
1. The ANSI SQL Grammar Specification
Under the ISO/IEC 9075 SQL standard, the BNF grammar for a query specification is defined as:
<query specification> ::=
SELECT [ <set quantifier> ] <select list> <table expression>
<select list> ::=
<asterisk> | <select sublist> [ { <comma> <select sublist> }... ]
By definition, a <select list> requires either an asterisk (*) or a comma-separated sequence of at least one expression. There is no syntactic path in ANSI SQL where a <select list> evaluates to an empty production rule.
2. The Relational Model vs. SQL (TABLE_DEE and TABLE_DUM)
In formal relational algebra (described by E.F. Codd and later expanded by C.J. Date and Hugh Darwen), a relation of degree zero (meaning zero attributes) is theoretically valid:
- TABLE_DUM: A relation with 0 attributes and 0 tuples (represents boolean
FALSE). - TABLE_DEE: A relation with 0 attributes and 1 tuple (represents boolean
TRUE).
While relational theory accommodates degree-zero relations, the SQL standard intentionally departed from this purity. SQL relations are defined as bags of tuples with ordered, named columns where the degree must be an integer $N \ge 1$.
3. Database Wire Protocols and Client Drivers
At the operational level, client libraries and network protocols (such as PostgreSQL Frontend/Backend protocol, MySQL client-server protocol, or JDBC/ODBC drivers) structure response packets into two distinct phases:
- Row Description / Metadata: Specifies the number of fields (
field_count), column types, and aliases. - Row Data: Delivers the tuple payloads matching that layout.
Drivers rely on a positive integer column count to parse result buffers. Emitting an empty schema packet breaks expectations in standard result-set interfaces across languages (e.g., Python's DB-API, JDBC ResultSetMetaData).
The Closest Workarounds Across Dialects
While an authentic 0-column result set is impossible in standard SQL, several engines provide quirky adjacent behaviors:
1. PostgreSQL Empty Composite Types
PostgreSQL allows constructing empty row literals:
SELECT ();
While the record itself has zero fields, PostgreSQL still wraps this inside one metadata column of type record.
2. Void Functions in PostgreSQL
SELECT pg_sleep(0);
Even when a function returns void, the engine still projects a single column named pg_sleep with the pseudo-type void.
3. Snowflake Metadata-Only Output
If your goal is verifying row presence without transferring actual column payloads over the wire, projecting a trivial constant or zero-byte value is the standard production solution:
SELECT 1
FROM t1;
This reduces column payload overhead to a minimum while satisfying compiler rules.
Summary
You cannot return a SQL query with zero columns. SQL engines, relational standard grammars, and client-server wire protocols are strictly architected around the rule that every tabular result must have a degree of at least 1. If an exclusion pattern removes all columns, modern compilers like Snowflake and DuckDB will proactively raise an error rather than allowing a headless result set.