Skip to content

Add best practices for Spacetime SQL #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 25, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions docs/sql/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,143 @@ SELECT * FROM "Order"
SELECT * FROM "Balance$"
```

## Best Practices for Performance and Scalability

When designing your schema or crafting your queries,
consider the following best practices to ensure optimal performance:

- **Add Primary Key and/or Unique Constraints:**
Constrain columns whose values are guaranteed to be distinct as either unique or primary keys.
The query planner can further optimize joins if it knows the join values to be unique.

- **Index Filtered Columns:**
Index columns frequently used in a `WHERE` clause.
Indexes reduce the number of rows scanned by the query engine.

- **Index Join Columns:**
Index columns whose values are frequently used as join keys.
These are columns that are used in the `ON` condition of a `JOIN`.

Again, this reduces the number of rows that must be scanned to answer a query.
It is also critical for the performance of subscription updates --
so much so that it is a compiler-enforced requirement,
as mentioned in the [subscription](#from) section.

If a column that has already been constrained as unique or a primary key,
it is not necessary to explicitly index it as well,
since these constraints automatically index the column in question.

- **Optimize Join Order:**
Place tables with the most selective filters first in your `FROM` clause.
This minimizes intermediate result sizes and improves query efficiency.

### Example

Take the following query that was used in a previous example:
```sql
-- Find all customers who ordered a particular product and when they ordered it
SELECT customer.first_name, customer.last_name, o.date
FROM Customers customer
JOIN Orders o ON customer.id = o.customer_id
JOIN Inventory product ON o.product_id = product.id
WHERE product.name = {product_name}
```

In order to conform with the best practices for optimizing performance and scalability:

- An index should be defined on `Inventory.name` because we are filtering on that column.
- `Inventory.id` and `Customers.id` should be defined as primary keys.
- Additionally non-unique indexes should be defined on `Orders.product_id` and `Orders.customer_id`.
- `Inventory` should appear first in the `FROM` clause because it is the only table mentioned in the `WHERE` clause.
- `Orders` should come next because it joins directly with `Inventory`.
- `Customers` should come next because it joins directly with `Orders`.

:::server-rust
```rust
#[table(
name = Inventory,
index(name = product_name, btree = [name]),
public
)]
struct Inventory {
#[primary_key]
id: u64,
name: String,
..
}

#[table(
name = Customers,
public
)]
struct Customers {
#[primary_key]
id: u64,
first_name: String,
last_name: String,
..
}

#[table(
name = Orders,
public
)]
struct Orders {
#[primary_key]
id: u64,
#[unique]
product_id: u64,
#[unique]
customer_id: u64,
..
}
```
:::
:::server-csharp
```cs
[SpacetimeDB.Table(Name = "Inventory")]
[SpacetimeDB.Index(Name = "product_name", BTree = ["name"])]
public partial struct Inventory
{
[SpacetimeDB.PrimaryKey]
public long id;
public string name;
..
}

[SpacetimeDB.Table(Name = "Customers")]
public partial struct Customers
{
[SpacetimeDB.PrimaryKey]
public long id;
public string first_name;
public string last_name;
..
}

[SpacetimeDB.Table(Name = "Orders")]
public partial struct Orders
{
[SpacetimeDB.PrimaryKey]
public long id;
[SpacetimeDB.Unique]
public long product_id;
[SpacetimeDB.Unique]
public long customer_id;
..
}
```
:::

```sql
-- Find all customers who ordered a particular product and when they ordered it
SELECT c.first_name, c.last_name, o.date
FROM Inventory product
JOIN Orders o ON product.id = o.product_id
JOIN Customers c ON c.id = o.customer_id
WHERE product.name = {product_name};
```

## Appendix

Common production rules that have been used throughout this document.
Expand Down