Предпочтительный способ доступа к данным в столбцах XML в SQL Server

StackOverflow https://stackoverflow.com/questions/4397572

Вопрос

Фон

Недавно я начал использовать 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 В прошлом просто для перехода в списки в Sprocs, но это похоже на дыхание свежего воздуха в сравнении!

Это было полезно?

Решение

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

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

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

Таким образом, вам в основном нужна хранящаяся функция в соответствии с этим:

CREATE FUNCTION [dbo].[GetPhoneNo1](@DataXML XML)
RETURNS VARCHAR(50)
WITH SCHEMABINDING
AS BEGIN
      DECLARE @result VARCHAR(20)

      SELECT 
        @result = @DataXML.value('(/PhoneNumbers/PhoneNumber[@Type="1"]/@Value)[1]', 'VARCHAR(50)')
      RETURN @result
END

Если у вас нет телефона Type 1, вы просто вернетесь к нулю.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top