несколько курсоров с помощью Npgsql? - PullRequest
0 голосов
/ 22 октября 2018

Я привык (из Ады с использованием libpq) к

  • открыть курсор

  • получить некоторые значения ключа

  • использовать эти ключевые значения в качестве параметров связывания для других операторов.

Но вместо этого я получаю исключение Npgsql.NpgsqlOperationInProgressEx.

Однажды я получил этоработает с sql-сервером, но решил это путем добавления '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();

Да, я мог бы прочитать первый оператор в списке и выполнить цикл по нему, но у меня действительно есть довольно унаследованный код, который построен, как указано выше.

/ Бьорн

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Как было написано выше, Npgsql действительно позволяет одновременно открывать только одно устройство чтения данных для данного соединения (в отличие от функции MSSQL MARS).Однако вы можете сделать что-то похожее , вручную используя серверные курсоры .Другими словами, вы можете выполнить один запрос и извлечь X строк из его курсора, а затем выполнить второй запрос на основе выходных данных первого.Вы всегда можете вернуться к первому запросу и получить еще X строк и т. Д.

Другими словами, в то время как Npgsql не допускает более одного читателя одновременно, PostgreSQL делаетразрешить более одного запроса (или курсора) одновременно.

0 голосов
/ 23 октября 2018

Проблема в том, что NpgSql позволяет одновременно открывать только одно устройство чтения данных.Если вы столкнулись с ситуацией, когда вам нужно одновременно открыть более одного устройства чтения данных для одного и того же соединения, я бы сказал, что может быть лучший способ достижения вашей конечной цели.

Итак, отказ от ответственности ...не делай этогоНО, если вы хотите сделать то, что вы описали выше, это был бы способ сделать это:

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 #.Это будет намного удобнее для базы данных, а для больших наборов данных должно быть немного быстрее.

...