Supabase数据更新 #

一、基础更新 #

1.1 使用SQL更新 #

sql
-- 更新单条记录
UPDATE users 
SET name = 'John Updated' 
WHERE id = 1;

-- 更新多个字段
UPDATE users 
SET 
    name = 'John Doe',
    email = 'john.doe@example.com',
    updated_at = NOW()
WHERE id = 1;

-- 更新并返回
UPDATE users 
SET name = 'John Updated' 
WHERE id = 1
RETURNING *;

1.2 使用客户端更新 #

typescript
// 更新单条记录
const { data, error } = await supabase
  .from('users')
  .update({ name: 'John Updated' })
  .eq('id', 1)
  .select()

// 更新多个字段
const { data, error } = await supabase
  .from('users')
  .update({
    name: 'John Doe',
    email: 'john.doe@example.com',
    updated_at: new Date().toISOString()
  })
  .eq('id', 1)
  .select()

1.3 更新注意事项 #

text
重要提示
├── update()必须配合过滤条件使用
├── 默认更新所有匹配的记录
├── 建议使用.eq('id', xxx)限制范围
└── 使用.select()返回更新后的数据

二、条件更新 #

2.1 各种过滤条件 #

typescript
// 按ID更新
const { data } = await supabase
  .from('products')
  .update({ price: 99.99 })
  .eq('id', 1)

// 按多个条件更新
const { data } = await supabase
  .from('products')
  .update({ status: 'sold' })
  .eq('category', 'electronics')
  .lt('stock', 1)

// 更新特定用户的数据
const { data } = await supabase
  .from('posts')
  .update({ published: true })
  .eq('author_id', userId)
  .eq('id', postId)

2.2 SQL条件更新 #

sql
-- 多条件更新
UPDATE products
SET status = 'out_of_stock'
WHERE category = 'electronics' AND stock = 0;

-- 使用子查询更新
UPDATE products
SET featured = true
WHERE id IN (
    SELECT product_id 
    FROM order_items 
    GROUP BY product_id 
    HAVING COUNT(*) > 100
);

-- 使用CASE更新
UPDATE products
SET price = CASE
    WHEN category = 'electronics' THEN price * 0.9
    WHEN category = 'books' THEN price * 0.8
    ELSE price
END;

三、批量更新 #

3.1 客户端批量更新 #

typescript
// 更新所有匹配的记录
const { data, error } = await supabase
  .from('products')
  .update({ on_sale: true })
  .eq('category', 'electronics')

// 批量更新特定记录
const { data, error } = await supabase
  .from('products')
  .update({ status: 'archived' })
  .in('id', [1, 2, 3, 4, 5])

3.2 SQL批量更新 #

sql
-- 批量更新
UPDATE products
SET discount = 0.1
WHERE category IN ('electronics', 'books');

-- 从另一个表更新
UPDATE products p
SET stock = i.new_stock
FROM inventory i
WHERE p.id = i.product_id;

四、增量更新 #

4.1 数值增减 #

typescript
// 使用RPC进行增量更新
const { data, error } = await supabase.rpc('increment_view_count', {
  post_id: 1
})
sql
-- 创建增量函数
CREATE OR REPLACE FUNCTION increment_view_count(post_id BIGINT)
RETURNS VOID AS $$
BEGIN
    UPDATE posts 
    SET view_count = view_count + 1 
    WHERE id = post_id;
END;
$$ LANGUAGE plpgsql;

-- 创建通用增量函数
CREATE OR REPLACE FUNCTION increment_column(
    table_name TEXT,
    column_name TEXT,
    row_id BIGINT,
    increment_value NUMERIC DEFAULT 1
)
RETURNS VOID AS $$
BEGIN
    EXECUTE format(
        'UPDATE %I SET %I = %I + $1 WHERE id = $2',
        table_name, column_name, column_name
    )
    USING increment_value, row_id;
END;
$$ LANGUAGE plpgsql;

4.2 SQL增量更新 #

sql
-- 增加数值
UPDATE products 
SET stock = stock + 10 
WHERE id = 1;

-- 减少数值
UPDATE products 
SET stock = stock - 1 
WHERE id = 1 AND stock > 0;

-- 返回更新后的值
UPDATE products 
SET view_count = view_count + 1 
WHERE id = 1
RETURNING view_count;

五、JSON更新 #

5.1 更新整个JSON字段 #

typescript
// 替换整个JSON
const { data, error } = await supabase
  .from('products')
  .update({
    metadata: {
      color: 'red',
      size: 'large',
      weight: 2.5
    }
  })
  .eq('id', 1)

5.2 更新JSON部分字段 #

sql
-- 更新JSON中的特定字段
UPDATE products
SET metadata = jsonb_set(metadata, '{color}', '"blue"')
WHERE id = 1;

-- 添加新字段
UPDATE products
SET metadata = metadata || '{"brand": "Apple"}'::jsonb
WHERE id = 1;

-- 删除字段
UPDATE products
SET metadata = metadata - 'weight'
WHERE id = 1;

-- 更新嵌套字段
UPDATE products
SET metadata = jsonb_set(metadata, '{specs,ram}', '32'::jsonb)
WHERE id = 1;

5.3 使用RPC更新JSON #

sql
-- 创建JSON更新函数
CREATE OR REPLACE FUNCTION update_product_metadata(
    product_id BIGINT,
    new_metadata JSONB
)
RETURNS VOID AS $$
BEGIN
    UPDATE products
    SET metadata = metadata || new_metadata
    WHERE id = product_id;
END;
$$ LANGUAGE plpgsql;
typescript
// 调用函数更新JSON
const { error } = await supabase.rpc('update_product_metadata', {
  product_id: 1,
  new_metadata: { color: 'green', featured: true }
})

六、数组更新 #

6.1 更新整个数组 #

typescript
// 替换整个数组
const { data, error } = await supabase
  .from('posts')
  .update({
    tags: ['javascript', 'typescript', 'react']
  })
  .eq('id', 1)

6.2 SQL数组操作 #

sql
-- 添加元素
UPDATE posts
SET tags = array_append(tags, 'new-tag')
WHERE id = 1;

-- 删除元素
UPDATE posts
SET tags = array_remove(tags, 'old-tag')
WHERE id = 1;

-- 替换元素
UPDATE posts
SET tags = array_replace(tags, 'old-tag', 'new-tag')
WHERE id = 1;

-- 连接数组
UPDATE posts
SET tags = tags || ARRAY['tag1', 'tag2']
WHERE id = 1;

七、条件表达式更新 #

7.1 CASE表达式 #

sql
-- 条件更新
UPDATE products
SET status = CASE
    WHEN stock = 0 THEN 'out_of_stock'
    WHEN stock < 10 THEN 'low_stock'
    ELSE 'in_stock'
END;

-- 带条件的价格调整
UPDATE products
SET price = CASE
    WHEN category = 'electronics' THEN price * 0.9
    WHEN category = 'books' THEN price * 0.8
    WHEN category = 'clothing' THEN price * 0.7
    ELSE price
END;

7.2 COALESCE更新 #

sql
-- 使用COALESCE处理NULL
UPDATE users
SET 
    name = COALESCE(name, 'Anonymous'),
    avatar_url = COALESCE(avatar_url, '/default-avatar.png');

-- 使用NULLIF
UPDATE products
SET discount = NULLIF(discount, 0);

八、更新时间戳 #

8.1 自动更新时间戳 #

sql
-- 创建触发器函数
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器
CREATE TRIGGER update_users_updated_at
    BEFORE UPDATE ON users
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

8.2 手动更新时间戳 #

typescript
// 更新时设置时间戳
const { data, error } = await supabase
  .from('users')
  .update({
    name: 'John Updated',
    updated_at: new Date().toISOString()
  })
  .eq('id', 1)

九、软删除 #

9.1 实现软删除 #

typescript
// 软删除
const { data, error } = await supabase
  .from('users')
  .update({ 
    deleted_at: new Date().toISOString(),
    status: 'deleted'
  })
  .eq('id', 1)

9.2 恢复软删除 #

typescript
// 恢复
const { data, error } = await supabase
  .from('users')
  .update({ 
    deleted_at: null,
    status: 'active'
  })
  .eq('id', 1)

9.3 查询时排除软删除 #

typescript
// 使用视图
const { data } = await supabase
  .from('active_users')  -- 视图
  .select('*')

// 或在查询中过滤
const { data } = await supabase
  .from('users')
  .select('*')
  .is('deleted_at', null)

十、并发控制 #

10.1 乐观锁 #

sql
-- 添加版本字段
ALTER TABLE products ADD COLUMN version INTEGER DEFAULT 1;

-- 更新时检查版本
UPDATE products
SET 
    price = 99.99,
    version = version + 1
WHERE id = 1 AND version = 5;

-- 如果返回0行,说明版本不匹配

10.2 客户端乐观锁 #

typescript
async function updateWithVersion(
  table: string,
  id: number,
  updates: any,
  expectedVersion: number
) {
  const { data, error } = await supabase
    .from(table)
    .update({
      ...updates,
      version: expectedVersion + 1
    })
    .eq('id', id)
    .eq('version', expectedVersion)
    .select()

  if (!data?.length) {
    throw new Error('Version conflict - data may have been modified')
  }

  return data[0]
}

十一、错误处理 #

11.1 常见错误 #

typescript
const { data, error } = await supabase
  .from('products')
  .update({ price: -10 })
  .eq('id', 1)

if (error) {
  switch (error.code) {
    case '23514':
      console.error('违反检查约束')
      break
    case '23505':
      console.error('唯一约束违反')
      break
    case '23503':
      console.error('外键约束违反')
      break
    case '42501':
      console.error('权限不足')
      break
    case 'PGRST116':
      console.error('未找到匹配记录')
      break
    default:
      console.error('更新失败:', error.message)
  }
}

十二、实战示例 #

12.1 更新用户资料 #

typescript
async function updateProfile(
  userId: string,
  updates: { name?: string; bio?: string; avatar_url?: string }
) {
  const { data, error } = await supabase
    .from('profiles')
    .update({
      ...updates,
      updated_at: new Date().toISOString()
    })
    .eq('id', userId)
    .select()
    .single()

  if (error) throw error
  return data
}

12.2 更新产品库存 #

typescript
async function updateStock(productId: number, quantity: number) {
  // 使用事务确保原子性
  const { data, error } = await supabase.rpc('update_product_stock', {
    product_id: productId,
    quantity_change: quantity
  })

  if (error) throw error
  return data
}
sql
CREATE OR REPLACE FUNCTION update_product_stock(
    product_id BIGINT,
    quantity_change INTEGER
)
RETURNS JSON AS $$
DECLARE
    new_stock INTEGER;
BEGIN
    UPDATE products
    SET stock = stock + quantity_change
    WHERE id = product_id
    RETURNING stock INTO new_stock;
    
    IF new_stock < 0 THEN
        RAISE EXCEPTION 'Insufficient stock';
    END IF;
    
    RETURN json_build_object(
        'product_id', product_id,
        'new_stock', new_stock
    );
END;
$$ LANGUAGE plpgsql;

12.3 批量更新状态 #

typescript
async function batchUpdateStatus(
  productIds: number[],
  newStatus: string
) {
  const { data, error } = await supabase
    .from('products')
    .update({ 
      status: newStatus,
      updated_at: new Date().toISOString()
    })
    .in('id', productIds)
    .select()

  if (error) throw error
  return data
}

十三、总结 #

更新操作要点:

操作 方法
基础更新 update({…}).eq(‘id’, x)
批量更新 update({…}).in(‘id’, […])
条件更新 update({…}).eq(…).gt(…)
JSON更新 jsonb_set(), || 操作符
数组更新 array_append(), array_remove()
增量更新 RPC函数

下一步,让我们学习数据删除!

最后更新:2026-03-28