> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Dataset Aliases

Status: Experimental.

A dataset alias is a name your SQL uses for a dataset, like `eth` in `eth.blocks`. You choose the dataset and version
behind it each time you send the query, in the `amp-alias` header. The SQL never names a version, so the same query can
read version 1.0.0 today and 2.0.0 tomorrow, or both at once, by changing only the header.

## Prerequisites

* A client that can send request headers, like [ADBC](/querying/connecting#python-with-adbc) or your own
  [Arrow Flight client](/querying/arrow-flight#write-a-client). `ampsql` and the SDKs have no option for it.
* To run the examples, the [example datasets](/querying/connecting#example-datasets), and the Python client from
  [Arrow Flight](/querying/arrow-flight#write-a-client), saved as `flight_query.py`.
* [`ampctl`](/clis/ampctl), to create the dataset that [compare two versions](/querying/dataset-aliases#compare-two-versions) reads.

## Bind a name to a dataset

Write the SQL with a plain name where the dataset's reference would go, and send the `amp-alias` header with it, mapping
the name to a reference:

:::terminal
```bash
python flight_query.py 'SELECT COUNT(*) AS blocks, MIN(block_num) AS first_block FROM chain.blocks' \
  'amp-alias: {"chain": "acme/eth_mainnet@1.0.0"}'
```

```text
[mainnet 24000000-24000099] 1 rows
 blocks  first_block
    100     24000000
[mainnet 24000000-24000099] complete
```
:::

Change only the header, and the same SQL reads another dataset with the same tables:

:::terminal
```bash
python flight_query.py 'SELECT COUNT(*) AS blocks, MIN(block_num) AS first_block FROM chain.blocks' \
  'amp-alias: {"chain": "acme/base_mainnet@1.0.0"}'
```

```text
[base 39397512-39398111] 1 rows
 blocks  first_block
    600     39397512
[base 39397512-39398111] complete
```
:::

Without the header, `chain` would mean a dataset named `chain` in the `_` namespace, and the query fails with
`table 'amp.chain.blocks' not found`.

With ADBC, pass the header as an option:

```python
options = {"adbc.flight.sql.rpc.call_header.amp-alias": '{"chain": "acme/eth_mainnet@1.0.0"}'}

with flight_sql.connect("grpc://localhost:1602", db_kwargs=options, autocommit=True) as conn:
    …
```

## The `amp-alias` header

The header's value is a JSON object that maps each alias to a dataset reference, like the `dependencies` of a
[derived dataset](/datasets/derived-datasets):

```json
{ "eth": "acme/eth_mainnet@1.0.0", "base": "acme/base_mainnet@1.0.0" }
```

| Part      | Rule                                                                                   |
| --------- | -------------------------------------------------------------------------------------- |
| Alias     | Starts with a letter, and has only letters, digits, and underscores. 63 bytes at most. |
| Reference | `namespace/name@revision`. Unlike in SQL, the revision is required.                    |
| Revision  | A version like `1.0.0`, or a manifest hash (all 64 characters). Not `latest` or `dev`. |
| Entries   | 32 at most. When an alias appears twice, the last one counts.                          |

* SQL lowercases the names you don't quote (see [names](/querying/sql#names)), so use lowercase aliases. An alias like
  `ETH` only matches `"ETH".blocks`, in quotes.
* An alias takes precedence over a dataset with the same name in the `_` namespace.
* Every alias must name a dataset that exists, even one the query doesn't use.
* Aliases last for one request. Send the header with each query.

### Revisions

Each revision suits a different use:

| Revision                | Reads                                                  | Use it to                                                  |
| ----------------------- | ------------------------------------------------------ | ---------------------------------------------------------- |
| A version, like `1.0.0` | That version                                           | Pin the version an app reads, until you change its header. |
| A manifest hash         | That exact manifest, wherever version tags point later | Pin a query to a dataset's exact definition.               |

An alias can't follow `latest` or `dev`, which move as versions are released: they fail with
``malformed `amp-alias` header``. To read a dataset's latest version, name it in the SQL without a version, like
`"acme/eth_mainnet".blocks`, and leave it out of the header.

See [versioning](/datasets/versioning) for how datasets get their versions, and
[`ampctl dataset versions`](/clis/ampctl/dataset) to list them.

## Compare two versions

Bind two aliases to two versions of a dataset to read them side by side, like before you switch an app to the newer one.
This example creates `acme/gas_usage`, a [derived dataset](/datasets/derived-datasets) with a `block_gas` table, which
has each block's gas use as a percentage of its limit. Version 1.0.0 gives it with two decimals, and version 2.0.0 drops
the decimals.

::::steps
### Write the two manifests

Each manifest defines the table in SQL, over the `eth_mainnet` example dataset, and declares its columns. Only the
`gas_used_pct` column differs:

```json [gas_usage_1.json]
{
  "kind": "manifest",
  "dependencies": {
    "eth": "acme/eth_mainnet@1.0.0"
  },
  "tables": {
    "block_gas": {
      "input": {
        "sql": "SELECT block_num, gas_used, ROUND(gas_used * 100.0 / gas_limit, 2) AS gas_used_pct FROM eth.blocks"
      },
      "schema": {
        "arrow": {
          "fields": [
            { "name": "_block_num", "type": "UInt64", "nullable": false },
            { "name": "_ts", "type": { "Timestamp": ["Nanosecond", "+00:00"] }, "nullable": false },
            { "name": "block_num", "type": "UInt64", "nullable": false },
            { "name": "gas_used", "type": "UInt64", "nullable": false },
            { "name": "gas_used_pct", "type": "Float64", "nullable": false }
          ]
        }
      }
    }
  }
}
```

```json [gas_usage_2.json]
{
  "kind": "manifest",
  "dependencies": {
    "eth": "acme/eth_mainnet@1.0.0"
  },
  "tables": {
    "block_gas": {
      "input": {
        "sql": "SELECT block_num, gas_used, CAST(gas_used * 100 / gas_limit AS INT) AS gas_used_pct FROM eth.blocks"
      },
      "schema": {
        "arrow": {
          "fields": [
            { "name": "_block_num", "type": "UInt64", "nullable": false },
            { "name": "_ts", "type": { "Timestamp": ["Nanosecond", "+00:00"] }, "nullable": false },
            { "name": "block_num", "type": "UInt64", "nullable": false },
            { "name": "gas_used", "type": "UInt64", "nullable": false },
            { "name": "gas_used_pct", "type": "Int32", "nullable": false }
          ]
        }
      }
    }
  }
}
```

### Register and deploy both versions

Each deployment stops at the example dataset's last block:

```bash
ampctl dataset register acme/gas_usage ./gas_usage_1.json --tag 1.0.0
ampctl dataset deploy acme/gas_usage@1.0.0 --end-block 24000099

ampctl dataset register acme/gas_usage ./gas_usage_2.json --tag 2.0.0
ampctl dataset deploy acme/gas_usage@2.0.0 --end-block 24000099
```

### Compare them

After a few seconds, both versions have all 100 blocks. Bind `old` to one and `new` to the other:

:::terminal
```bash
python flight_query.py 'SELECT old.block_num, old.gas_used_pct AS old_pct, new.gas_used_pct AS new_pct
  FROM old.block_gas AS old JOIN new.block_gas AS new ON old.block_num = new.block_num
  ORDER BY old.block_num LIMIT 3' \
  'amp-alias: {"old": "acme/gas_usage@1.0.0", "new": "acme/gas_usage@2.0.0"}'
```

```text
[mainnet 24000000-24000099] 3 rows
 block_num  old_pct  new_pct
  24000000    96.43       96
  24000001    46.10       46
  24000002    29.34       29
[mainnet 24000000-24000099] complete
```
:::
::::

## Mix aliases and references

The header only binds the names it lists. The query's other datasets, like those named by their references, resolve as
they would without it:

:::terminal
```bash
python flight_query.py "SELECT 'ethereum' AS chain, COUNT(*) AS blocks FROM eth.blocks
  UNION ALL SELECT 'base', COUNT(*) FROM \"acme/base_mainnet@1.0.0\".blocks
  ORDER BY chain DESC" \
  'amp-alias: {"eth": "acme/eth_mainnet@1.0.0"}'
```

```text
[base 39397512-39398111, mainnet 24000000-24000099] 2 rows
   chain  blocks
ethereum     100
    base     600
[base 39397512-39398111, mainnet 24000000-24000099] complete
```
:::

## When aliases resolve

A query takes [two calls](/querying/arrow-flight#how-a-query-runs). Amp resolves the aliases in the first,
`GetFlightInfo`, and records the manifest each one points to in the query's ticket. The second call, `DoGet`, reads
those manifests, even if a version tag moved in between, and a streaming query keeps reading them for as long as it
runs.

A dataset the header doesn't bind, like `"acme/eth_mainnet".blocks`, resolves again in `DoGet`, so a version
released between the two calls changes what it reads. Alias it to prevent that.

Aliases don't grant access to datasets. When a gateway limits the [namespaces](/querying/arrow-flight#namespace-access)
a request reads, an alias to a dataset outside them fails as if the dataset didn't exist.

## Errors

| Message contains                             | Status                | Cause                                                                                                                             | Fix                                                                         |
| -------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| ``malformed `amp-alias` header``             | `FAILED_PRECONDITION` | The value isn't a JSON object, an alias isn't a valid name, or a reference has no revision, an invalid one, or `latest` or `dev`. | Check it against the [header's rules](/querying/dataset-aliases#the-amp-alias-header).               |
| `carries more than 32 entries`               | `FAILED_PRECONDITION` | The header has more than 32 aliases.                                                                                              | Send only the aliases the query uses.                                       |
| `` `eth` does not name a known dataset: … `` | `INVALID_ARGUMENT`    | The alias names a dataset or version that doesn't exist, or that the request can't read.                                          | Check the reference with [`ampctl dataset versions`](/clis/ampctl/dataset). |
| `failed to look up`                          | `UNAVAILABLE`         | Amp couldn't reach its dataset registry.                                                                                          | Retry.                                                                      |

For example, an alias to a version that doesn't exist:

```text
failed to resolve the query's dataset references | Caused by: `eth` does not name a known dataset: acme/eth_mainnet@9.9.9
```
