# Select (projections)

> Reshape what a query returns: one column, an anonymous row, or a `class` you declared. The projection becomes the SQL SELECT list, so the columns you did not ask for are never read — which is the point of it. `Select` must be the LAST clause of the chain; only `Where`, `OrderBy` and `Take` may precede it, and only `Distinct()` may follow.

<!-- id: query-select · area: query · stability: stable · html: https://osysharp.com/reference/query/select/ -->

## Summary        {#summary}
`Select` says **which columns you want**, and in what shape:

```osy title="three shapes of projection" test app=query-select
class OrderRow {
  public string Code;
  public decimal Total;
}

entity Order {
  [Required, MaxLength(40)] string Code;
  [MaxLength(60)] string Region;
  decimal Total;
  bool Cancelled;
}

List<string> Codes() {
  return Order.Select(o => o.Code).ToList();               // one column → a List<string>
}

List<OrderRow> Rows() {
  return Order.Where(o => !o.Cancelled)
              .Select(o => new OrderRow { Code = o.Code, Total = o.Total })
              .ToList();                                                       // → a class you declared
}
```

The projection becomes the SQL `SELECT` list, so the columns you did not name are **never read** — no wide rows over
the wire, and no entity to materialise.

## Signature      {#signature}
```osy syntax
<Query>.Select(o => o.Column)              // a scalar     → List<string> / List<decimal> / …
<Query>.Select(o => o.Total * 2)           // an expression scalar
<Query>.Select(o => new T { A = o.X, … })  // a `class` you declared → List<T>
<Query>.Select(o => new { o.X, o.Y })      // anonymous — usable on the spot, cannot be returned
```

## Description    {#description}

### It must be the last clause   {#last-clause}
A `Select` **ends** the chain. The compiler enforces a narrow window around it, and the restrictions are not
arbitrary — each one is a thing SQL cannot do once the rows have been reshaped:

**Before a `Select`, only:** `Where` · `OrderBy` / `OrderByDescending` · `Take`.

- **`ThenBy` may not precede a `Select`.** Sort the projected result instead.
- **`Skip` may not precede a `Select`** (though `Take` may). To page a projection: project into a `class` and page
  that, or page the entity query and project after.
- **`Include` may not precede a `Select`** — and it would be meaningless if it could: [`Include`](https://osysharp.com/reference/query/include/)
  pre-loads *related entity rows*, and a projection does not return entity rows at all. Just select what you want.

**After a `Select`, only:** `ToList()` (a no-op — the projection already materialises) and `Distinct()`.

There are no terminals over a projection: no `First()`, no `Count()`, no `Sum()`. Do the aggregate over the entity
query instead ([Sum / Average / Min / Max / Count](https://osysharp.com/reference/query/aggregates/)), or group it ([GroupBy (and HAVING)](https://osysharp.com/reference/query/group-by/)).

### The one exception: `Distinct().Count()`   {#distinct-count}
The one composition allowed after a projection, because it is a single SQL expression — and it answers a question you
genuinely cannot get another way:

```osy title="how many DIFFERENT regions have we sold into" test app=query-select
int RegionsSoldInto() {
  return Order.Select(o => o.Region).Distinct().Count();   // → COUNT(DISTINCT region)
}
```

`Distinct()` over a scalar projection then `Count()` becomes `COUNT(DISTINCT col)`. It must be the last clause, and
the projection must be a scalar. See [Distinct](https://osysharp.com/reference/query/distinct/).

### Which shape to reach for   {#shapes}
- **A scalar** (`o => o.Code`) when you want a list of values — ids to pass on, codes to render, amounts to sum in
  code. You get a real `List<T>`.
- **A `class`** when you want rows with names, especially across a function boundary. This is the workhorse: declare
  the shape, project into it, return it. It is a plain data shape ([class methods](https://osysharp.com/reference/class/methods/)) — no entity, no tracking, no
  lazy loading, nothing to surprise you later. A class projection can also back a reactive `live var` in a component —
  a live list of the shape you render, refreshing on commit ([The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/)).
- **Anonymous** (`o => new { o.Code, o.Total }`) only where the result is consumed on the spot. It has no name, so it
  cannot be a return type.

### It does not change what security allows   {#security}
A projection narrows the **columns**, never the **rows**. The [read rules](https://osysharp.com/reference/security/entity-security/) are compiled into
the same statement, so `Select` cannot be used to see a row you were not granted — and a
[field mask](https://osysharp.com/reference/security/entity-security/) (`deny read PasswordHash when …`) still applies to the column you projected.
Projecting is a performance and shape decision, not an access one.

## Examples       {#examples}
Projecting a computed value, and a narrow row for a list view — the common case, and the one that keeps a grid fast:

```osy title="a list view fetches four columns, not the whole row" test app=query-select
class OrderCard {
  public string Code;
  public decimal Total;
  public decimal WithVat;
  public bool Big;
}

List<OrderCard> Cards(decimal bigFrom) {
  return Order.Where(o => !o.Cancelled)
              .OrderByDescending(o => o.Total)
              .Take(50)
              .Select(o => new OrderCard {
                Code    = o.Code,
                Total   = o.Total,
                WithVat = o.Total * 1.2m,        // computed in the database
                Big     = o.Total > bigFrom })   // a captured local works, like any parameter
              .ToList();
}
```

### Where did my row come in the order?   {#indexed}
`Select((s, i) => …)` — C#'s index-aware projection — works over stored rows once the chain names an order: `i` is
the row's 0-based position in that order, computed by the database. The partitioned forms (per-group ranks, the
previous row's value) are the [window functions](https://osysharp.com/reference/query/window/).

## See also       {#see-also}
- [GroupBy (and HAVING)](https://osysharp.com/reference/query/group-by/) — a projection with a `GroupBy` in front of it: one row per group
- [Distinct](https://osysharp.com/reference/query/distinct/) — `Distinct()`, and `Distinct().Count()`
- [Include (pre-loading relations)](https://osysharp.com/reference/query/include/) — the opposite need: keep the entity rows, but pre-load their relations
- [class methods](https://osysharp.com/reference/class/methods/) — the `class` a projection targets
- [Querying data](https://osysharp.com/reference/query/index/) — where a projection sits in the chain
