Огромная таблица (9 миллионов записей) в SQL-сервере - PullRequest
8 голосов
/ 26 апреля 2011

Наша страховая компания только что отправила нам данные о потенциальных клиентах.Есть 9 миллионов строк.Строки состоят из LeadID (guid), RawLeadXML (xml - возможно, максимум 3-4 КБ) и LeadStatusID (int).

Сначала я попытался добавить целое число автономного номера и сделать его первичным ключом этогоТаблица.Ну, он добавил поле, но не смог сделать его первичным ключом (Недостаточно памяти в пуле буферов.)

Что мне нужно сделать, это взять каждую запись, 1 на 1, иполучите XML, поместите его в объект XmlDocument в .Net, удалите все поля, которые мне нужны (имя, фамилия и т. д.), и сохраните эту проанализированную информацию в другой таблице.

Я даже не могу получить этооператор для запуска: выберите * из числа потенциальных клиентов, где идентификатор от 1 до 1000

Если я просто выберу 1000 записей за раз (выберите лучшие 1000 * из числа потенциальных клиентов), это работает, но как тогда выбрать следующие 1000записи без какой-либо контрольной точки?

Моя машина имеет 4 процессора (2,53 ГГц) и 12 ГБ оперативной памяти.Это не сервер, но это мощная машина.Честно говоря, я не знаю, что делать дальше.

EDIT : я не учел, что исходный файл на самом деле был файлом MDF (и связанным с ним LDF), поэтому я просто прикрепил его ких в SQL Server.

РЕДАКТИРОВАТЬ 2 : Я перепутал и сказал, что столбец RawLeadXML был XML - это не так, это просто nvarchar (max).Честно говоря, я не знал, что там был тип данных xml.

РЕДАКТИРОВАТЬ 3 : я даже не могу выдать оператор удаления для этой таблицы: "удалить из лидов, где leadid = '100a7927-5311-4f12-8fe3-95c079d32dd4'" взрывается с помощью:

Msg 802, Level 17, State 20, Line 2
There is insufficient memory available in the buffer pool.

Понятия не имею, что делать дальше.Как, черт возьми, это вообще проблема?В мире существуют тысячи баз данных с большим количеством записей, чем у меня.

РЕДАКТИРОВАТЬ 4 : На случай, если кому-то все равно, ни одно из приведенных ниже решений не сработало.Я уверен, что это ограничение моей машины, и определенно не осуждение за прекрасные ответы, которые я получил ниже.В настоящее время я передаю заархивированную БД (2,6 ГБ) на наши серверы в Rackspace, а затем попытаюсь добавить индекс на этом оборудовании, надеюсь, не отключая наши производственные серверы.Как только индекс будет добавлен, я надеюсь, что смогу заархивировать базу данных и перенести ее обратно на свой локальный компьютер, а затем иметь возможность что-то с ней сделать.

EDIT 5 : Моя машина была буквально неспособна обрабатывать стол такого размера.Моя машина имеет 12 ГБ оперативной памяти, 64-разрядную версию Windows 7 Professional, четырехъядерный процессор 2,53 ГГц, SSD-накопитель и т. Д. Это довольно проблематично для машины для разработки.И это не могло справиться с этим.

Итак, я переместил БД на наш сервер в Rackspace в Лондоне.48 ГБ или памяти в этом, и он смог добавить нужный мне индекс.Даже после этого моя машина не смогла сделать с ней ничего такого полезного, поэтому я написал программу .Net, которая работает в Лондоне, чтобы выводить 1000 записей за раз, анализировать их в другой таблице, а затемпометьте исходные записи как обработанные.

Как только я это сделаю, мне придется покинуть БД в Лондоне, потому что я сомневаюсь, что смогу написать какие-либо значимые отчеты против этого чудовища на местном уровне.Это сделает разработку веселой.

Резюме : Я считаю, что нет хорошего способа обработки такого большого набора данных без использования аппаратного обеспечения серверного класса с как минимум 48 ГБ ОЗУ (в моем случае).

Ответы [ 9 ]

9 голосов
/ 27 апреля 2011

Ошибка 802 не означает нехватку памяти в классическом смысле нехватки памяти для выполнения выделения (что вызвало бы ошибка 701 ). Ошибка 802 фактически означает, что буферный пул не может расти, что может произойти по нескольким причинам:

  • макс. Память сервера параметр явно предотвращает рост пула буферов, проверьте настройки сервера.
  • Предел виртуального адресного пространства x86 достигнут, а AWE не включен . Проверьте, есть ли у вас экземпляр x86 (32-разрядный), и если да, проверьте, если все выполнены условия для включения AWE.

Если проблема все еще не найдена, прочитайте Как использовать команду DBCC MEMORYSTATUS для мониторинга использования памяти в SQL Server 2005 (статья в равной степени применима и к SQL Server 2008, и к 2008 R2) и следуйте инструкциям, чтобы понять, кто / что потребляет вашу память.

4 голосов
/ 26 апреля 2011

9 миллионов строк невелики, у вас, вероятно, нет индекса для столбца LeadId. Сначала создайте его, хотя это займет некоторое время (не обязательно должен быть уникальный или первичный ключ). Чем использовать «ВЫБЕРИТЕ ТОП 1000 LeadId, RawXML ORDER BY LeadId» для первого запроса. Запишите последнее значение LeadId (MaxLeadId), используйте "ВЫБЕРИТЕ ТОП 1000 LeadId, RawXML, где LeadId> MaxLeadId, ЗАКАЗАТЬ по LeadId" и так далее ...

3 голосов
/ 26 апреля 2011

Добавление столбца не является опцией, так как добавление автоматического идентификатора также не работает.Вы хотите сохранить измененные / очищенные данные в новой таблице.Эта таблица может иметь автоматический идентификатор и отдельные столбцы для данных, извлекаемых из Xml.

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

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

На основании комментария предлагается предложение SQL:

WHILE EXISTS(SELECT * FROM [source] [s] 
              WHERE NOT EXISTS(SELECT * FROM [destination] [d] WHERE [d].[leadId] = [s].[leadId]))
BEGIN
    INSERT INTO [destination] ([leadId], [RawLeadXML], [LeadStatusId])
        SELECT TOP 100 [s].[leadId], [s].[RawLeadXML], [s].[LeadStatusId]
        FROM [source] [s] 
        WHERE NOT EXISTS(SELECT * FROM [destination] [d] WHERE [d].[leadId] = [s].[leadId])
END

Я установил количество записей для вставки равным 100. Это должно сэкономить ваше использование памяти.

1 голос
/ 26 апреля 2011

Обдумайте это, используя row_number () и ranks.

Взгляните на эту тему . В нем есть основные понятия, с которых можно начать.

1 голос
/ 26 апреля 2011

Как насчет

  • Выберите первую строку (верхнюю часть 1) таблицы, сохраните LeadID в переменной.
  • Загрузите значение столбца xml в документ xml (.NET)
  • нацелить на нужные вам узлы с помощью xpath
  • вставить эти значения в новую запись
  • удалить запись из "главной" таблицы с помощью регистра (илипометить это как выполненное)
  • commit
  • Шаг 1 снова
1 голос
/ 26 апреля 2011

У вас есть диск, чтобы сэкономить?Возможно, создать такую ​​же структуру таблицы в «TableAux», но с Autonumeric Id, а затем выполнить вставку из таблицы ...

Импорт в TableAux из плоского файла (сначала экспортировать его, если он не плоский файл)), это еще один способ сделать это.

Получение идентификатора для ваших регистров является приоритетным для работы с ними.

0 голосов
/ 04 мая 2011

Как насчет простого извлечения данных в текстовый файл прямо из БД Access - тогда вы можете просто импортировать их в RDBM по вашему выбору с массовой вставкой.

Получение доступа к базам данных через таблицы ссылок в MSSQL не является идеальным. Здесь мы используем курсор записи только для чтения, только вперед, чтобы пройти по строкам по одной за раз.

Это VBScript, поэтому он не будет самым быстрым, но он должен работать. Вам придется адаптировать процедуру извлечения XML для вашей схемы.


' Code run against a access DB with column RawLeadXML

Dim connection

Set connection = CreateObject("ADODB.Connection")
connection.Open "Driver={Microsoft Access Driver (*.mdb)}; DBQ=d:\path\to\file.mdb;"

Dim recs

' By default this is a read only, forward only cursor
Set recs = connection.Execute("SELECT * FROM Leads")

Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

Dim out
Set out = fso.OpenTextFile("d:\path\to\output.txt", 2, True)

Dim id

id = 0

While Not recs.EOF
    id = id + 1 
    out.Write CStr(id) ' write an ID counter
    out.Write ","
    ExtractFieldsFromXML recs.Fields("RawLeadXML").Value, out
    out.Write Chr(10) ' Linefeed
    recs.MoveNext
Wend

out.Close
recs.Close
connection.Close

' Extract data from the XML and write it to the stream
' separated by commas
Sub ExtractFieldsFromXML(xml, out)
    Dim doc

    Set doc = CreateObject("MSXML2.DOMDocument")

    MsgBox xml

    doc.loadXML xml

    out.Write doc.selectSingleNode("/xml/firstname").text
    out.Write ","
    out.Write doc.selectSingleNode("/xml/lastname").text

End Sub
0 голосов
/ 26 апреля 2011

В каком формате вы изначально получаете выписку?Если это CSV или что-то подобное, и вам не нужна другая информация в этом фрагменте XML, я бы использовал SSIS и полностью пропустил эту промежуточную таблицу.У нативного типа XML есть некоторые накладные расходы, которые вам не нужны, если вы просто хотите уничтожить один раз XML-файл, а затем выбросить его.

В противном случае вы сможете использовать путь XMLоператоры непосредственно в SQL-запрос, чтобы получить данные из одной таблицы в другую.Если вы пытаетесь добавить новый ПК от дизайнера, он может потерпеть неудачу на большом столе.Вам нужно будет написать сценарий изменений и запустить их вручную, и, возможно, настроить их, чтобы сделать вещи более эффективными.В конечном счете, 9-метровые ряды не так уж велики по современным стандартам, но вам нужно продумывать вещи больше, чем с 9-ю тысячами.

0 голосов
/ 26 апреля 2011

Если это плоский файл, не могли бы вы получить первые 1000 строк, загрузить их, а затем вернуться назад и удалить строки из файла на основе GUID, а затем повторить? (Сначала сделайте резервную копию файла.)

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

...