【问题标题】:Search for multiple values in xml column in SQL在 SQL 的 xml 列中搜索多个值
【发布时间】:2011-05-28 14:40:02
【问题描述】:

这是我的桌子

BasketId(int)   BasketName(varchar) BasketFruits(xml)
1       Gold        <FRUITS><FID>1</FID><FID>2</FID><FID>3</FID><FID>4</FID><FID>5</FID><FID>6</FID></FRUITS>
2       Silver      <FRUITS><FID>1</FID><FID>2</FID><FID>3</FID><FID>4</FID></FRUITS>
3       Bronze      <FRUITS><FID>3</FID><FID>4</FID><FID>5</FID></FRUITS>

我需要搜索具有 FID 值 1 和 3 的购物篮 所以在这种情况下,我会得到 GoldSilver

虽然我已经达到了可以搜索 SINGLE FID 值的结果,例如 1 使用此代码:

declare @fruitId varchar(10);
set @fruitId=1;
select * from Baskets
WHERE BasketFruits.exist('//FID/text()[contains(.,sql:variable("@fruitId"))]') = 1

如果是 T-SQL,我会像这样使用 IN 子句

SELECT * FROM Baskets where FID in (1,3)

感谢任何帮助/解决方法...

【问题讨论】:

    标签: sql-server xml sql-server-2008 xquery-sql


    【解决方案1】:

    第一个选项是添加另一个存在 where 子句。

    declare @fruitId1 int;
    set @fruitId1=1;
    
    declare @fruitId2 int;
    set @fruitId2=3;
    
    select *
    from @Test
    where
      BasketFruits.exist('/FRUITS/FID[.=sql:variable("@fruitId1")]')=1 and
      BasketFruits.exist('/FRUITS/FID[.=sql:variable("@fruitId2")]')=1
    

    另一个版本是在 xquery 语句中使用这两个变量,计算命中。

    select * 
    from @Test
    where BasketFruits.value(
      'count(distinct-values(/FRUITS/FID[.=(sql:variable("@fruitId1"),sql:variable("@fruitId2"))]))', 'int') = 2
    

    如果您在编写查询时知道要使用多少个 FID 参数,那么上述两个查询就可以正常工作。如果您处于 FID 数量不同的情况,您可以改用类似的方法。

    declare @FIDs xml = '<FID>1</FID><FID>3</FID>'
    
    ;with cteParam(FID) as
    (
      select T.N.value('.', 'int')
      from @FIDs.nodes('FID') as T(N)
    )  
    select T.BasketName
    from @Test as T
      cross apply T.BasketFruits.nodes('/FRUITS/FID') as F(FID)
      inner join cteParam as p
        on F.FID.value('.', 'int') = P.FID
    group by T.BasketName
    having count(T.BasketName) = (select count(*) from cteParam)
     
    

    将@FIDs 变量构建为 XML 以保存要在查询中使用的值。

    您可以在这里测试最后一个查询:https://data.stackexchange.com/stackoverflow/q/101600/relational-division-with-xquery

    【讨论】:

    • 出色的工作!!找这个答案很久了。伟大的工作米凯尔埃里克森。你就是男人!
    【解决方案2】:

    这比我希望的要复杂一些 - 但这个解决方案有效。

    基本上,我使用 CTE(公用表表达式)分解表并将 &lt;FID&gt; 节点中的所有值交叉连接到篮子名称。

    从该 CTE 中,我选择同时包含 13 值的篮子。

    DECLARE @Test TABLE (BasketID INT, BasketName VARCHAR(20), BasketFruits XML)
    
    INSERT INTO @TEST
    VALUES(1, 'Gold', '<FRUITS><FID>1</FID><FID>2</FID><FID>3</FID><FID>4</FID><FID>5</FID><FID>6</FID></FRUITS>'),
    (2, 'Silver', '<FRUITS><FID>1</FID><FID>2</FID><FID>3</FID><FID>4</FID></FRUITS>'),
    (3, 'Bronze', '<FRUITS><FID>3</FID><FID>4</FID><FID>5</FID></FRUITS>')
    
    ;WITH IDandFID AS
    (
    SELECT
        t.BasketID,
        t.BasketName,
        FR.FID.value('(.)[1]', 'int') AS 'FID'
    FROM @Test t
    CROSS APPLY basketfruits.nodes('/FRUITS/FID') AS FR(FID)
    )
    SELECT DISTINCT 
        BasketName
    FROM 
        IDandFID i1
    WHERE 
        EXISTS(SELECT * FROM IDandFID i2 WHERE i1.BasketID = i2.BasketID AND i2.FID = 1)
        AND EXISTS(SELECT * FROM IDandFID i3 WHERE i1.BasketID = i3.BasketID AND i3.FID = 3)
    

    运行这个查询,我得到了预期的输出:

    BasketName
    ----------
    Gold
    Silver
    

    【讨论】:

    • 这个解决方案毫无疑问是有效的。做得好!但是,如果要匹配的值的数量像现在这样增加,如果我想搜索具有 FruitIds 1,2 和 3 的篮子,则必须使用动态查询连接另一个 EXISTS 语句。或者我想,我需要在 while 循环中递归调用这个存储过程......?
    • @nav:是的,我同意 - 解决方案并不完美,因为它不是“可扩展的” - 但我现在想不出任何其他方式
    【解决方案3】:

    这是不是太琐碎了?

    SELECT * FROM Baskets WHERE BasketFruits LIKE '%<FID>1</FID>%' AND BasketFruits LIKE '%<FID>3</FID>%'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-16
      • 2018-07-06
      • 1970-01-01
      • 2020-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多