Supabase数据插入 #
一、基础插入 #
1.1 使用SQL插入 #
sql
-- 插入单条数据
INSERT INTO users (name, email)
VALUES ('John Doe', 'john@example.com');
-- 插入并返回
INSERT INTO users (name, email)
VALUES ('Jane Doe', 'jane@example.com')
RETURNING *;
-- 插入多条数据
INSERT INTO users (name, email)
VALUES
('User 1', 'user1@example.com'),
('User 2', 'user2@example.com'),
('User 3', 'user3@example.com')
RETURNING *;
1.2 使用客户端插入 #
typescript
// 插入单条数据
const { data, error } = await supabase
.from('users')
.insert({
name: 'John Doe',
email: 'john@example.com'
})
.select()
// data: [{ id: 1, name: 'John Doe', email: 'john@example.com', ... }]
// 插入多条数据
const { data, error } = await supabase
.from('users')
.insert([
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' },
{ name: 'User 3', email: 'user3@example.com' }
])
.select()
1.3 插入选项 #
typescript
// 默认返回插入的数据
const { data, error } = await supabase
.from('users')
.insert({ name: 'John', email: 'john@example.com' })
.select()
// 指定返回列
const { data, error } = await supabase
.from('users')
.insert({ name: 'John', email: 'john@example.com' })
.select('id, name')
// 不返回数据
const { error } = await supabase
.from('users')
.insert({ name: 'John', email: 'john@example.com' })
二、批量插入 #
2.1 SQL批量插入 #
sql
-- 批量插入
INSERT INTO products (name, price, category)
VALUES
('Product 1', 19.99, 'electronics'),
('Product 2', 29.99, 'electronics'),
('Product 3', 39.99, 'clothing'),
('Product 4', 49.99, 'clothing'),
('Product 5', 59.99, 'books');
-- 从查询结果插入
INSERT INTO archived_orders (order_id, customer_id, total)
SELECT id, customer_id, total
FROM orders
WHERE created_at < NOW() - INTERVAL '1 year';
2.2 客户端批量插入 #
typescript
// 批量插入
const products = Array.from({ length: 100 }, (_, i) => ({
name: `Product ${i + 1}`,
price: Math.random() * 100,
category: ['electronics', 'clothing', 'books'][i % 3]
}))
const { data, error } = await supabase
.from('products')
.insert(products)
.select()
// 大批量插入建议分批
async function batchInsert(items: any[], batchSize = 100) {
const results = []
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize)
const { data, error } = await supabase
.from('products')
.insert(batch)
.select()
if (error) throw error
results.push(...data)
}
return results
}
三、Upsert操作 #
3.1 SQL Upsert #
sql
-- ON CONFLICT DO UPDATE
INSERT INTO users (email, name)
VALUES ('john@example.com', 'John Updated')
ON CONFLICT (email)
DO UPDATE SET
name = EXCLUDED.name,
updated_at = NOW();
-- ON CONFLICT DO NOTHING
INSERT INTO users (email, name)
VALUES ('john@example.com', 'John Doe')
ON CONFLICT (email) DO NOTHING;
3.2 客户端Upsert #
typescript
// 基于唯一键upsert
const { data, error } = await supabase
.from('users')
.upsert({
email: 'john@example.com',
name: 'John Updated'
})
.select()
// 指定唯一键
const { data, error } = await supabase
.from('users')
.upsert(
{ email: 'john@example.com', name: 'John' },
{ onConflict: 'email' }
)
.select()
// 批量upsert
const { data, error } = await supabase
.from('products')
.upsert([
{ id: 1, name: 'Updated Product 1', price: 99.99 },
{ id: 2, name: 'Updated Product 2', price: 149.99 }
])
.select()
3.3 Upsert选项 #
typescript
// ignoreDuplicates: 忽略重复,不更新
const { data, error } = await supabase
.from('users')
.upsert(
{ email: 'john@example.com', name: 'John' },
{
onConflict: 'email',
ignoreDuplicates: true
}
)
// 默认行为是更新重复数据
四、插入关联数据 #
4.1 插入带外键的数据 #
typescript
// 先获取用户ID
const { data: user } = await supabase
.from('users')
.select('id')
.eq('email', 'john@example.com')
.single()
// 插入文章
const { data, error } = await supabase
.from('posts')
.insert({
title: 'My First Post',
content: 'Hello World',
author_id: user.id
})
.select()
4.2 使用SQL事务插入关联数据 #
sql
-- 使用CTE插入关联数据
WITH new_user AS (
INSERT INTO users (name, email)
VALUES ('John Doe', 'john@example.com')
RETURNING id
)
INSERT INTO profiles (user_id, bio)
SELECT id, 'New user bio' FROM new_user;
五、插入JSON数据 #
5.1 插入JSONB #
typescript
// 插入JSON数据
const { data, error } = await supabase
.from('products')
.insert({
name: 'Laptop',
price: 999.99,
attributes: {
color: 'silver',
weight: 1.5,
specs: {
cpu: 'M1 Pro',
ram: 16,
storage: 512
}
},
tags: ['electronics', 'computer', 'apple']
})
.select()
5.2 SQL插入JSON #
sql
INSERT INTO products (name, price, attributes, tags)
VALUES (
'Laptop',
999.99,
'{"color": "silver", "specs": {"cpu": "M1 Pro"}}'::jsonb,
ARRAY['electronics', 'computer']
);
六、插入数组数据 #
6.1 插入数组 #
typescript
// 插入数组字段
const { data, error } = await supabase
.from('posts')
.insert({
title: 'Tech Post',
tags: ['javascript', 'typescript', 'react'],
ratings: [5, 4, 5, 4, 5]
})
.select()
6.2 SQL插入数组 #
sql
INSERT INTO posts (title, tags, ratings)
VALUES (
'Tech Post',
ARRAY['javascript', 'typescript', 'react'],
ARRAY[5, 4, 5, 4, 5]
);
-- 或使用字符串语法
INSERT INTO posts (title, tags)
VALUES ('Tech Post', '{javascript,typescript,react}');
七、错误处理 #
7.1 常见错误 #
typescript
const { data, error } = await supabase
.from('users')
.insert({ email: 'john@example.com' })
if (error) {
switch (error.code) {
case '23505':
console.error('数据已存在')
break
case '23503':
console.error('外键约束违反')
break
case '23502':
console.error('必填字段为空')
break
case '42501':
console.error('权限不足')
break
default:
console.error('插入失败:', error.message)
}
}
7.2 错误处理最佳实践 #
typescript
async function insertUser(userData: { name: string; email: string }) {
try {
const { data, error } = await supabase
.from('users')
.insert(userData)
.select()
.single()
if (error) throw error
return { success: true, data }
} catch (error: any) {
console.error('Insert error:', error)
return {
success: false,
error: error.message || 'Unknown error'
}
}
}
// 使用
const result = await insertUser({
name: 'John',
email: 'john@example.com'
})
if (result.success) {
console.log('User created:', result.data)
} else {
console.error('Failed:', result.error)
}
八、性能优化 #
8.1 批量插入优化 #
typescript
// 使用事务批量插入
async function batchInsertWithTransaction(items: any[]) {
const { data, error } = await supabase.rpc('batch_insert_products', {
products: items
})
return { data, error }
}
sql
-- 创建批量插入函数
CREATE OR REPLACE FUNCTION batch_insert_products(products JSONB)
RETURNS SETOF products AS $$
BEGIN
INSERT INTO products (name, price, category)
SELECT
p->>'name',
(p->>'price')::numeric,
p->>'category'
FROM jsonb_array_elements(products) AS p
RETURNING *;
END;
$$ LANGUAGE plpgsql;
8.2 插入性能建议 #
text
性能优化建议
├── 使用批量插入代替循环单条插入
├── 关闭不必要的索引
├── 使用COPY命令导入大量数据
├── 分批处理大数据量
└── 考虑使用服务端函数
九、使用RPC插入 #
9.1 创建插入函数 #
sql
-- 创建插入函数
CREATE OR REPLACE FUNCTION create_user_with_profile(
user_email TEXT,
user_name TEXT,
user_bio TEXT DEFAULT NULL
)
RETURNS JSON AS $$
DECLARE
new_user_id UUID;
result JSON;
BEGIN
-- 插入用户
INSERT INTO auth.users (email, raw_user_meta_data)
VALUES (user_email, jsonb_build_object('name', user_name))
RETURNING id INTO new_user_id;
-- 插入资料
INSERT INTO profiles (id, bio)
VALUES (new_user_id, user_bio);
-- 返回结果
SELECT json_build_object(
'user_id', new_user_id,
'email', user_email,
'name', user_name
) INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
9.2 调用RPC插入 #
typescript
// 调用自定义插入函数
const { data, error } = await supabase.rpc('create_user_with_profile', {
user_email: 'john@example.com',
user_name: 'John Doe',
user_bio: 'Software Developer'
})
console.log('Created user:', data)
十、实战示例 #
10.1 创建博客文章 #
typescript
interface Post {
title: string
content: string
author_id: string
tags: string[]
published?: boolean
}
async function createPost(post: Post) {
const { data, error } = await supabase
.from('posts')
.insert({
title: post.title,
content: post.content,
author_id: post.author_id,
tags: post.tags,
published: post.published ?? false,
slug: post.title.toLowerCase().replace(/\s+/g, '-')
})
.select()
.single()
if (error) throw error
return data
}
10.2 创建订单 #
typescript
interface OrderItem {
product_id: number
quantity: number
price: number
}
async function createOrder(
userId: string,
items: OrderItem[]
) {
// 计算总价
const total = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
// 创建订单
const { data: order, error: orderError } = await supabase
.from('orders')
.insert({
user_id: userId,
total,
status: 'pending'
})
.select()
.single()
if (orderError) throw orderError
// 创建订单项
const orderItems = items.map(item => ({
order_id: order.id,
...item
}))
const { error: itemsError } = await supabase
.from('order_items')
.insert(orderItems)
if (itemsError) throw itemsError
return order
}
十一、总结 #
插入操作要点:
| 操作 | 方法 |
|---|---|
| 单条插入 | insert({…}) |
| 批量插入 | insert([…]) |
| 插入或更新 | upsert({…}) |
| 返回数据 | .select() |
| 错误处理 | 检查error.code |
下一步,让我们学习数据查询!
最后更新:2026-03-28