You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLite插入时如何递增默认列值?Laravel适配问题问询

解决Laravel updateOrCreate在SQLite中递增列的报错问题

背景与现有实现

你开发了一个异常处理器来跟踪API调用的失败尝试次数,当调用失败时会用Laravel的updateOrCreate方法来更新或创建数据库记录:

try {
    Vmi::createPurchaseOrder($purchaseOrder);
} catch (Exception $e) {
    PurchaseOrder::updateOrCreate(
        ['invoice_id' => $purchaseOrder->invoice_id],
        [
            'error' => $e->getMessage(),
            'attempts' => DB::raw('attempts + 1'),
        ]
    );
}

对应的表结构如下:

  • invoice_id: unsigned int
  • error: text
  • attempts: unsigned int,默认值为0

这段逻辑在MySQL中运行正常,但切换到内存SQLite做测试时,抛出了如下错误:

PDOException: SQLSTATE[HY000]: General error: 1 no such column: attempts

问题根源:SQLite与MySQL的语法差异

问题出在SQLite不支持INSERT语句中引用同表的列。你给出的SQL示例很清楚:

  • 下面的INSERT语句在MySQL中可行,但SQLite会报错,因为插入新记录时attempts列还不存在,无法引用它做加法:
insert into purchase_orders ( invoice_id, error, attempts )
values ( 1, 'Invalid customer ID', attempts + 1 );
  • 而UPDATE语句在SQLite中是正常的,因为此时记录已经存在,attempts列有值可以引用:
update purchase_orders set attempts = attempts + 1 where invoice_id = 1;

Laravel的updateOrCreate方法在执行创建操作时,会把你传入的DB::raw('attempts +1')直接拼到INSERT语句里,这就触发了SQLite的语法限制。

解决方案:拆分逻辑适配SQLite

我们可以把updateOrCreate的逻辑拆分开,分别处理“更新现有记录”和“创建新记录”的场景,避免在INSERT时引用同表列:

方案一:手动判断记录是否存在

try {
    Vmi::createPurchaseOrder($purchaseOrder);
} catch (Exception $e) {
    $record = PurchaseOrder::where('invoice_id', $purchaseOrder->invoice_id)->first();

    if ($record) {
        // 记录存在,执行更新,递增attempts
        $record->update([
            'error' => $e->getMessage(),
            'attempts' => DB::raw('attempts + 1'),
        ]);
    } else {
        // 记录不存在,创建新记录,attempts设为1(第一次失败)
        PurchaseOrder::create([
            'invoice_id' => $purchaseOrder->invoice_id,
            'error' => $e->getMessage(),
            'attempts' => 1,
        ]);
    }
}

方案二:使用firstOrNew结合increment方法

这个方案更简洁,利用Laravel的increment方法自动适配不同数据库:

try {
    Vmi::createPurchaseOrder($purchaseOrder);
} catch (Exception $e) {
    $record = PurchaseOrder::firstOrNew(['invoice_id' => $purchaseOrder->invoice_id]);
    $record->error = $e->getMessage();
    
    if ($record->exists) {
        // 已有记录,直接递增attempts(Laravel会生成正确的UPDATE语句)
        $record->increment('attempts');
    } else {
        // 新记录,设置attempts为1后保存
        $record->attempts = 1;
        $record->save();
    }
}

这两种方案都能同时兼容MySQL和SQLite,解决你遇到的测试报错问题。

内容的提问来源于stack exchange,提问作者Kelly Kiernan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:45:38