Предпочтительный способ доступа к данным в столбцах XML в SQL Server - PullRequest
2 голосов
/ 09 декабря 2010

Фон

В последнее время я стал гораздо чаще использовать XML в качестве столбца в SQL Server 2005. Вчера, во время небольшого простоя, я заметил, что две из таблиц ссылок, которые я использовал, очень удобен, и мне скучно Слезы о том, что нужно написать еще больше кода поддержки структуры для пары соединений.

Чтобы фактически сгенерировать данные для этих двух таблиц ссылок, я передаю два хранимых процедуры в XML-поле, которое записывает основную запись, разбивает две XML-переменные на @tables и вставляет их в фактические таблицы с новым SCOPE_IDENTITY() из основной записи.

После некоторого времени я решил полностью отказаться от этих таблиц и просто сохранить XML в полях XML. Теперь я понимаю, что здесь есть некоторые подводные камни, такие как общая производительность запросов, GROUP BY не работает с данными XML. И запрос, как правило, немного беспорядочный, но в целом мне нравится, что теперь я могу работать с XElement, когда получаю данные обратно.

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

Я задаюсь вопросом о том, как лучше получить эти данные. Многие из моих запросов связаны с получением основной записи, основанной на критериях ребенка или даже записи подчиненной. Большинство sprocs в базе данных делают это, но в гораздо более сложном масштабе, обычно требуя UDF и подзапросов для эффективной работы, но я привел тривиальный пример для проверки запросов некоторых данных ...

INSERT INTO Customers VALUES ('Tom', '', '<PhoneNumbers><PhoneNumber Type="1" Value="01234 456789" /><PhoneNumber Type="2" Value="01746 482954" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Andy', '', '<PhoneNumbers><PhoneNumber Type="2" Value="07948 598348" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Mike', '', '<PhoneNumbers><PhoneNumber Type="3" Value="02875 482945" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Steve', '', '<PhoneNumbers></PhoneNumbers>')

Теперь я вижу два способа схватить его.

Метод 1

DECLARE @PhoneType INT
SET  @PhoneType = 2

SELECT ct.*
FROM Customers ct
WHERE ct.PhoneNumbers.exist('/PhoneNumbers/PhoneNumber[@Type=sql:variable("@PhoneType")]') = 1

В самом деле? sql: переменная чувствует себя немного вредно Тем не менее, это работает. Однако значительно труднее получить доступ к данным более осмысленным образом.

Метод 2

SELECT ct.*, pt.PhoneType
FROM Customers ct
  CROSS APPLY ct.PhoneNumbers.nodes('/PhoneNumbers/PhoneNumber') AS nums(pn)
  INNER JOIN PhoneTypes pt ON pt.ID = nums.pn.value('./@Type[1]', 'int')
WHERE nums.pn.value('./@Type[1]', 'int') = @PhoneType

Это больше похоже на это. Я уже могу легко расширить это, чтобы сделать соединения и все другие хорошие вещи. Ранее я использовал CROSS APPLY в табличной функции, и это было очень хорошо. План выполнения для этого, в отличие от предыдущего запроса, значительно более продвинутый. По общему признанию я не сделал никакой индексации и еще много чего в этих таблицах, но это 97% от общей стоимости пакета.

Метод 2 (расширенный)

SELECT ct.ID, ct.CustomerName, ct.Notes, pt.PhoneType
FROM Customers ct
  CROSS APPLY ct.PhoneNumbers.nodes('/PhoneNumbers/PhoneNumber') AS nums(pn)
  INNER JOIN PhoneTypes pt ON pt.ID = nums.pn.value('./@Type[1]', 'int')
WHERE nums.pn.value('./@Type[1]', 'int') IN (SELECT ID FROM PhoneTypes)

Хороший IN пункт здесь. Я также могу сделать что-то вроде pt.PhoneType = 'Work'

Наконец

Итак, я, по сути, получаю результаты, которые мне нужны, но нужно ли что-то знать при использовании этого механизма для опроса небольших объемов данных XML? Будет ли это падать на производительность во время сложных поисков? И является ли хранение таких данных стиля разметки слишком большой нагрузкой?

примечание

В прошлом я использовал такие вещи, как sp_xml_preparedocument и OPENXML, просто чтобы передать списки в звездочек, но для сравнения это как глоток свежего воздуха!

1 Ответ

2 голосов
/ 09 декабря 2010

Один из подходов, который мы выбрали для некоторых из наших ключевых элементов информации, хранящейся в столбце XML, - это «отобразить» их как вычисленные постоянные свойства в «родительской» таблице.Это делается с помощью небольшой хранимой функции.

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

Это также здорово, поскольку его можно проиндексировать!Так что, если вы ищете и / или присоединяетесь к такому полю - это работает как талисман!

Так что вам, в основном, нужна сохраненная функция в соответствии с этим:у вас нет номера телефона типа 1, вы просто получите NULL.

Затем вам нужно расширить родительскую таблицу вычисляемым постоянным столбцом:

ALTER TABLE dbo.Customers
   ADD PhoneNumberType1 AS dbo.GetPhoneNo1(PhoneNumbers)

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

...