2013-08-16 13 views
8

Ja próbuje wykonać usługę Kalendarz, w tym usługi kalendarza, istnieją zdarzenia i zdarzenia mogą być oznaczone z metadanych, które można przeszukiwać.SQL SELECT gdzie wartość tag LIKE

Chcę, aby móc wyszukiwać rekordy gdzie wszystkie tagi muszą istnieje (Obowiązkowe Tagi) i/lub gdzie istnieją jakieś znaczniki (tagi opcjonalne).

udało mi się utworzyć kwerendę gdzie to działa, gdy wartość dopasowania tag „dokładnie”. Ale nie mogę się dowiedzieć, jak zwracać wyniki, gdy wartość tagu jest LIKE "% value%".

Oto moja obecna implementacja

tabele i dane

CREATE TABLE Events 
(
Id INT, 
EventText VARCHAR(500) 
); 

CREATE TABLE EventDates 
(
Id INT, 
EventId INT, 
StartDate DATETIME, 
EndDate DATETIME, 
Archived BIT 
); 

CREATE TABLE Tags 
(
Id INT, 
Description VARCHAR(50) 
); 

CREATE TABLE EventTags 
(
EventId INT, 
TagId INT, 
Value VARCHAR(50) 
); 

INSERT INTO Events VALUES (1, 'Event Name 1'); 
INSERT INTO Events VALUES (2, 'Event Name 2'); 

INSERT INTO EventDates VALUES (1, 1, '2013-01-01', '2013-01-02', 0); 
INSERT INTO EventDates VALUES (2, 1, '2013-01-07', '2013-01-08', 0); 
INSERT INTO EventDates VALUES (3, 2, '2013-01-02', '2013-01-03', 0); 

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

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

Zapytanie

DECLARE @MandatoryTagXml XML 
DECLARE @OptionalTagXml XML 
DECLARE @StartDate DATETIME 
DECLARE @EndDate DATETIME 
DECLARE @SearchTypeId SMALLINT 

SET @StartDate = '2013-01-01' 
SET @EndDate = '2013-01-31' 
SET @SearchTypeId = 1 

-- Tags that it must match all of 
SET @MandatoryTagXml = '<tags> 
          <tag> 
          <description>Tag Name 1</description> 
          <value>Value 1</value> 
          </tag> 
         </tags>' 

-- Tags that it can match one or more of 
SET @OptionalTagXml = '<tags> 
          <tag> 
          <description>Tag Name 2</description> 
          <value>Value 2</value> 
          </tag> 
         </tags>' 

    DECLARE @MandatoryIdTable TABLE ([EventId] BIGINT, [EventDateId] BIGINT) 
    DECLARE @OptionalIdTable TABLE ([EventId] BIGINT, [EventDateId] BIGINT) 

IF(@MandatoryTagXml IS NOT NULL) 
BEGIN 
    -- Select ids with matching mandatory tags. 
    ;WITH MandatoryTags AS 
    (
     SELECT TagValue.value('(./value)[1]', 'nvarchar(100)') AS value, 
      TagValue.value('(./description)[1]', 'nvarchar(100)') AS [description]  
     FROM @MandatoryTagXml.nodes('/tags/tag') AS T(TagValue) 
    ) 

    INSERT INTO @MandatoryIdTable 
    -- Records where ALL tags match EXACTLY 
    SELECT E.Id [EventId], ED.Id [EventDateId] 
    FROM [dbo].[Events] E 
    INNER JOIN [dbo].[EventDates] ED ON ED.EventId = E.Id 
    WHERE ED.StartDate >= @StartDate 
    AND ED.EndDate <= @EndDate 
    AND ED.Archived = 0 
    AND NOT EXISTS (
        SELECT T.Id, c.value 
        FROM MandatoryTags c JOIN Tags T 
         ON c.[description] = T.[Description] 
        EXCEPT 
        SELECT T.TagId, T.Value 
        FROM [EventTags] T 
        WHERE T.EventId = E.Id       
        ) 
END 
ELSE -- Select All records 
BEGIN 
    INSERT INTO @MandatoryIdTable 
    -- Records where ALL tags match EXACTLY 
    SELECT E.Id [EventId], ED.Id [EventDateId] 
    FROM [dbo].[Events] E 
    INNER JOIN [dbo].[EventDates] ED ON ED.EventId = E.Id 
    WHERE ED.StartDate >= @StartDate 
    AND ED.EndDate <= @EndDate 
    AND ED.Archived = 0 
END 

    ;WITH OptionalTags AS 
    (
     SELECT TagValue.value('(./value)[1]', 'nvarchar(100)') AS value, 
      TagValue.value('(./description)[1]', 'nvarchar(100)') AS [description]  
     FROM @OptionalTagXml.nodes('/tags/tag') AS T(TagValue) 
    ) 

    INSERT INTO @OptionalIdTable 
    -- Records ANY tags match EXACTLY 
    SELECT E.Id [EventId], ED.Id [EventDateId] 
    FROM [dbo].[Events] E 
    INNER JOIN [dbo].[EventDates] ED ON ED.EventId = E.Id 
    WHERE ED.StartDate >= @StartDate 
    AND ED.EndDate <= @EndDate 
    AND ED.Archived = 0 
    AND EXISTS (
       SELECT T.Id, c.value 
       FROM OptionalTags c JOIN Tags T 
        ON c.[description] = T.[Description] 
       INTERSECT 
       SELECT T.TagId, T.Value 
       FROM [EventTags] T 
       WHERE T.EventId = E.Id       
       ) 

-- Determine if we need to factor in optional tags in result set 
IF (@OptionalTagXml IS NOT NULL) 
BEGIN 
    -- Select results that exist in both optional and mandatory tables 
    SELECT DISTINCT M.* 
    FROM @MandatoryIdTable M 
    INNER JOIN @OptionalIdTable O ON O.EventId = M.EventId AND O.EventDateId = M.EventDateId 
END 
ELSE 
BEGIN 
    -- Select results that exist in mandatory table 
    SELECT DISTINCT M.* 
    FROM @MandatoryIdTable M 
END 

I stworzyli SQLFiddle Demo dla niego.

Mój pomysł jest użycie @SearchTypeId aby przełączyć między dokładnym poszukiwaniu meczu i jak szukanie meczu.

(Uwaga Nie jestem DBA, więc nie może być lepsze sposoby, aby to zrobić. Jestem otwarty na sugestie)

Czy ktoś może zaoferować sugestie, w jaki sposób dostać się jak zapałki na wartości bramek?

Dziękujemy

+3

+1 za wersję demonstracyjną SQL Fiddle. Każde pytanie SQL powinno to mieć! –

Odpowiedz

0

chucknelson dał mi prod co potrzebne do wykrycia, co potrzebne do zrobienia.

Dodanie tego odcinka dostaje takiego meczu na wartość:

JOIN EventTags ET 
      ON C.[Value] LIKE '%' + ET.Value + '%' 

So, for example, the mandatory section becomes: 

    -- Select ids with matching mandatory tags. 
    ;WITH MandatoryTags AS 
    (
     SELECT TagValue.value('(./value)[1]', 'nvarchar(100)') AS value, 
      TagValue.value('(./description)[1]', 'nvarchar(100)') AS [description]  
     FROM @MandatoryTagXml.nodes('/tags/tag') AS T(TagValue) 
    ) 

    INSERT INTO @MandatoryIdTable 
    -- Records where ALL tags match EXACTLY 
    SELECT E.Id [EventId], ED.Id [EventDateId] 
    FROM [dbo].[Events] E 
    INNER JOIN [dbo].[EventDates] ED ON ED.EventId = E.Id 
    WHERE ED.StartDate >= @StartDate 
    AND ED.EndDate <= @EndDate 
    AND ED.Archived = 0 
    AND NOT EXISTS (
        SELECT T.Id, c.value 
        FROM MandatoryTags c 
        JOIN Tags T 
         ON c.[description] = T.[Description] 
-- Add LIKE match on value 
        JOIN EventTags ET 
         ON C.[Value] LIKE '%' + ET.Value + '%' 
        EXCEPT 
        SELECT T.TagId, T.Value 
        FROM [EventTags] T 
        WHERE T.EventId = E.Id 
        ) 

Pozwala mi to zrobić LIKE mecz, a przy użyciu parametru @SearchType mogę uruchamiać oryginalną zapytanie, czy ten zmieniony jeden odpowiednio.

1

Myślę, że pomysł wykorzystania pewnego rodzaju flagi/wyłącznik, aby zmienić typ dopasowania zadziała. Zaimplementowałem go za pomocą słów zamiast identyfikatorów, ale jeśli po prostu przełączysz warunek łączenia na podstawie typu wyszukiwania, powinieneś uzyskać LIKE dopasowania zgodnie z oczekiwaniami.

Fiddle: http://sqlfiddle.com/#!3/d9fbd/3/0

raz pierwszy dodany znacznik, który był podobny do znacznika 1 i przymocowany do przypadku 2 do testów.

INSERT INTO Tags VALUES (3, 'Different Tag Name 1'); 
INSERT INTO EventTags VALUES (2, 3, 'Value 3'); 

Następnie utworzyłem flagę/przełącznik typu wyszukiwania.

DECLARE @SearchType NVARCHAR(10) 
SET @SearchType = 'LIKE' --other type is EXACT 

Teraz można przełączać warunek łączyć EXISTS na podstawie tej flagi. Zmieniłem twoich NOT EXISTS na EXISTS tylko dla mojego zrozumienia. Poniżej znajduje się nowy warunek łączenia, przy użyciu obowiązkowego bloku bloków jako przykładu.

-- Select ids with matching mandatory tags. 
;WITH MandatoryTags AS 
(
    SELECT TagValue.value('(./value)[1]', 'nvarchar(100)') AS value, 
     TagValue.value('(./description)[1]', 'nvarchar(100)') AS [description]  
    FROM @MandatoryTagXml.nodes('/tags/tag') AS T(TagValue) 
) 

INSERT INTO @MandatoryIdTable 
-- Records where ALL tags match EXACTLY or LIKE 
SELECT E.Id [EventId], ED.Id [EventDateId] 
FROM [dbo].[Events] E 
INNER JOIN [dbo].[EventDates] ED ON ED.EventId = E.Id 
WHERE ED.StartDate >= @StartDate 
AND ED.EndDate <= @EndDate 
AND ED.Archived = 0 
AND EXISTS (
       -- Just care about tag IDs here, not the values 
       SELECT T.Id 
       FROM MandatoryTags c JOIN Tags T 
        ON (
         -- Toggle join type based on flag/switch 
         (@SearchType = 'EXACT' AND c.[description] = T.[Description]) 
         OR 
         (@SearchType = 'LIKE' AND T.[Description] LIKE ('%' + c.[description] + '%')) 
        ) 
       INTERSECT 
       SELECT T.TagId 
       FROM [EventTags] T 
       WHERE T.EventId = E.Id       
       ) 

Jestem pewien, że istnieje jakiś re-factoring i optymalizacji można zrobić w tym SQL, ale powinno to przynajmniej daje jeden pomysł, w jaki sposób lubisz dopasowania razie potrzeby. Mam nadzieję, że to pomoże!

+0

Dzięki za informację zwrotną, jest to fajny pomysł, ale robi to raczej dopasowanie w opisie znacznika, niż wartość. Próbowałem spojrzeć na podobną implementację przy użyciu wartości znacznika, ale niestety nie widzę, jak to by działało. – Yetiish