迭代Xml Column元素并与Xml Variable进行比较

时间:2009-07-15 15:08:21

标签: sql-server xml sql-server-2005 tsql stored-procedures

我不确定如何正确地说出来,但我对Sql Server中的XML非常(非常)新。

我在表中定义了一个XML列,如果Xml列中的数据与预定义列表中的元素匹配,我想检索记录的id。

数据看起来有点像这样:

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

我想要检查的是,类似的参数和值列表是否与Xml列的列表相匹配。我可以看到你无法在Sql Server中进行Xml比较。

我可以为一个参数做到这一点:

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

但是我想要一些可以处理任何数量参数的东西。

2 个答案:

答案 0 :(得分:1)

您可以使用.nodes()运算符将XML投影到列中,然后比较投影列。这通常使用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);

您可以在CTE中使用此SELECT:

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';

答案 1 :(得分:1)

我自己对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')

尽管我不知道如何在最后计算不同的参数的好方法。您可以充分信任您的搜索条件,认为这是不必要的。