2013-05-13 41 views
6

我有一個文檔表和一個標記表。文件標有各種值。從表A中選擇它加入到表B中的所有數據的地方

我試圖創建這些標籤的搜索,並在大多數情況下它工作。但是,當它匹配任何標籤時,我會得到額外的結果。我只想要與所有標籤匹配的結果。

我創造了這個來說明問題http://sqlfiddle.com/#!3/8b98e/11

表和數據:

CREATE TABLE Documents 
(
DocId INT, 
DocText VARCHAR(500) 
); 

CREATE TABLE Tags 
(
    TagId INT, 
    TagName VARCHAR(50) 
); 

CREATE TABLE DocumentTags 
(
    DocTagId INT, 
    DocId INT, 
    TagId INT, 
    Value VARCHAR(50) 
); 

INSERT INTO Documents VALUES (1, 'Document 1 Text'); 
INSERT INTO Documents VALUES (2, 'Document 2 Text'); 

INSERT INTO Tags VALUES (1, 'Tag Name 1'); 
INSERT INTO Tags VALUES (2, 'Tag Name 2'); 

INSERT INTO DocumentTags VALUES (1, 1, 1, 'Value 1'); 
INSERT INTO DocumentTags VALUES (1, 1, 2, 'Value 2'); 
INSERT INTO DocumentTags VALUES (1, 2, 1, 'Value 1'); 

代碼:

-- Set up the parameters 
DECLARE @TagXml VARCHAR(max) 
SET @TagXml = '<tags> 
        <tag> 
        <description>Tag Name 1</description> 
        <value>Value 1</value> 
        </tag> 
        <tag> 
        <description>Tag Name 2</description> 
        <value>Value 2</value> 
        </tag> 
       </tags>' 

-- Create a table to store the parsed xml in 
DECLARE @XmlTagData TABLE 
(
    id varchar(20) 
    ,[description] varchar(100) 
    ,value varchar(250) 
) 

-- Populate our XML table 
DECLARE @iTag int 
EXEC sp_xml_preparedocument @iTag OUTPUT, @TagXml 
-- Execute a SELECT statement that uses the OPENXML rowset provider 
-- to produce a table from our xml structure and insert it into our temp table 
INSERT INTO @XmlTagData (id, [description], value) 
SELECT id, [description], value 
FROM OPENXML (@iTag, '/tags/tag',1) 
     WITH (id varchar(20), 
       [description] varchar(100) 'description', 
       value varchar(250) 'value') 

EXECUTE sp_xml_removedocument @iTag 

-- Update the XML table Id's to match existsing Tag Id's 
UPDATE  @XmlTagData 
SET   X.Id = T.TagId 
FROM  @XmlTagData X 
INNER JOIN Tags T ON X.[description] = T.TagName 

-- Check it looks right 
--SELECT * 
--FROM @XmlTagData 

-- This is where things do not quite work. I get both doc 1 & 2 back, 
-- but what I want is just document 1. 
-- i.e. documents that have both tags with matching values 
SELECT DISTINCT D.* 
FROM Documents D 
INNER JOIN DocumentTags T ON T.DocId = D.DocId 
INNER JOIN @XmlTagData X ON X.id = T.TagId AND X.value = T.Value 

(注意我不是一個DBA,所以可能有更好的辦法,希望我走在正確的軌道上,但我a如果我的實施可以改善,請打開其他建議。)

任何人都可以提供有關如何獲得只有所有標籤的結果的任何建議嗎?

非常感謝。

+0

如果您有諸如coldfusion,.net,php等應用程序代碼可用,則使用該代碼會更容易。 – 2013-05-13 16:22:07

+0

這個問題叫做關係分工。 – 2013-05-13 16:45:57

回答

2

在最後一個查詢中使用[NOT] EXISTSEXCEPT運算符

SELECT * 
FROM Documents D 
WHERE NOT EXISTS (
        SELECT X.ID , X.Value 
        FROM @XmlTagData X 
        EXCEPT 
        SELECT T.TagId, T.VALUE 
        FROM DocumentTags T 
        WHERE T.DocId = D.DocId 
       ) 

演示上SQLFiddle

OR

SELECT * 
FROM Documents D 
WHERE EXISTS (
       SELECT X.ID , X.Value 
       FROM @XmlTagData X 
       EXCEPT 
       SELECT T.TagId, T.VALUE 
       FROM DocumentTags T 
       WHERE T.DocId != D.DocId 
      ) 

演示上SQLFiddle

OR

你也可以使用使用XQuery方法簡單的解決方案:nodes()value())和CTE /子查詢。

-- Set up the parameters 
DECLARE @TagXml XML 
SET @TagXml = '<tags> 
        <tag> 
        <description>Tag Name 1</description> 
        <value>Value 1</value> 
        </tag> 
        <tag> 
        <description>Tag Name 2</description> 
        <value>Value 2</value> 
        </tag>    
       </tags>'    


;WITH cte AS 
(
    SELECT TagValue.value('(./value)[1]', 'nvarchar(100)') AS value, 
     TagValue.value('(./description)[1]', 'nvarchar(100)') AS [description]  
    FROM @TagXml.nodes('/tags/tag') AS T(TagValue) 
) 
    SELECT * 
    FROM Documents D 
    WHERE NOT EXISTS (
        SELECT T.TagId, c.value 
        FROM cte c JOIN Tags T WITH(FORCESEEK) 
         ON c.[description] = T.TagName 
        EXCEPT 
        SELECT T.TagId, T.VALUE 
        FROM DocumentTags T WITH(FORCESEEK) 
        WHERE T.DocId = D.DocId       
        ) 

演示上SQLFiddle

OR

-- Set up the parameters 
DECLARE @TagXml XML 
SET @TagXml = '<tags> 
        <tag> 
        <description>Tag Name 1</description> 
        <value>Value 1</value> 
        </tag> 
        <tag> 
        <description>Tag Name 2</description> 
        <value>Value 2</value> 
        </tag>    
       </tags>'  

    SELECT * 
    FROM Documents D 
    WHERE NOT EXISTS (
        SELECT T2.TagId, 
          TagValue.value('(./value)[1]', 'nvarchar(100)') AS value       
        FROM @TagXml.nodes('/tags/tag') AS T(TagValue) 
         JOIN Tags T2 WITH(FORCESEEK) 
         ON TagValue.value('(./description)[1]', 'nvarchar(100)') = T2.TagName           
        EXCEPT 
        SELECT T.TagId, T.VALUE 
        FROM DocumentTags T WITH(FORCESEEK) 
        WHERE T.DocId = D.DocId      
        ) 

演示上SQLFiddle

爲了提高性能,使用索引(索引的強制操作上的標籤和DocumentTags表求)和表格提示(FORCESEEK提示添加到上面的查詢中):

CREATE INDEX x ON Documents(DocId) INCLUDE(DocText) 
CREATE INDEX x ON Tags(TagName) INCLUDE(TagId) 
CREATE INDEX x ON DocumentTags(DocId) INCLUDE(TagID, VALUE) 
+0

完美!非常感謝你。 – Yetiish 2013-05-14 07:39:36

+1

不客氣;)作爲迴應,用XQuery方法添加了一個新的解決方案。試試吧。 – 2013-05-14 08:29:34

+0

看起來優雅!削減了很多編碼!謝謝 – Yetiish 2013-05-14 11:03:02

0

我真的不知道該語法SQL Server的,但我想這樣的事情應該工作

0

添加where子句來檢查不存在的條件:

SELECT DISTINCT D.* 
FROM Documents D 
INNER JOIN DocumentTags T ON T.DocId = D.DocId 
INNER JOIN @XmlTagData X ON X.id = T.TagId AND X.value = T.Value 
WHERE NOT EXISTS (SELECT 1 FROM Documents dt2 
        CROSS JOIN Tags t2 
        LEFT JOIN DocumentTags dt3 
        ON t2.TagId = dt3.TagId 
        AND dt2.DocId = dt3.DocId 
        WHERE dt3.DocTagId IS NULL 
        AND dt2.DocId = D.DocId) 

SQL Fiddle.

相關問題