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