如何在MySQL上正确使用Laravel和SQL Server?

时间:2016-01-05 22:28:54

标签: php sql-server laravel laravel-5.1

我正在尝试使用我的Laravel项目将SQL Server用于我的数据库。我能够将SQL Server与Laravel 5.2连接起来。但是,当我尝试将数据播种到表中时,我收到此错误

[Illuminate\Database\QueryException] SQLSTATE[23000]: [Microsoft][SQL Server Native Client 11.0][SQL Server]Cann ot insert explicit value for identity column in table 'surveys' when IDENTITY_INSERT is set to OFF. (SQL: insert into [surveys] ([id], [name]) values (10, 'Some Text'))
  

注意:我正在尝试提供可能导致问题的身份值。

在研究SQL错误时,我了解到我需要执行以下查询。

播种前我需要执行

SET IDENTITY_INSERT surveys ON;

播种后我需要执行

SET IDENTITY_INSERT surveys OFF;

但我不知道如何使用Laravel

执行这些命令

如何在没有此问题的情况下为标识列提供值时播种?

已更新 这是我的播种机

<?php

use Illuminate\Database\Seeder;

class FinalSurveyTS extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {   
        $myTable = 'surveys';

        DB::statement('SET IDENTITY_INSERT ' . $myTable . ' ON');


        DB::table($myTable)->truncate();


        DB::table($myTable)->insert([
            'id' => 10,
            'name' => 'some name',
        ]);

        DB::statement('SET IDENTITY_INSERT ' . $myTable . ' OFF');
    }
}

5 个答案:

答案 0 :(得分:3)

这是我找到的。

Laravel不支持交易中的原始声明。

任何原始语句都将单独执行,并与数据库建立单独的连接。

执行时的方法

#This script will update remote address rules with the specified IP scope(s)
$displayname = read-host "Enter firewall rule display name"
$rules = Get-NetFirewallRule -DisplayName $displayname | Where-Object {$_.Direction -eq "Inbound" -and ($_ | Get-NetFirewallAddressFilter).RemoteAddress -ne "LocalSubnet" }
$ips = @()
do {
 $answer = (Read-Host "Please enter IP address")
 if ($answer -ne '') {$ips += $answer}
}
until ($answer -eq '')

foreach ($rule in $rules)
    {

    #Set-NetFirewallRule -DisplayName $r.DisplayName -RemoteAddress $ips
    write-host $rule.Displayname $ips

}

...

Laravel将处理3种不同的数据库连接。这意味着第二个语句永远不会知道第一个或第三个事务。在这种情况下,第一行不会影响到什么。

解决这个问题“直到Laravel添加对事务的原始语句的支持”是将insert语句创建为原始查询,如此

DB::statement('SET IDENTITY_INSERT surveys ON;');
DB::table(...)->insert(...);DB::table(...)->insert(...);DB::table(...)->insert(...);
DB::statement('SET IDENTITY_INSERT surveys ON;');

我希望这篇文章可以帮助别人。

答案 1 :(得分:2)

对于任何通过Google找到它的人-新的正确答案是像这样使用unprepared()

DB::beginTransaction();
DB::unprepared('SET IDENTITY_INSERT test ON');
DB::table('test')->insert(['id' => 1, 'name' => 'example']);
DB::unprepared('SET IDENTITY_INSERT test OFF');
DB::commit();

如在此问题线程上所讨论的:https://github.com/laravel/framework/issues/27778

答案 2 :(得分:1)

为了执行这些命令,您可以将其作为原始

执行
DB::statement('SET IDENTITY_INSERT surveys ON;');

然后

DB::statement('SET IDENTITY_INSERT surveys OFF;');

DB :: statement适用于不返回数据的命令

修改 当播种机像这样执行时:

DB::statement(...);
DB::table(...)->insert(...);
DB::statement(...);

似乎由于某种原因未保留IDENTITY_INSERT变量的状态,因此使其工作的方法是将该代码包装在事务中。

try {
    DB::beginTransaction();
    DB::statement(...);
    DB::table(...)->insert(...);
    DB::statement(...);
    DB::commit();
} catch(Exception $e) {
    DB::rollback();
}

答案 3 :(得分:0)

  

任何原始语句都将通过与数据库的单独连接单独执行。

因此,您应该遵循我进行连接的方式; 这是示例代码;

    DB::transaction(function () use ($reader, $dbDriver, $tableName) {
      if ($dbDriver == 'pgsql') DB::statement('ALTER TABLE ' . $tableName . ' DISABLE TRIGGER ALL');
      elseif ($dbDriver == 'sqlsrv')
        {
          $pdo = DB::connection()->getPdo();
          $pdo->setAttribute(PDO::SQLSRV_ATTR_DIRECT_QUERY, true);
          DB::statement('SET IDENTITY_INSERT ' . $tableName . ' ON');
        }

      foreach ($reader as $row) DB::table($tableName)->insert([$row]);

      if ($dbDriver == 'pgsql') DB::statement('ALTER TABLE ' . $tableName . ' ENABLE TRIGGER ALL');
      elseif ($dbDriver == 'sqlsrv')
        {
          DB::statement('SET IDENTITY_INSERT ' . $tableName . ' OFF');
          $pdo->setAttribute(PDO::SQLSRV_ATTR_DIRECT_QUERY, false);
        }
    });

答案 4 :(得分:0)

我最终创建了以下方法来将IDENTITY_INSERT控制为SQL表播种过程的ON / OFF:

function setIdentityInsert($table, $onStatus = true)
{
    $status = $onStatus ? 'ON' : 'OFF';

    $this->sqlConnection->unprepared("SET IDENTITY_INSERT $table $status");
}

function insertTableData($table, $data)
{
    return $this->sqlConnection->table($table)->insert($data);
}

function seedTable($table, $hasAutoIncrementPrimaryKey = false, $data = [])
{
    if ($hasAutoIncrementPrimaryKey) {
        $this->setIdentityInsert($table);
        $response = $this->insertTableData($table, $data);
        $this->setIdentityInsert($table, false);

        return $response;
    }
    else {
        return $this->insertTableData($table, $data);
    }
}

注意:通常,该表需要具有自动递增的主键才能将“身份插入”设置为ON,这就是为什么要使用$hasAutoIncrementPrimaryKey标志的原因。否则,播种可能会引发以下错误:

SQLSTATE[HY000]: General error: 544 Cannot insert explicit value for
identity column in table 'test_table_name' when IDENTITY_INSERT is set to
OFF. [544] (severity 16) [(null)]

希望这会有所帮助!