skills/flutter-drift/references/queries.md

272 lines
5.8 KiB
Markdown

---
title: Queries
description: Write type-safe Drift SELECT queries from Flutter code
---
## Scope Rule
From widgets, repositories, or services, call query builders on the database instance:
```dart
final todos = await database.select(database.todoItems).get();
```
Use bare `select(todoItems)` only inside `AppDatabase` or a `DatabaseAccessor` where Drift exposes the table and query methods directly.
## Basic Select
Get all rows:
```dart
final allTodos = await database.select(database.todoItems).get();
```
Watch all rows:
```dart
final allTodosStream = database.select(database.todoItems).watch();
```
## Where Clauses
Use cascades on the statement, then call `get()` or `watch()` on the completed statement.
```dart
final completedTodos = await (database.select(database.todoItems)
..where((t) => t.isCompleted.equals(true)))
.get();
```
Combine expressions with `&` and `|`:
```dart
final importantTodos = await (database.select(database.todoItems)
..where(
(t) => t.isCompleted.equals(false) & t.priority.isBiggerThanValue(2),
))
.get();
```
Common filters:
- `equals(value)`
- `isBiggerThanValue(value)`
- `isSmallerThanValue(value)`
- `isBiggerOrEqualValue(value)`
- `isSmallerOrEqualValue(value)`
- `like(pattern)`
- `contains(value)`
- `isNull()`
- `isNotNull()`
## Limit, Offset, And Order
```dart
final page = await (database.select(database.todoItems)
..orderBy([
(t) => OrderingTerm(
expression: t.createdAt,
mode: OrderingMode.desc,
),
])
..limit(20, offset: 40))
.get();
```
## Single Row
Use `getSingle()` only when the query must return exactly one row.
```dart
final todo = await (database.select(database.todoItems)
..where((t) => t.id.equals(id)))
.getSingle();
```
Use `getSingleOrNull()` for optional rows:
```dart
final todo = await (database.select(database.todoItems)
..where((t) => t.id.equals(id)))
.getSingleOrNull();
```
Watch a single row:
```dart
final todoStream = (database.select(database.todoItems)
..where((t) => t.id.equals(id)))
.watchSingleOrNull();
```
## Mapping
Map after the statement has been configured:
```dart
final titles = await (database.select(database.todoItems)
..where((t) => t.title.contains(searchTerm)))
.map((row) => row.title)
.get();
```
## Joins
Use joins when UI needs related rows in one query.
```dart
class TodoWithCategory {
TodoWithCategory({required this.todo, required this.category});
final TodoItem todo;
final TodoCategory? category;
}
final query = database.select(database.todoItems).join([
leftOuterJoin(
database.categories,
database.categories.id.equalsExp(database.todoItems.category),
),
]);
final rows = await query
.map(
(row) => TodoWithCategory(
todo: row.readTable(database.todoItems),
category: row.readTableOrNull(database.categories),
),
)
.get();
```
Use `useColumns: false` when a joined table is needed only for filtering:
```dart
final joined = database.select(database.todoItems).join([
innerJoin(
database.categories,
database.categories.id.equalsExp(database.todoItems.category),
useColumns: false,
),
]);
joined.where(database.categories.name.equals('Work'));
final workTodos = await joined
.map((row) => row.readTable(database.todoItems))
.get();
```
## Self Joins
Alias tables when joining a table to itself:
```dart
final otherTodos = alias(database.todoItems, 'other');
final query = database.select(otherTodos).join([
innerJoin(
database.todoItems,
database.todoItems.category.equalsExp(otherTodos.category),
useColumns: false,
),
]);
query.where(database.todoItems.title.contains('important'));
final relatedTodos = await query.map((row) => row.readTable(otherTodos)).get();
```
## Aggregations
Use `selectOnly` when selecting computed columns.
```dart
final countExpression = database.todoItems.id.count();
final count = await (database.selectOnly(database.todoItems)
..addColumns([countExpression]))
.map((row) => row.read(countExpression) ?? 0)
.getSingle();
```
Group by a related table:
```dart
final countExpression = database.todoItems.id.count();
final query = database.select(database.categories).join([
leftOuterJoin(
database.todoItems,
database.todoItems.category.equalsExp(database.categories.id),
useColumns: false,
),
])
..addColumns([countExpression])
..groupBy([database.categories.id]);
final counts = await query
.map(
(row) => (
category: row.readTable(database.categories),
count: row.read(countExpression) ?? 0,
),
)
.get();
```
## Subqueries
Use subqueries for reusable query fragments:
```dart
final recent = Subquery(
database.select(database.todoItems)
..orderBy([(t) => OrderingTerm.desc(t.createdAt)])
..limit(10),
'recent_todos',
);
final rows = await database.select(recent).get();
```
## Custom Columns
Add computed expressions with `addColumns`:
```dart
final isImportant = database.todoItems.priority.isBiggerThanValue(2);
final rows = await database.select(database.todoItems)
.addColumns([isImportant])
.map(
(row) => (
todo: row.readTable(database.todoItems),
important: row.read(isImportant) ?? false,
),
)
.get();
```
## Exists
```dart
final hasTodosExpression = existsQuery(database.select(database.todoItems));
final hasTodos = await (database.selectOnly(database.todoItems)
..addColumns([hasTodosExpression]))
.map((row) => row.read(hasTodosExpression) ?? false)
.getSingle();
```
## Union
```dart
final completed = database.select(database.todoItems)
..where((t) => t.isCompleted.equals(true));
final highPriority = database.select(database.todoItems)
..where((t) => t.priority.isBiggerThanValue(2));
final rows = await completed.unionAll(highPriority).get();
```