System.data.Sqlite:无论使用查询还是非查询,都返回受影响的行数

时间:2019-06-14 17:40:16

标签: sqlite powershell ado.net system.data.sqlite

我正在使用System.Data.SQLite SQLite的ADO.NET提供程序和以下Powershell代码对Sqlite3数据库执行查询(和非查询):

Function Invoke-SQLite ($DBFile,$Query) {

    try {
        Add-Type -Path ".\System.Data.SQLite.dll"
    }
    catch {
        write-warning "Unable to load System.Data.SQLite.dll"
        return
    }
    if (!$DBFile) {
        throw "DB Not Found" R
        Sleep 5
        Exit
    }
    $conn = New-Object System.Data.SQLite.SQLiteConnection
    $conn.ConnectionString="Data Source={0}" -f $DBFile
    $conn.Open()
    $cmd = $Conn.CreateCommand()
    $cmd.CommandText = $Query
    #$cmd.CommandTimeout = 10
    $ds = New-Object system.Data.DataSet
    $da = New-Object System.Data.SQLite.SQLiteDataAdapter($cmd)
    [void]$da.fill($ds)
    $cmd.Dispose()
    $conn.Close()

    write-host ("{0} Row(s) returned " -f ($ds.Tables[0].Rows|Measure-Object|Select -ExpandProperty Count)) 

    return $ds.Tables[0]
}

问题是:虽然很容易知道在查询操作中选择了多少行,但是如果该操作是INSERT,DELETE或UPDATE(非查询),则情况并非如此

我知道我可以使用ExecuteNonQuery方法,但是我需要一个通用包装器,该包装器在不影响查询执行的情况下返回受影响的行数(例如,如Invoke-SQLCmd所做的那样)

有可能吗?

谢谢!

1 个答案:

答案 0 :(得分:1)

答案前的一些评论:

  • System.data.Sqlite支持为一个命令执行多个SQL语句,只要CommandText的每个有效语句都以分号(;)分隔即可。这意味着查询和DML语句(即INSERT,UPDATE,DELETE)可能混合在一起。您不想在$Query中的语句类型之间进行区分的事实告诉我,您很可能只是盲目地传递语句,因此它可以包含语句的任何组合。仅获取一个值(无论是来自查询还是DML)似乎太局限了。
  • 仅使用DataAdapter填充数据集以获取计数是无效的。相反,最好只获取一个DataReader对象并计算返回的行。这也允许为每个查询语句检索一个单独的计数,而使用DataAdapter对象会掩盖某些问题。 (也许枚举结果数据集中的所有表都可以得到相同的数字,但是我不确定那总是相等的。)
    • 一件好事是,如果您坚持使用DataAdapter,它将仍然执行DML语句(即使预期结果是返回行的查询)。数据集不会被更改(填充),但是命令文本中的所有语句仍将影响数据库中的更改,因此以下解决方案仍然有用。
  • 即使代码可行,我也认为打印“ {0}返回的行”的行是为了获得简单的计数,但是$ds.Tables[0].Rows必须为$ds.Tables[0].Rows.Count

有关此特定解决方案的说明:

  • 关键是调用sqlite SQL functions changes()total_changes()中的一个。可以使用SQL:SELECT total_changes();来检索它们。我建议在命令之前和之后获取total_changes(),然后减去差值。这样一来,一条命令执行的多条语句就会发生变化。
  • 我不是PowerShell专家,所以我用C#测试了所有内容。由于可能需要进行调整,因此将下面的代码更多地视为伪代码。

代码:

$conn = New-Object System.Data.SQLite.SQLiteConnection
try {
  $conn.ConnectionString="Data Source={0}" -f $DBFile
  $conn.Open()

  $cmdCount = $Conn.CreateCommand()
  $cmd = $Conn.CreateCommand()
  try {
    $cmdCount.CommandText = "SELECT total_changes();"

    $beforeChanges = $cmdcount.ExecuteScalar()

    $cmd.CommandText = $Query

    $ds = New-Object System.Data.DataSet
    $da = New-Object System.Data.SQLite.SQLiteDataAdapter($cmd)

    $rows = 0
    try {
      [void]$da.fill($ds)
      foreach ($tbl in $ds.Tables) {
         $rows += $tbl.Rows.Count;  
      }    
    } catch {}

    $afterChanges = $cmdcount.ExecuteScalar()
    $DMLchanges = $afterChanges - $beforeChanges

    $totalRowAndChanges = $rows + $DMLchanges

    # $ds.Tables[0] may or may not be valid here.
    # If query returned no data, no tables will exist.
  } finally {
    $cmdCount.Dispose()
    $cmd.Dispose()
  }
} finally {
  $conn.Dispose()
}

或者,您可以消除DataAdapter:

    $cmd.CommandText = $Query

    $rdr = $cmd.ExecuteReader()

    $rows = 0
    do {
      while ($rdr.Read()) { 
        $rows++
      }
    } while ($rdr.NextResult())
    $rdr.Close();