Выполните итерацию элементов столбца Xml и сравните их с переменной XML - PullRequest
0 голосов
/ 15 июля 2009

Я не уверен, как правильно это сформулировать, но я очень (очень) новичок в XML в Sql Server.

У меня есть столбец XML, определенный в таблице, и я хочу получить идентификатор записи, если данные в столбце Xml соответствуют элементам в предварительно определенном списке.

Данные выглядят примерно так:

<Parameters>
    <Parameter>
        <Name>Param1</Name>
        <Value>Value1</Value>
    </Parameter>
    <Parameter>
        <Name>Param2</Name>
        <Value>Value2</Value>
    </Parameter>
</Parameter>

Я хотел бы проверить, совпадает ли аналогичный список параметров и значений со списком столбцов Xml. Я вижу, что вы не можете сделать сравнение XML в Sql Server.

Я могу сделать это для одного параметра:

select * from table where
parameters.value('(/Parameters/Parameter/Name)[1]', 'varchar(50)') = 'Param1'
and 
parameters.value('(/Parameters/Parameter/Value)[1]', 'varchar(50)') = 'Value1'

Но я хочу что-то, что справится с любым количеством параметров.

Ответы [ 2 ]

1 голос
/ 15 июля 2009

Я сам довольно новичок в SQL XML, так что, возможно, есть лучший способ, чем этот, но он выглядит достаточно элегантно:

-- Set up some sample data

CREATE TABLE Data (
    Id int
    , Attributes xml
)

-- Number 1 is red and small
INSERT Data
VALUES ( 1, '
<Parameters>
    <Parameter>
        <Name>Color</Name>
        <Value>Red</Value>
    </Parameter>
    <Parameter>
        <Name>Size</Name>
        <Value>Small</Value>
    </Parameter>
</Parameters>' )

-- Number 2 is blue and large
INSERT Data
VALUES ( 2, '
<Parameters>
    <Parameter>
        <Name>Color</Name>
        <Value>Blue</Value>
    </Parameter>
    <Parameter>
        <Name>Size</Name>
        <Value>Large</Value>
    </Parameter>
</Parameters>' )

-- Number 3 is Large
INSERT Data
VALUES ( 3, '
<Parameters>
    <Parameter>
        <Name>Size</Name>
        <Value>Large</Value>
    </Parameter>
</Parameters>' )



-- Search for large ones
DECLARE @searchCriteriaXml xml
SET @searchCriteriaXml = '<Parameters>
    <Parameter>
        <Name>Size</Name>
        <Value>Large</Value>
    </Parameter>
</Parameters>'

/*
-- Or for large blue ones:
SET @searchCriteriaXml = '<Parameters>
    <Parameter>
        <Name>Size</Name>
        <Value>Large</Value>
    </Parameter>
    <Parameter>
        <Name>Color</Name>
        <Value>Blue</Value>
    </Parameter>
</Parameters>'
*/

-- *************************************
-- Here begins the search process

-- Shred the search criteria into a rowset
DECLARE @searchCriteria TABLE (
    Name nvarchar(100)
    , Value nvarchar(100)
)

INSERT INTO
    @searchCriteria
SELECT DISTINCT
    P.value('Name[1]', 'nvarchar(100)')
    , P.value('Value[1]', 'nvarchar(100)')
FROM
    @searchCriteriaXml.nodes('/Parameters/Parameter') SC(P)

-- Debug:
-- SELECT * FROM @searchCriteria

-- To find matching items, we want to shred each
-- item's xml, INNER JOIN against the search criteria,
-- and return those Ids that matched exactly as many rows 
-- as there are in the criteria

SELECT
    Id
FROM
    (
    SELECT
        Data.Id
        , P.value('Name[1]', 'nvarchar(100)') ParameterName
        , P.value('Value[1]', 'nvarchar(100)') ParameterValue
    FROM
        Data
        CROSS APPLY Attributes.nodes('/Parameters/Parameter') D(P)
    ) D -- the shredded data
    INNER JOIN @searchCriteria SC 
        ON D.ParameterName = SC.Name
        AND D.ParameterValue = SC.Value
GROUP BY Id
HAVING COUNT(*) = (SELECT COUNT(*) FROM @searchCriteria)



DROP TABLE Data

На самом деле, я думаю, что, подумав об этом, нет особой причины явно включать критерии поиска в эту табличную переменную - мы бы также просто уничтожили ее в самой операции соединения:

SELECT
    Id
FROM
    (
    SELECT
        Data.Id
        , P.value('Name[1]', 'nvarchar(100)') ParameterName
        , P.value('Value[1]', 'nvarchar(100)') ParameterValue
    FROM
        Data
        CROSS APPLY Attributes.nodes('/Parameters/Parameter') D(P)
    ) D -- the shredded data
    INNER JOIN 
    (
    SELECT DISTINCT
        P.value('Name[1]', 'nvarchar(100)') Name
        , P.value('Value[1]', 'nvarchar(100)') Value
    FROM
        @searchCriteriaXml.nodes('/Parameters/Parameter') SC(P)
    ) SC    -- the shredded search criteria
        ON D.ParameterName = SC.Name
        AND D.ParameterValue = SC.Value
GROUP BY Id
HAVING COUNT(*) = @searchCriteriaXml.value('count(/Parameters/Parameter)', 'int')

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

1 голос
/ 15 июля 2009

Вы можете проецировать XML в столбцы с помощью оператора .nodes () , а затем сравнивать проецируемые столбцы. Обычно это делается с помощью CROSS APPLY, например, (набирая из памяти):

SELECT x.value('(Name)[1]', 'varchar(50)') as Name
   , x.value('(Value)[1]', 'varchar(50)') as Value
   from Table
   CROSS APPLY parameters.nodes('/Parameters/Parameter') AS t(x);

Вы можете использовать этот SELECT, например, в CTE:

WITH shredded_xml AS (
SELECT Table.ID
   , x.value('(Name)[1]', 'varchar(50)') as Name
   , x.value('(Value)[1]', 'varchar(50)') as Value
   from Table
   CROSS APPLY parameters.nodes('/Parameters/Parameter') AS t(x))
SELECT * 
   FROM shredded_xml 
   WHERE Name = 'Param1'
    AND Value = 'Value1';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...