---
title: "SELECT"
sidebarTitle: "SELECT"
description: "Projection, filtering, ordering, and composing queries out of other queries."
icon: table-columns
keywords: ["select", "where", "order by", "limit", "cte", "union", "distinct"]
---
Enterprise [SQL runs on a deployment.](/sql)
```sql
[WITH cte AS (...)]
SELECT [ALL | DISTINCT] select_expr [, ...]
FROM from_item [[JOIN ...] ON ...]
[WHERE predicate]
[GROUP BY expr [, ...]] [HAVING predicate]
[WINDOW name AS (...)]
[ORDER BY expr [ASC | DESC] [NULLS FIRST | LAST] [, ...]]
[LIMIT n] [OFFSET n]
```
## Projection {#projection}
```sql SQL
SELECT id, title, identifier FROM documents
```
```python Python icon="python"
tbl.search().select(["id", "title", "identifier"]).to_arrow()
```
```typescript TypeScript icon="square-js"
await tbl.query().select(["id", "title", "identifier"]).toArrow();
```
Projection pushes into the scan in all three -- an unselected column is never
read off storage. On a table of images or video that is the difference between a
fast query and a slow one, so **prefer naming columns over `SELECT *`**.
Row metadata is available as [pseudo-columns](/sql/data-types#pseudo-columns)
(`_rowid`, `_rowaddr`, `_rowoffset`) but only when named, so they stay out of
`SELECT *`.
`DISTINCT` deduplicates the projected rows.
## Filtering {#filtering}
```sql SQL
SELECT * FROM documents
WHERE identifier > 0 AND identifier < 1000000
```
```python Python icon="python"
tbl.search().where("identifier > 0 AND identifier < 1000000").to_arrow()
```
```typescript TypeScript icon="square-js"
await tbl.query().where("identifier > 0 AND identifier < 1000000").toArrow();
```
The SDK `where` takes the same expression language, so a predicate moves between
them unchanged. A predicate on an indexed column becomes a `ScalarIndexQuery`
rather than a scan -- confirm with [`explain_plan`](/search/explain). See
[Filtering](/search/filtering) for the operators and their pushdown behaviour.
## Ordering and limiting {#order-limit}
```sql
SELECT id, title FROM documents
ORDER BY created_at DESC NULLS LAST
LIMIT 100 OFFSET 200
```
`ORDER BY` on a large table is a sort over the whole result, so pair it with a
`LIMIT` unless you genuinely need every row ordered.
## Set operations {#set-operations}
`UNION`, `UNION ALL`, `INTERSECT` and `EXCEPT` combine whole queries:
```sql
SELECT id FROM documents WHERE category = 'film'
UNION ALL
SELECT id FROM archived_documents WHERE category = 'film'
```
`UNION` deduplicates and `UNION ALL` does not, which makes `UNION ALL` the
cheaper one when you already know the inputs are disjoint.
## Common table expressions {#ctes}
```sql
WITH recent AS (
SELECT * FROM documents WHERE created_at > now() - INTERVAL '7 days'
)
SELECT category, count(*) FROM recent GROUP BY category
```
A CTE is scoped to the statement. One you want to keep is a
[view](/management/view); one whose rows you want stored is a
[materialized view](/management/materialized-view).
## Subqueries {#subqueries}
Subqueries are allowed anywhere an expression or a relation is -- in `FROM`, in
`WHERE` with `IN` or `EXISTS`, and as a scalar in the select list:
```sql
SELECT title
FROM documents
WHERE category_id IN (SELECT id FROM categories WHERE active)
```
## Namespaced tables {#namespaces}
A table inside a [namespace](/management/namespace) is written as a dotted path:
```sql
SELECT * FROM prod.events.clicks
```
## Reading the plan {#plans}
`EXPLAIN` and `EXPLAIN ANALYZE` work as DataFusion defines them, and produce the
same operators the SDK methods do:
```sql
EXPLAIN ANALYZE SELECT * FROM documents WHERE identifier < 1000
```
See [Explain](/search/explain) and [Explain Analyze](/search/explain).