2013-05-13 39 views
6

Tôi có một bảng tài liệu và một bảng thẻ. Các tài liệu được gắn thẻ với các giá trị khác nhau.Chọn từ Bảng A nơi nó tham gia vào tất cả dữ liệu trong Bảng B

Tôi đang cố gắng tạo một tìm kiếm các thẻ này và phần lớn thẻ đang hoạt động. Tuy nhiên, tôi nhận được kết quả bổ sung khi nó khớp với bất kỳ thẻ nào. Tôi chỉ muốn kết quả mà nó phù hợp với tất cả các thẻ.

Tôi đã tạo ra này để minh họa cho vấn đề http://sqlfiddle.com/#!3/8b98e/11

Bàn dữ liệu:

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'); 

Code:

-- 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 

(Lưu ý tôi không phải là một DBA, vì vậy có thể có cách làm tốt hơn, hy vọng tôi đi đúng hướng, nhưng tôi m mở cho các đề xuất khác nếu việc triển khai của tôi có thể được cải thiện.)

Có ai có thể đưa ra bất kỳ đề xuất nào về cách chỉ nhận được kết quả có tất cả các thẻ không?

Rất cám ơn.

+0

Nếu bạn có mã ứng dụng như coldfusion, .net, php, v.v., sẽ dễ sử dụng hơn. –

+0

Vấn đề này được gọi là phân chia quan hệ. –

Trả lời

2

Tùy chọn sử dụng với các nhà điều hành [NOT] EXISTSEXCEPT trong truy vấn cuối cùng

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 
       ) 

Demo trên SQLFiddle

HOẶC

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 
      ) 

Demo trên SQLFiddle

HOẶC

Ngoài ra bạn có thể sử dụng một giải pháp đơn giản với các phương pháp XQuery: nodes(), value()) và CTE/Truy vấn phụ.

-- 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       
        ) 

Demo trên SQLFiddle

HOẶC

-- 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      
        ) 

Demo trên SQLFiddle

Để cải thiện hiệu suất (buộc hoạt động của chỉ mục tìm kiếm trên các Tags và bảng DocumentTags), chỉ số sử dụng và gợi ý bảng (gợi ý FORCESEEK được thêm vào truy vấn ở trên):

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

Hoàn hảo! Cảm ơn nhiều. – Yetiish

+1

Bạn được chào đón;) Trong phản hồi đã thêm một giải pháp mới với các phương pháp XQuery. Hãy thử nó quá. –

+0

Có vẻ thanh lịch! trims xuống trên rất nhiều mã hóa! Cảm ơn – Yetiish

0

Tôi không thực sự chắc chắn về cú pháp cho SQL Server, nhưng tôi đoán điều gì đó như thế này nên làm việc

0

Thêm một mệnh đề where để kiểm tra một không tồn tại có điều kiện:

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.

Các vấn đề liên quan