Структурирование больших наборов строк SQL и их использование в .NET - PullRequest
6 голосов
/ 22 ноября 2011

Взгляните на эту схему псевдо (обратите внимание, что является упрощением, поэтому постарайтесь не комментировать слишком «целесообразность» самой схемы). Предположим, что на ФК установлены индексы.

 TABLE Lookup (
     Lookup_ID int not null PK
     Name nvarchar(255) not null
 )

 TABLE Document (
     Document_ID int not null PK
     Previous_ID null FK REFERENCES Document(Document_ID)
 )

 TABLE Document_Lookup (
     Document_ID int not null FK REFERENCES Document(Document_ID)
     Lookup_ID int not null FK REFERENCES Lookup(Lookup_ID)
 )

Тома: Документ, 4 миллиона строк, из которых 90% имеют нулевое значение поля Previous_ID; Поиск, 6000 строк, Среднее число запросов, прикрепленных к каждому документу 20, что дает Document_Lookup 80 миллионов строк.

Теперь в .NET Service есть структура для представления строки поиска, подобной этой: -

 struct Lookup
 {
      public int ID;
      public string Name;
      public List<int> DocumentIDs;
 }

и строки поиска хранятся в Dictionary<int, Lookup>, где ключом является идентификатор поиска. Важным моментом здесь является то, что в этом словаре должны содержаться записи, на которые на поиск ссылается хотя бы один документ, то есть список DocumentIDs должен иметь число> 0.

Моя задача - эффективно заполнить этот словарь. Таким образом, простой подход будет: -

  SELECT dl.Lookup_ID, l.Name, dl.Document_ID
  FROM Document_Lookup dl
  INNER JOIN Lookup l ON l.Lookup_ID = dl.Lookup_ID
  INNER JOIN Document d ON d.Document_ID = dl.Lookup_ID
  WHERE d.Previous_ID IS NULL
  ORDER BY dl.Lookup_ID, dl.Document_ID

Это может быть использовано для достаточно эффективного заполнения словаря.

Вопрос : Производит ли базовая доставка набора строк (TDS?) Некоторую оптимизацию? Мне кажется, что запросы, которые нормализуют данные, очень распространены, поэтому вероятность того, что значения полей не меняются от одной строки к другой, высока, поэтому было бы целесообразно оптомизировать поток, не посылая значения полей, которые не т изменилось. Кто-нибудь знает, имеет ли место такая оптомизация? (Оптимизация, по-видимому, не существует).

Какой более сложный запрос я мог бы использовать для устранения дублирования (я думаю, в частности, для повторения значения имени)? Я слышал о такой вещи, как «вложенный набор строк», можно ли создать такую ​​вещь? Будет ли это более производительным? Как бы я получить доступ к нему в .NET?

Я бы выполнил два запроса; один для заполнения словаря поиска, а второй для заполнения списков. Затем я добавил бы код, чтобы выбить неиспользуемый поиск. Однако представьте, что мои прогнозы были неверными, и в конечном итоге «Уточняющий запрос» составил 1 миллион строк, а на какой-либо документ ссылалась только четверть?

Ответы [ 2 ]

1 голос
/ 24 января 2012
  • Поскольку на практике имена относительно короткие, оптимизация может не потребоваться.

  • Самая простая оптимизация - это разделить ее на два запроса: один для получения имен, другой для получения списка Document_ID. (может быть в другом порядке, если это упрощает заполнение ваших структур данных).

Пример:

/*First get the name of the Lookup*/
select distinct dl.Lookup_ID, l.Name
FROM Document_Lookup dl 
INNER JOIN Lookup l ON l.Lookup_ID = dl.Lookup_ID 
INNER JOIN Document d ON d.Document_ID = dl.Lookup_ID 
WHERE d.Previous_ID IS NULL 
ORDER BY dl.Lookup_ID, dl.Document_ID 

/*Now get the list of Document_IDs for each*/
SELECT dl.Lookup_ID, dl.Document_ID 
FROM Document_Lookup dl 
INNER JOIN Lookup l ON l.Lookup_ID = dl.Lookup_ID 
INNER JOIN Document d ON d.Document_ID = dl.Lookup_ID 
WHERE d.Previous_ID IS NULL 
ORDER BY dl.Lookup_ID, dl.Document_ID 
  • Существуют также различные приемы, которые вы можете использовать, чтобы втиснуть их в один стол, но я полагаю, что это не стоит.

  • Наборы иерархических строк, о которых вы думаете, являются поставщиком MSDASHAPE OLEDB. Они могут сделать то, что вы предлагаете, но ограничат вас использованием поставщика OLEDB для SQL, который может не соответствовать вашим ожиданиям.

  • Наконец, внимательно рассмотрим XML

Например:

select
  l.lookup_ID as "@l", 
  l.name as "@n",
  (
    select dl.Document_ID as "node()", ' ' as "node()" 
    from Document_Lookup dl where dl.lookup_ID = l.lookup_ID for xml path(''), type
  ) as "*"
  from Lookup l
  where l.lookup_ID in (select dl.lookup_ID from Document_Lookup dl)
  for xml path('dl')

возвращается:

<dl l="1" n="One">1 2 </dl>
<dl l="2" n="Two">2 </dl>
0 голосов
/ 23 ноября 2011

Когда вы спрашиваете о «вложенных наборах строк», вы имеете в виду использование метода DbDataReader.NextResult ()?

если ваш запрос имеет два «выхода» (два оператора select, которые возвращают отдельные результирующие наборы), вы можете пройти по первому циклу с помощью DbDataReader.Next (), а когда он возвращает «false», вы можете вызвать DbDataReader.NextResult () и затем снова используйте DbDataReader.Next () для продолжения.

var reader = cmd.ExecuteReader();
  while(reader.Read()){
    // load data
  }

  if(reader.NextResult()){
    while(reader.Read()){
      // lookup record from first result

      // load data from second result
    }
  }

Я часто делал это, чтобы уменьшить количество дублирующихся данных в аналогичной ситуации, и это работает очень хорошо:

SELECT * FROM tableA WHERE [condition]
SELECT * FROM tableB WHERE EXISTS (SELECT * FROM tableA WHERE [condition] AND tableB.FK = tableA.PK)

Отказ от ответственности: я не пробовал это с набором результатов, большим, как вы описываете.

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

...