2012-04-19 4 views
0

Я читаю db, который содержит некоторые таблицы, используя команду ExecuteReader(). Основываясь на результате первого результата Read(), я читаю в двух разных таблицах, так как мне нужен идентификатор, возвращенный в первом запросе, для запуска второго.Использование нескольких ExecuteReader в то же время

Проблема заключается в том, что поиск осуществляется очень медленно.

tuCommand.CommandText = "SELECT * FROM tblTranslationUnit WHERE DocumentId = " + doc.DocumentId; 
var tuReader = tuCommand.ExecuteReader(); 
while (tuReader.Read()) 
{ 
    var tu = new TranslationUnit 
    { 
     TranslationUnitId = tuReader.GetInt64(0), 
     DocumentId = tuReader.GetInt64(1), 
     Raw = tuReader.GetString(2), 
     IsSegmented = tuReader.GetBoolean(3), 
     Reader = this, // Ryan: Fixed so that it sets the reader to itself 
    }; 

    using (var propCommand = _dbConn.CreateCommand()) 
    { 
     propCommand.CommandText = "SELECT * FROM tblTranslationUnitProperties WHERE TranslationUnitId = " + tu.TranslationUnitId; 
     var propReader = propCommand.ExecuteReader(); 
     while (propReader.Read()) tu.Properties.Add(GetProperty(propReader)); 
    } 
    yield return tu; 
} 

Если удалить второй ExecuteReader() запрос очень быстро

Я также попытался поставить вторую ExecuteReader(), используя новое соединение и новую транзакцию, но результат почти такой же

Любая идея или подсказка? Как я могу выполнить такой поиск? Есть ли лучший подход? (Я полагаю, да).


Подробнее Структура дб:

- Document 
     - properties 
     - errors 
    -TranslationUnits 
     - properties 
     - errors 
     - Segments 
      - properties 
      - errors 

Таким образом, в некоторых частях кода мы будем иметь такую ​​структуру

foreach (document in db) 
     foreach (property in document) 
     foreach (error in document) 
    foreach (translationunit in document) 
     foreach (property in translationunit) 
     foreach (error in translationunit) 
     foreach (segment in translationunit) 
      foreach (property in segment) 
      foreach (error in segment) 

Основываясь на том, что использование объединения вернуть все это не очень хорошая идея Я думал, если проблема была просто проблемой конфигурации SQLite. Я имею в виду, если это возможно, чтобы добавить любой параметр или аналогичный, чтобы сообщить системе, что мы будем использовать несколько указателей

Теперь мы переходим к DataTable решения:

  1. открывает соединение
  2. чтения 1000 записей из таблицы
  3. закрыть соединение
  4. открыть новое соединение
  5. чтения 1000 записей дочерней таблицы
  6. закрыть новое соединение
  7. ...
+0

Я не знаю, разрешит ли он проблему, но вы должны приложить оба вызова ExecuteReader() в инструкции «using», чтобы убедиться, что читатель правильно закрыт. –

+0

Чтобы быть уверенным, какую библиотеку и версию sqlite вы используете? –

ответ

0

Пробовали ли вы просто "JOIN"? Или мне что-то не хватает в вашем вопросе?

SELECT tbl2.* 
    FROM tblTranslationUnit tbl1 
    JOIN tblTranslationUnitProperties tbl2 ON tbl2.TranslationUnitId = tbl1.TranslationUnitId 
+0

Спасибо. Мы уже тестировали его с помощью «JOIN», и он был быстрым, и это могло быть решением для случая, который я разместил. Но у нас есть другие с большим количеством таблиц, где создание объекта на основе восстановленной даты может быть медленным. Если нет автоматического (или полуавтоматического) способа сделать это – davidd00

+0

Я не вижу этого случая; можете ли вы привести пример? – Argeman

+0

Если я использую ВЫБЕРИТЕ tbl1. *, Tbl2. * ОТ tblTranslationUnit tbl1 РЕГИСТРИРУЙТЕСЬ tblTranslationUnitProperties tbl2 ON tbl2.TranslationUnitId = tbl1.TranslationUnitId возвращать данные образуют обе таблицы. Как я могу легко создать объекты? поскольку строки из первого db могут появляться несколько раз. – davidd00

0

Сначала вы можете написать выбрать с присоединиться и получить все в одном запросе

SELECT * FROM tblTranslationUnit join tblTranslationUnitProperties on 
tblTranslationUnitProperties.TranslationUnitId = tblTranslationUnit.id 
WHERE DocumentId = @docID //<= use parameter 

Если это не поможет, возможно, вам необходимо индексировать таблицы.

0

Прочитайте весь результат из первого запроса сразу, закройте DataReader, а затем перечислите результаты в памяти.

+0

Спасибо. Это невозможно, так как первый запрос может быть действительно большим, и мы будем иметь память isses – davidd00

1

Похоже, что у вас есть проблемы с масштабируемостью. У SQLite есть слово «Lite» в нем по какой-то причине. В нем не хватает определенных функций, таких как высокий уровень параллелизма, мелкомасштабный контроль доступа, богатый набор встроенных функций, хранимые процедуры, эзотерические функции языка SQL, расширения XML и/или Java, масштабируемость по типу или по-пета-байту и т. Д. Я рекомендую менять базы данных для стартеров.

Я также не знаю, на ваш вопрос, почему вам понадобится 1000 документов в памяти сразу и особенно 1000 документов с 1000 частями с еще 1000 частями, все в памяти. Я не знаю ваших требований к пользовательскому интерфейсу, но в течение 15 лет в программировании я не помню, чтобы когда-либо отображалось 1000 вещей на одной веб-странице или форме без какого-либо механизма подкачки, так что вам действительно нужно получить 1000 * 1000 * 1000 сущностей из базы данных сразу?

Я думаю, вам нужно еще раз взглянуть на пользовательский интерфейс, текущую модель и слой данных, чтобы искать способы доставки как можно меньшего количества контента, не жертвуя большой производительностью. Рассмотрите возможность использования таких шаблонов, как Lazy Loading, чтение буферов вперед, кеширование, пейджинг, методы поиска, общие статические данные и т. Д., Чтобы снизить первоначальные затраты.

Подумайте о покупке дома. У большинства из нас нет денег, чтобы заплатить за весь дом, чтобы мы получили ипотеку. Ипотека - это способ распространения авансовых платежей с течением времени. Есть негативное влияние, которое приходит со всеми ипотечными кредитами, называемыми интересами. Теперь, вместо того, чтобы обстреливать 100 000, моя общая стоимость составляет 250 000, но поскольку я могу позволить себе оплатить текущий платеж, я не замечаю дополнительных 150 000, потому что дополнительная стоимость поглощается небольшими приращениями с течением времени. Также обратите внимание, что я не могу даже вернуть все 250 000 долларов, если я продаю свой дом через 5 лет вместо того, чтобы проживать на протяжении всего срока кредита.

Дело в том, что вы можете разложить затраты на дополнительные подключения для получения меньших наборов записей и по-прежнему предоставлять пользователю то, что им нужно сейчас. Это уменьшит общие первоначальные затраты, но добавит дополнительную стоимость для отдельных наборов записей.

+0

Спасибо. Как было сказано ниже, я не знаю, как легко разделить результат объединения на разные объекты (TranslationUnits, properties). – davidd00

+0

Идентификатор по порядку и по мере того, как вы просматриваете результаты, отслеживайте текущий идентификатор. Когда он изменится, запустите новый объект. –

+0

ОК. Я просто догадался, что есть более простой (или более чистый) способ. Это ясно. Я проверю это. Спасибо – davidd00

1

Привет я собираюсь добавить свои открытия на этом (я wokring с Дэвидом)

Я изменил путь мы читаем таблицы из БД с использованием буфера, как описано Дэвид так нет одновременных соединений ни читатели, выполняющие в то же время. Кажется, это немного быстрее, но заметно заметно. Вот некоторые цифры.

Я заполняю базу данных (все таблицы) 5000 единиц перевода за 2,5 секунды. Затем, когда я просматриваю таблицу TranslationUnit (около 5000 строк), время чтения впечатляет: 0,07 секунды. Код выглядит следующим образом:

foreach (var tu in document) 
{ 
    ... do something ... 
} 

Если я прочитал Сегменты для каждого письменного перевода, как это:

foreach (var tu in document) 
{ 
    foreach (var seg in tu) 
    { 
     ... do something ... 
    } 
} 

Чтение время начинает получать некрасиво: около 10 секунд. Обратите внимание, что каждая единица переводов имеет ровно 2 сегмента (хотя мы не ограничиваем это в дизайне)

Для 10000 единиц перевода требуется около 6 секунд для заполнения базы данных и около 2 минут для ее чтения. (почти мгновенно, если только 1 для чтения с переводными единицами)

Для 50000 единиц перевода требуется около 32 секунд для заполнения, и я сдался после 1 часа ожидания завершения чтения. (почти мгновенно, если только 1 для чтения переводческих единиц)

Так что я предполагаю, что время на чтение экспоненциально растет. Было бы разумно подумать, что это связано с тем, что он должен изменить указатель базы данных на другую таблицу? (между единицами перевода и таблицами сегментов).

+0

Является ли это тем, какие индексы решат? –

+0

Проблема решена. Создание индексов для таблицы Segments на основе TranslationUnitId сделало трюк. 11 секунд для чтения блоков 50000 и 100000 сегментов :) –

Смежные вопросы