2011-05-28 72 views
4

在XML列多个值这是我的表搜索SQL

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 所以,在这种情况下,篮我会得到黄金

虽然我已经达到的结果,我可以寻找像使用此代码 一个单FID值:

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) 

任何帮助/变通赞赏...

回答

2

第一种选择将再添存在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以保存要在查询中使用的值。

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

+0

出色的工作!长期以来一直在寻找这个答案。伟大的工作Mikael Eriksson。你是男人! – nav 2011-05-30 02:03:52

0

这是太微不足道了?

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

不能使用'LIKE'操作上键入'XML'的列.... – 2011-05-28 21:15:05

1

这比我所希望的要多一点 - 但这个解决方案是有效的。

基本上,我正在使用CTE(公用表表达式),它将表格拆分并交叉连接来自<FID>节点的所有值到篮子名称。

从该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 
+0

此解决方案毫无疑问。做得好! 但是,如果要匹配的值的数量像现在一样增加,如果我想用** FruitIds ** 1,2和3搜索篮子,则必须使用动态查询连接另一个EXISTS语句。 或者我想,我需要在递归的while循环中调用这个存储过程...? – nav 2011-05-28 23:50:22

+0

@nav:是的,我同意 - 解决方案并不完美,因为它不是“可扩展的” - 但我现在想不到任何其他方式 – 2011-05-29 07:36:18