Предпочтительный способ доступа к данным в столбцах XML в SQL Server
-
10-10-2019 - |
Вопрос
Фон
Недавно я начал использовать 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 или что -то в этом роде, вы ожидаете, что большинство ваших рядов будут, это может быть очень приятным и гладким способом получить эту информацию легче и эффективнее.