使用 Npgsql 的多个游标?
multiple cursor using Npgsql?
我习惯了(从 Ada 使用 libpq)
打开游标
获取一些关键值
使用这些键值作为其他语句的绑定参数。
但是我得到了 Npgsql.NpgsqlOperationInProgressException
相反。
我曾经用 sql-server 解决过这个问题,但通过将 'MARS' 添加到连接字符串(多个活动记录集)
解决了这个问题
我可以在这里做类似的事情吗?
这是我正在尝试做的事情:
conn.Open();
// Define a query
NpgsqlCommand cmdGetSelectionIds = new NpgsqlCommand("select distinct(R.SELECTIONID) from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS <> 'REMOVED'", conn);
NpgsqlCommand cmdNumWins = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS = 'WINNER'", conn);
NpgsqlCommand cmdNumPlcs = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'PLACE' " +
"and R.STATUS = 'WINNER'", conn);
NpgsqlCommand cmdNumLosses = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS = 'LOSER'", conn);
NpgsqlDataReader drGetSelectionIds = cmdGetSelectionIds.ExecuteReader();
while (drGetSelectionIds.Read())
{
selid = drGetSelectionIds.GetInt32(0);
cmdNumWins.Parameters.AddWithValue("selid", selid);
using (NpgsqlDataReader drNumWins = cmdNumWins.ExecuteReader())
{
if (drNumWins.Read()) numWins = drNumWins.GetInt32(0);
}
using (NpgsqlDataReader drNumPlcs = cmdNumPlcs.ExecuteReader())
{
if (drNumPlcs.Read()) numPlcs = drNumPlcs.GetInt32(0);
}
using (NpgsqlDataReader drNumLosses = cmdNumLosses.ExecuteReader()) {
if (drNumLosses.Read()) numPlcs = drNumLosses.GetInt32(0);
}
Console.Write("selid : {0} \t num W {1} \t num P {2} num L {3} \t points {4}\n", selid, numWins, numPlcs, numLosses, (3.0*numWins + numPlcs )/(numWins+numLosses));
}
// Close connection
conn.Close();
是的,我可以将第一条语句读入列表并循环遍历它,
但我确实有一些像上面那样构建的遗留代码。
/比约恩
问题是 NpgSql 一次只允许打开一个数据 reader。如果您需要同时针对同一个连接打开多个数据 reader,我认为可能有更好的方法来达到您的最终目标。
所以,免责声明...不要这样做。但是,如果您想执行上述操作,这将是实现它的一种方法:
cmdNumWins.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
cmdNumPlcs.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
cmdNumLosses.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
List<int> idList = new List<int>();
using (NpgsqlDataReader drGetSelectionIds = cmdGetSelectionIds.ExecuteReader())
{
while (drGetSelectionIds.Read())
idList.Add(drGetSelectionIds.GetInt32(0));
drGetSelectionIds.Close();
}
foreach (int selid in idList)
{
cmdNumWins.Parameters[0].Value = selid;
cmdNumPlcs.Parameters[0].Value = selid;
cmdNumLosses.Parameters[0].Value = selid;
numWins = Convert.ToInt32(cmdNumWins.ExecuteScalar());
numPlcs = Convert.ToInt32(cmdNumPlcs.ExecuteScalar());
numLosses = Convert.ToInt32(cmdNumLosses.ExecuteScalar());
Console.Write("selid : {0} \t num W {1} \t num P {2} num L {3} \t points {4}\n", selid,
numWins, numPlcs, numLosses, (3.0 * numWins + numPlcs) / (numWins + numLosses));
}
更好的方法是将这一切作为一个查询来完成:
select
r.selectionid,
count (1) filter (where m.markettype = 'WIN' and r.status = 'WINNER') as win,
count (1) fitler (where m.markettype = 'PLACE' and r.status = 'WINNER') as place,
count (1) filter (where m.markettype = 'WIN' and r.status = 'LOSER') as lose
from
arunners r
join amarkets m on m.marketid = r.marketid
where
r.status != 'REMOVED'
group by
r.selectionid
我可能错过了您逻辑的一些细微差别,但希望您能理解。这应该一口气快速高效地完成您的 C# 逻辑正在做的事情。它对数据库更友好,对于大型数据集应该更快。
如上所述,Npgsql 确实只允许在给定连接上同时打开一个数据 reader(与 MSSQL 的 MARS 功能不同)。但是,您可以做一些类似的事情 by manually using server-side cursors yourself。换句话说,您可以 运行 一个查询并从其游标中获取 X 行,然后根据第一个查询的输出执行第二个查询。您可以随时返回到第一个查询并获取更多 X 行,等等。
换句话说,虽然 Npgsql 不允许同时存在多个 reader,但 PostgreSQL 确实允许多个 query (或光标)同时。
我习惯了(从 Ada 使用 libpq)
打开游标
获取一些关键值
使用这些键值作为其他语句的绑定参数。
但是我得到了 Npgsql.NpgsqlOperationInProgressException 相反。
我曾经用 sql-server 解决过这个问题,但通过将 'MARS' 添加到连接字符串(多个活动记录集)
解决了这个问题我可以在这里做类似的事情吗?
这是我正在尝试做的事情:
conn.Open();
// Define a query
NpgsqlCommand cmdGetSelectionIds = new NpgsqlCommand("select distinct(R.SELECTIONID) from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS <> 'REMOVED'", conn);
NpgsqlCommand cmdNumWins = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS = 'WINNER'", conn);
NpgsqlCommand cmdNumPlcs = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'PLACE' " +
"and R.STATUS = 'WINNER'", conn);
NpgsqlCommand cmdNumLosses = new NpgsqlCommand("select count('a') from ARUNNERS R, AMARKETS M " +
"where true " +
"and M.MARKETID = R.MARKETID " +
"and R.SELECTIONID = @selid " +
"and M.MARKETTYPE = 'WIN' " +
"and R.STATUS = 'LOSER'", conn);
NpgsqlDataReader drGetSelectionIds = cmdGetSelectionIds.ExecuteReader();
while (drGetSelectionIds.Read())
{
selid = drGetSelectionIds.GetInt32(0);
cmdNumWins.Parameters.AddWithValue("selid", selid);
using (NpgsqlDataReader drNumWins = cmdNumWins.ExecuteReader())
{
if (drNumWins.Read()) numWins = drNumWins.GetInt32(0);
}
using (NpgsqlDataReader drNumPlcs = cmdNumPlcs.ExecuteReader())
{
if (drNumPlcs.Read()) numPlcs = drNumPlcs.GetInt32(0);
}
using (NpgsqlDataReader drNumLosses = cmdNumLosses.ExecuteReader()) {
if (drNumLosses.Read()) numPlcs = drNumLosses.GetInt32(0);
}
Console.Write("selid : {0} \t num W {1} \t num P {2} num L {3} \t points {4}\n", selid, numWins, numPlcs, numLosses, (3.0*numWins + numPlcs )/(numWins+numLosses));
}
// Close connection
conn.Close();
是的,我可以将第一条语句读入列表并循环遍历它, 但我确实有一些像上面那样构建的遗留代码。
/比约恩
问题是 NpgSql 一次只允许打开一个数据 reader。如果您需要同时针对同一个连接打开多个数据 reader,我认为可能有更好的方法来达到您的最终目标。
所以,免责声明...不要这样做。但是,如果您想执行上述操作,这将是实现它的一种方法:
cmdNumWins.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
cmdNumPlcs.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
cmdNumLosses.Parameters.Add(new NpgsqlParameter("@selid", NpgsqlDbType.Integer));
List<int> idList = new List<int>();
using (NpgsqlDataReader drGetSelectionIds = cmdGetSelectionIds.ExecuteReader())
{
while (drGetSelectionIds.Read())
idList.Add(drGetSelectionIds.GetInt32(0));
drGetSelectionIds.Close();
}
foreach (int selid in idList)
{
cmdNumWins.Parameters[0].Value = selid;
cmdNumPlcs.Parameters[0].Value = selid;
cmdNumLosses.Parameters[0].Value = selid;
numWins = Convert.ToInt32(cmdNumWins.ExecuteScalar());
numPlcs = Convert.ToInt32(cmdNumPlcs.ExecuteScalar());
numLosses = Convert.ToInt32(cmdNumLosses.ExecuteScalar());
Console.Write("selid : {0} \t num W {1} \t num P {2} num L {3} \t points {4}\n", selid,
numWins, numPlcs, numLosses, (3.0 * numWins + numPlcs) / (numWins + numLosses));
}
更好的方法是将这一切作为一个查询来完成:
select
r.selectionid,
count (1) filter (where m.markettype = 'WIN' and r.status = 'WINNER') as win,
count (1) fitler (where m.markettype = 'PLACE' and r.status = 'WINNER') as place,
count (1) filter (where m.markettype = 'WIN' and r.status = 'LOSER') as lose
from
arunners r
join amarkets m on m.marketid = r.marketid
where
r.status != 'REMOVED'
group by
r.selectionid
我可能错过了您逻辑的一些细微差别,但希望您能理解。这应该一口气快速高效地完成您的 C# 逻辑正在做的事情。它对数据库更友好,对于大型数据集应该更快。
如上所述,Npgsql 确实只允许在给定连接上同时打开一个数据 reader(与 MSSQL 的 MARS 功能不同)。但是,您可以做一些类似的事情 by manually using server-side cursors yourself。换句话说,您可以 运行 一个查询并从其游标中获取 X 行,然后根据第一个查询的输出执行第二个查询。您可以随时返回到第一个查询并获取更多 X 行,等等。
换句话说,虽然 Npgsql 不允许同时存在多个 reader,但 PostgreSQL 确实允许多个 query (或光标)同时。