System.data.Sqlite:Return 受影响的行数,无论是否使用 Query 或 NonQuery

System.data.Sqlite: Return number of affected rows no matter if Query or NonQuery is used

我正在使用 System.Data.SQLite ADO.NET SQLite 提供程序和以下 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 方法,但我需要一个通用包装器,其中 returns 受影响的行数,同时对它执行的查询不可知(例如 Invoke-SQLCmd 会做的)

这可能吗?

谢谢!

回答前的几点评论:

  • System.data.Sqlite 支持为一个命令执行多个 SQL 语句,只要 CommandText 的每个有效语句都由分号 (;) 分隔即可。这意味着可以混合使用查询和 DML 语句(即 INSERT、UPDATE、DELETE)。您不想区分 $Query 中的语句类型这一事实告诉我,您很可能只是盲目地传递语句,因此它可能包含语句的任意组合。仅获取一个值(无论是来自查询还是 DML)似乎太局限了。
  • 使用 DataAdapter 填充数据集只是为了获取计数是低效的。相反,最好只获取一个 DataReader 对象并对返回的行进行计数。这也允许为每个要检索的查询语句单独计数,使用 DataAdapter 对象可以隐藏一些内容。 (也许枚举结果数据集中的所有表可以获得相同的数字,但我不确定这是否总是等价的。)
    • 一个好处是,如果您坚持使用 DataAdapter,它仍然会执行 DML 语句(即使预期结果是查询 returns 行)。数据集不会被改变(填充),但是命令文本中的所有语句仍然会影响数据库的改变,所以下面的解决方案仍然有用。
  • 即使代码有效,我假设打印“{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();