2013-05-13 19 views
6

Bir belge tablom ve bir etiket tablosu var. Belgeler çeşitli değerlerle etiketlenmiştir.Tablo B'den seçerek Tablo B'deki tüm verilere katıldıkları yer: Tablo B

Bu etiketlerin bir listesini oluşturmaya çalışıyorum ve çoğunlukla çalışıyor. Ancak, herhangi bir etiketle eşleştiğinde fazladan sonuç alınıyorum. Sadece tüm etiketlerle eşleştiği sonuçları istiyorum.

Sorunu http://sqlfiddle.com/#!3/8b98e/11

Tablolar ve Veri göstermek için bu yarattık: böylece,

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

(Ben DBA değilim Not:

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

Kodu İşleri yapmanın daha iyi yolları olabilir. İnşallah doğru yoldayım ama Uygulamanızın iyileştirilebilmesi için diğer önerilere açık olun.)

Yalnızca, tüm etiketlere sahip sonuçların nasıl elde edileceği konusunda herhangi bir öneride bulunabilir mi?

Çok teşekkürler.

+0

Coldfusion, .net, php, vb. Gibi bir uygulama kodunuz varsa, bunu kullanmak daha kolay olacaktır. –

+0

Bu sorun ilişkisel bölünme olarak adlandırılır. –

cevap

2

Son sorgudaki seçeneği [NOT] EXISTS ve EXCEPT işleçleri ile kullanın

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

VEYA

Ayrıca basit bir XQuery yöntemlerle çözümü kullanabilirsiniz SQLFiddle

VEYA

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 üzerine Demo: nodes(), value()) ve CTE/Alt sorgu.

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

performansını artırma amacıyla SQLFiddle

VEYA

-- 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 üzerinde SQLFiddle

üzerine Demo kullanım endekslerinin (endeksinin zorla çalıştırma Etiketler ve DocumentTags tabloları aramak) ve tablo ipuçları (yukarıdaki sorguya FORCESEEK ipucu eklenmiştir):

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

Mükemmel! Çok teşekkür ederim. – Yetiish

+1

Rica ederim;) Yanıt olarak XQuery yöntemleri ile yeni bir çözüm eklendi. Sen de dene. –

+0

Zarif görünüyor! kodlamanın çok üzerinde düzeltir! Teşekkürler – Yetiish

0

Ben SQL Server için sözdizimi gerçekten emin değilim, ama böyle bir şey yantümce değil var kontrol etmek nerede

0

bir ekleme çalışması gerekir tahmin koşullu:

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.