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();
我正在使用 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();