【问题标题】:Iterate Xml Column elements and compare against Xml Variable迭代 Xml 列元素并与 Xml 变量进行比较
【发布时间】:2009-07-15 15:08:20
【问题描述】:

我不知道如何正确表达,但我对 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'

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

【问题讨论】:

  • 所有参数名称和值是否真的保存在一个 single 参数元素中?这会让事情变得……有趣
  • 对不起,我的错,已编辑。
  • 谢谢。现在你能说传入的“必须匹配所有这些”数据的格式是什么吗?是类似的 xml 格式 - 还是逗号分隔的字符串 - 还是表格?
  • 它是Xml格式,风格完全一样,只是元素的值和数量可能不同。

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


【解决方案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';

【讨论】:

  • 干杯我会看看这个。
  • 对于具有任意数量参数的通用解决方案,您还应该像 Aakash 一样将 @xml 变量分解为一个表并连接这两个表。
【解决方案2】:

我本人对 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')

虽然我想不出一个好方法来计算 distinct 参数。您可能能够充分信任您的搜索条件,从而认为这没有必要。

【讨论】:

  • 干杯我会看看这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-15
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
相关资源
最近更新 更多