# Data manipulation

The Python SDK provides methods for selecting, creating, updating, and deleting records in SurrealDB.

The Python SDK provides dedicated methods for common CRUD operations on records and tables. These methods offer a structured alternative to writing raw SurrealQL, with built-in parameter handling and type safety.

This page covers how to target tables and records, and how to select, create, insert, update, merge, patch, and delete data.

## API references

<table>
	<thead>
		<tr>
			<th scope="col">Method</th>
			<th scope="col">Description</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#select"><code>db.select(record)</code></a></td>
			<td scope="row" data-label="Description">Selects all records from a table, or a specific record</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#create"><code>db.create(record, data?)</code></a></td>
			<td scope="row" data-label="Description">Creates a new record with an optional data payload</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#insert"><code>db.insert(table, data)</code></a></td>
			<td scope="row" data-label="Description">Inserts one or multiple records into a table</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#insert-relation"><code>db.insert_relation(table, data)</code></a></td>
			<td scope="row" data-label="Description">Inserts one or multiple relation records</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#update"><code>db.update(record, data?)</code></a></td>
			<td scope="row" data-label="Description">Replaces the entire content of a record or all records in a table</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#upsert"><code>db.upsert(record, data?)</code></a></td>
			<td scope="row" data-label="Description">Creates a record if it does not exist, or replaces it entirely</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#merge"><code>db.merge(record, data?)</code></a></td>
			<td scope="row" data-label="Description">Merges data into an existing record, preserving unmentioned fields</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#patch"><code>db.patch(record, data?)</code></a></td>
			<td scope="row" data-label="Description">Applies JSON Patch operations to a record or all records in a table</td>
		</tr>
		<tr>
			<td scope="row" data-label="Method"><a href="/docs/reference/python/api/core/surreal.md#delete"><code>db.delete(record)</code></a></td>
			<td scope="row" data-label="Description">Deletes a specific record or all records from a table</td>
		</tr>
	</tbody>
</table>

## Targeting tables and records

Most data manipulation methods accept a `record` parameter that determines the scope of the operation. You can pass a table name as a string to target all records in that table, or a [RecordID](/docs/reference/python/api/values/record-id.md) to target a specific record.

```python
from surrealdb import RecordID

db.select("users")

db.select(RecordID("users", "tobie"))
```

When a string is passed, the operation applies to the entire table. When a `RecordID` is passed, it applies to the single record identified by that ID. See the [RecordID reference](/docs/reference/python/api/values/record-id.md) for more on constructing record identifiers.

## Selecting records

The `.select()` method retrieves records from the database. Pass a table name to get all records, or a `RecordID` to get a single record.

**Synchronous**

		```python
		from surrealdb import Surreal, RecordID

		with Surreal("ws://localhost:8000") as db:
		    db.use("surrealdb", "docs")
		    db.signin({"username": "root", "password": "secret"})

		    all_users = db.select("users")

		    tobie = db.select(RecordID("users", "tobie"))
		```

**Asynchronous**

		```python
		from surrealdb import AsyncSurreal, RecordID

		async with AsyncSurreal("ws://localhost:8000") as db:
		    await db.use("surrealdb", "docs")
		    await db.signin({"username": "root", "password": "secret"})

		    all_users = await db.select("users")

		    tobie = await db.select(RecordID("users", "tobie"))
		```

When selecting a table, the method returns a list. When selecting a specific record, it returns a single value or `None` if the record does not exist.

## Creating records

The `.create()` method creates a new record. Pass a table name to generate a random ID, or a `RecordID` to specify the ID explicitly.

**Synchronous**

		```python
		from surrealdb import RecordID

		user = db.create("users", {
		    "name": "Alice",
		    "email": "alice@example.com",
		    "age": 30,
		})

		specific = db.create(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "age": 35,
		})
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		user = await db.create("users", {
		    "name": "Alice",
		    "email": "alice@example.com",
		    "age": 30,
		})

		specific = await db.create(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "age": 35,
		})
		```

The method returns the created record, including any server-generated fields such as the `id`.

## Inserting records

The `.insert()` method inserts one or more records into a table. This is useful for bulk operations where you need to add multiple records at once.

**Synchronous**

		```python
		db.insert("users", {"name": "Alice", "age": 30})

		db.insert("users", [
		    {"name": "Bob", "age": 25},
		    {"name": "Charlie", "age": 40},
		])
		```

**Asynchronous**

		```python
		await db.insert("users", {"name": "Alice", "age": 30})

		await db.insert("users", [
		    {"name": "Bob", "age": 25},
		    {"name": "Charlie", "age": 40},
		])
		```

The `.insert_relation()` method works the same way but is designed for creating graph edges between records. Each record must include `in` and `out` fields pointing to the connected records.

**Synchronous**

		```python
		from surrealdb import RecordID

		db.insert_relation("likes", {
		    "in": RecordID("users", "tobie"),
		    "out": RecordID("posts", 123),
		})

		db.insert_relation("likes", [
		    {"in": RecordID("users", "tobie"), "out": RecordID("posts", 123)},
		    {"in": RecordID("users", "jaime"), "out": RecordID("posts", 456)},
		])
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		await db.insert_relation("likes", {
		    "in": RecordID("users", "tobie"),
		    "out": RecordID("posts", 123),
		})

		await db.insert_relation("likes", [
		    {"in": RecordID("users", "tobie"), "out": RecordID("posts", 123)},
		    {"in": RecordID("users", "jaime"), "out": RecordID("posts", 456)},
		])
		```

## Replacing records

The `.update()` method replaces the entire content of a record or all records in a table. Any fields not included in the new data are removed.

**Synchronous**

		```python
		from surrealdb import RecordID

		db.update(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "active": True,
		})

		db.update("users", {"active": False})
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		await db.update(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "active": True,
		})

		await db.update("users", {"active": False})
		```

> [!NOTE]
> Because `.update()` performs a full replacement, omitted fields are deleted from the record. Use `.merge()` if you want to preserve existing fields.

## Upserting records

The `.upsert()` method creates a record if it does not already exist, or replaces it entirely if it does. This combines the behaviour of `.create()` and `.update()` in a single operation.

**Synchronous**

		```python
		from surrealdb import RecordID

		db.upsert(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "active": True,
		})
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		await db.upsert(RecordID("users", "tobie"), {
		    "name": "Tobie",
		    "email": "tobie@surrealdb.com",
		    "active": True,
		})
		```

## Merging data

The `.merge()` method deep-merges the provided data into the existing record, preserving any fields that are not mentioned in the merge payload. This is useful for partial updates.

**Synchronous**

		```python
		from surrealdb import RecordID

		db.merge(RecordID("users", "tobie"), {
		    "settings": {"active": True},
		})

		db.merge("users", {
		    "updated_at": "2026-02-25T12:00:00Z",
		})
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		await db.merge(RecordID("users", "tobie"), {
		    "settings": {"active": True},
		})

		await db.merge("users", {
		    "updated_at": "2026-02-25T12:00:00Z",
		})
		```

In the example above, only the `settings.active` field is changed on the specific record. All other fields on the record remain untouched.

## Applying patches

The `.patch()` method applies [JSON Patch (RFC 6902)](https://jsonpatch.com/) operations to a record or all records in a table. Each operation is a dictionary with `op`, `path`, and optionally `value` fields.

**Synchronous**

		```python
		from surrealdb import RecordID

		db.patch(RecordID("users", "tobie"), [
		    {"op": "replace", "path": "/settings/active", "value": False},
		    {"op": "add", "path": "/tags", "value": ["developer", "admin"]},
		    {"op": "remove", "path": "/temp"},
		])
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		await db.patch(RecordID("users", "tobie"), [
		    {"op": "replace", "path": "/settings/active", "value": False},
		    {"op": "add", "path": "/tags", "value": ["developer", "admin"]},
		    {"op": "remove", "path": "/temp"},
		])
		```

Supported operations include `add`, `remove`, `replace`, `move`, `copy`, and `test`.

## Deleting records

The `.delete()` method removes a specific record or all records from a table. The method returns the deleted record(s).

**Synchronous**

		```python
		from surrealdb import RecordID

		deleted = db.delete(RecordID("users", "tobie"))

		all_deleted = db.delete("users")
		```

**Asynchronous**

		```python
		from surrealdb import RecordID

		deleted = await db.delete(RecordID("users", "tobie"))

		all_deleted = await db.delete("users")
		```

## Learn more

- [Surreal API reference](/docs/reference/python/api/core/surreal.md) for complete method signatures and parameters
- [Executing queries](/docs/reference/python/concepts/executing-queries.md) for running SurrealQL statements directly
- [Value types](/docs/reference/python/api/types.md) for the types used by data manipulation methods
- [RecordID reference](/docs/reference/python/api/values/record-id.md) for constructing record identifiers
- [SurrealQL CRUD statements](/docs/reference/query-language/statements/overview.md) for the underlying query language
