sqlite - System.data.Sqlite:无论使用 Query 还是 NonQuery,都返回受影响的行数
问题描述
我正在使用System.Data.SQLite
SQLite 的 ADO.NET 提供程序和以下 Powershell 代码对 Sqlite3 DB 执行查询(和非查询):
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 会这样做)
那可能吗?
谢谢!
解决方案
答案前的一些评论:
- System.data.Sqlite 支持对一个命令执行多条 SQL 语句,只要 CommandText 中的每个有效语句用分号 (;) 分隔即可。这意味着可能存在查询和 DML 语句(即 INSERT、UPDATE、DELETE)的混合。您不想区分语句类型的事实
$Query
告诉我,您可能只是盲目地传递语句,因此它可能包含语句的任何组合。仅获取一个值(无论是来自查询还是 DML)似乎过于局限。 - 使用 DataAdapter 填充数据集只是为了获取计数是低效的。相反,最好只获取一个 DataReader 对象并计算返回的行数。这还允许检索每个查询语句的单独计数,使用 DataAdapter 对象会掩盖这些计数。(也许枚举结果数据集中的所有表可以获得相同的数字,但我不确定这是否总是等价的。)
- 一件好事是,如果您坚持使用 DataAdapter,它仍然会执行 DML 语句(即使预期结果是返回行的查询)。数据集不会被更改(填充),但命令文本中的所有语句仍然会影响数据库中的更改,因此以下解决方案仍然有用。
- 即使代码有效,我假设打印“{0} Rows returned”的行是为了得到一个简单的计数,但
$ds.Tables[0].Rows
需要是$ds.Tables[0].Rows.Count
.
关于此特定解决方案的注意事项:
- 关键是调用任一 sqlite SQL 函数
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();
推荐阅读
- ios - RingCentral:构建失败任务失败,退出代码 65:迦太基安装
- c# - C# 如何使用我的 MessageBoxButtons/Icons 修复此错误错误
- java - Java中的内存映射大文件
- javascript - 选定行的每一列的总和如何?
- spring-cloud-feign - OpenFeign 多部分表单和@FeignClient
- java - 字符串“槽提取”
- laravel - Laravel 最近的活动页面
- visual-studio - 从一个站点到另一个站点的 API 调用 - 相同的 IIS 服务器
- python - lambda 函数有什么用?
- docker - 如何在 docker 容器中提供docfx?