2011-05-28 83 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