2017-02-16 11 views
1

Я пытаюсь создать базу данных (используя SQLite в C#) с таблицей для хранения предложений, таблицей для хранения каждого отдельного слова, которое используется в этих предложениях, и таблицей соединения, которая связывает слова к предложениям, которые они появляются. Затем я пытаюсь заполнить базу данных более чем 15 миллионами предложений. По моим оценкам, в моем коде происходит около 150 миллионов вставок. Прямо сейчас, мой код выполняет всего пару сотен предложений в секунду, что займет много времени, чтобы пройти через этот огромный набор данных. Как я могу сделать это быстрее?Оптимизация запросов sqlite insert

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

Таблицы:

CREATE TABLE sentences (sid INTEGER NOT NULL PRIMARY KEY, sentence TEXT); 
CREATE TABLE words (wid INTEGER NOT NULL PRIMARY KEY, dictform TEXT UNIQUE); 
CREATE TABLE sentence_words(sid INTEGER NOT NULL, wid INTEGER NOT NULL, CONSTRAINT PK_sentence_words PRIMARY KEY (sid, wid), FOREIGN KEY(sid) REFERENCES Sentences(sid), FOREIGN KEY(wid) REFERENCES Words(wid)); 

Код:

while ((input = sr.ReadLine()) != null) //read in a new sentence 
     { 
      tr = m_dbConnection.BeginTransaction(); 
      sql = "INSERT INTO sentences (sentence) VALUES(@sentence)"; 
      cmd = new SQLiteCommand(sql, m_dbConnection); 
      cmd.Parameters.AddWithValue("@sentence", input); 
      cmd.ExecuteNonQuery(); 

      dict_words = jutils.onlyDict(input); //convert all words to their 'dictionary form' 
      var words = dict_words.Split(' '); 
      foreach (var wd in words) //for each word 
      { 
       sql = "INSERT or IGNORE INTO words (dictform) VALUES(@dictform)"; 
       cmd = new SQLiteCommand(sql, m_dbConnection); 
       cmd.Parameters.AddWithValue("@dictform", wd); 
       cmd.ExecuteNonQuery(); 

       sql = "INSERT or IGNORE INTO sentence_words (sid, wid) VALUES((SELECT sid FROM sentences WHERE sentence = @sentence), (SELECT wid FROM words WHERE dictform = @dictform))"; 
       cmd = new SQLiteCommand(sql, m_dbConnection); 
       cmd.Parameters.AddWithValue("@sentence", input); 
       cmd.Parameters.AddWithValue("@dictform", wd); 
       cmd.ExecuteNonQuery(); 
      } 
      tr.Commit(); 
     } 

ответ

2

Всегда, мы должны избегать 'один на один' задач SQL при работе с такими большими данными.

В моем случае (если память не обременена), загрузите данные в DataTable и обработайте (с помощью LINQ) по мере необходимости и, наконец, используйте SqlBulkCopy в конце.

Существует также SqlBulkUpdate, но созданный частным автором, поддерживающим SQL 2008. Если до 2008 года мы все еще можем быстро это сделать, но вам нужно создать временную таблицу SQL и использовать команду UPDATE Join.

SqlBulkCopy очень быстро, как раз в несколько секунд.

+0

После некоторого тестирования запросов для каждой из моих таблиц кажется, что очень медленная часть - это моя таблица соединений. Чтобы сделать каждую вставку в этой таблице, мне нужно делать выборки через другую огромную таблицу, которая, кажется, является проблемой. Я не очень хорошо знаком с таблицами данных или linq, но я предполагаю, что для создания данных для таблицы соединений мне все равно придется выполнять операции такого же типа, нет? Любые идеи о том, как я упростил бы этот аспект этого? – Zarxrax

+0

В моем случае было всего 10 ~ 30 тысяч строк, но я сдался. Вам лучше знать, что это «times x numbers of rows», независимо от того, как маленькое слово с 1 столбцом. Итерирование миллионов раз физического подключения к SQL будет продолжаться вечно. Если вам нужно иметь дело с базой данных SQL, вам лучше ознакомиться с DataTable, LINQ, Subquery, BulkCopy, BulkUpdate, LINQtoSQL.Изменение миллионов раз «один за другим» будет быть ночным кошмаром. –

+0

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

0

Вы можете сгруппировать по определенному числу линий

int lines = -1; 
int group = 500; 
while ((input = sr.ReadLine()) != null) 
    { 
    lines++; 
    if (lines%group == 0) { 
     tr = m_dbConnection.BeginTransaction(); 
     } 

и совершают каждой группу

 if (lines%group == group-1) { 
     tr.Commit(); 
     } 
    } 
if (lines%group >= 0 && lines%group < group-1) { 
    tr.Commit(); 
    } 

я имел некоторые старые случаи, когда я был вынужден использовать .Net 4.0 и BulkCopy не был из-за его асинхронной реализации.

0

Просто поместите все в одну транзакцию. (Журнал отката хранит старую версию любой страницы, измененной транзакцией, поэтому для пустой базы данных она не может стать большой.)

Кроме того, все поиски значения sentence неэффективны, потому что база данных должна пройти всю таблицу. Если этот столбец имел индекс (явно или неявно с ограничением UNIQUE), эти поисковые запросы были бы намного быстрее.

+0

Похоже, это может быть правильно. Я изначально тестировал его в режиме отладки визуальной студии, и я наблюдал, как использование памяти медленно увеличивается по мере запуска программы. Но после его компиляции и разрешения на несколько часов моя память фактически не увеличилась. Однако код, кажется, все медленнее и медленнее. После работы в течение нескольких часов ему удалось завершить чуть более 200 000 предложений из 15 миллионов. Таким образом, даже если все это завершено в одну транзакцию, оно слишком медленное. – Zarxrax

+0

Это, вероятно, все эти неиндексированные запросы «предложения». –

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