2016-02-13 4 views
4

Я пытаюсь сделать что-то похожее на столбец, пересекающийся на двух таблицах. Таблица:Столбец пересекается на двух таблицах

  • LogTag: журнал может иметь ноль или несколько тегов
  • MatchingRule: правило соответствия состоит из одного или нескольких тегов, которые определяют правила

Журнал может иметь ноль или больше правил, соответствующих ему. Я буду проходить в MatchingRuleID и ожидаю вернуть все журналы, соответствующие этому правилу.

Ожидаемый результат: Итоговый набор совпадений LogID s. Например. переходя в MatchingRuleID = 30 должен возвращать LogID 101. MatchingRuleID = 31 должен возвращать 100.

Кроме того, LogTag таблица может иметь миллионы строк так, предпочтительным является эффективным запрос.

Вопрос: Как найти все LogID s, которые соответствуют указанному правилу определения?

enter image description here

Схема:

CREATE TABLE dbo.Tag 
(
    TagID INT, 
    TagName NVARCHAR(50) 
) 
INSERT INTO dbo.Tag (TagID, TagName) 
VALUES (1, 'tag1'), (2, 'tag2'), (3, 'tag3') 

CREATE TABLE dbo.LogTag 
(
    LogID INT, 
    TagID INT 
) 
INSERT INTO dbo.LogTag (LogID, TagID) 
VALUES (100, 1), (101, 1), (101, 2), (101, 3), (101, 4), (102, 2), (102, 3) 

CREATE TABLE dbo.MatchingRule 
(
    MatchingRuleID INT, 
    TagID INT 
) 
INSERT INTO dbo.MatchingRule (MatchingRuleID, TagID) 
VALUES (30, 1), (30, 2), (30, 3), (31, 1) 
+0

, что является ожидаемым результат? – Squirrel

+0

@Squirrel, редактирование сделано для уточнения – g2server

+0

где ожидаемый результат? –

ответ

2

важно иметь правильный кластерный индекс по таблицам. Я добавил альтернативный индекс в комментарии для #log_tag, что может повысить производительность для больших наборов. Поскольку у меня нет надлежащего образца для тестирования, вам нужно будет проверить, что лучше.

CREATE TABLE #tag(tag_id INT PRIMARY KEY,tag_name NVARCHAR(50)); 
INSERT INTO #tag (tag_id,tag_name)VALUES 
    (1,'tag1'),(2,'tag2'),(3,'tag3'); 

-- Try this key for large sets: PRIMARY KEY(tag_id,log_id)); 
CREATE TABLE #log_tag(log_id INT,tag_id INT,PRIMARY KEY(log_id,tag_id)) 
INSERT INTO #log_tag (log_id,tag_id)VALUES 
    (100,1),(101,1),(101,2),(101,3),(101,4),(102,2),(102,3); 

CREATE TABLE #matching_rule(matching_rule_id INT,tag_id INT,PRIMARY KEY(matching_rule_id,tag_id)); 
INSERT INTO #matching_rule(matching_rule_id,tag_id)VALUES 
    (30,1),(30,2),(30,3),(31,1); 

DECLARE @matching_rule_id INT=31; 

;WITH required_tags AS (
    SELECT tag_id 
    FROM #matching_rule 
    WHERE [email protected]_rule_id 
) 
SELECT lt.log_id 
FROM required_tags AS rt 
    INNER JOIN #log_tag AS lt ON 
     lt.tag_id=rt.tag_id 
GROUP BY lt.log_id 
HAVING COUNT(*)=(SELECT COUNT(*) FROM required_tags); 

DROP TABLE #log_tag; 
DROP TABLE #matching_rule; 
DROP TABLE #tag; 

Результатов являются те, в вашем ожидаемого результата как для 30 & 31.

плана выполнения для индекса, используемого в сценарии:

Execution plan for index used in script

1

Попробуйте этот запрос

Fiddle Here

DECLARE @InputMatchingRuleId INT = 30 
;WITH CTE1 
AS 
(
    SELECT DENSE_RANK() OVER(ORDER BY LT.TAGID) AS RN,LT.TagID,LT.LOGID 
    FROM MatchingRule MR INNER JOIN LogTag LT ON LT.TagID = MR.TagID 
    WHERE [email protected] 

), 
CTE2 
AS 
(
    SELECT 1 AS RN2,LOGID FROM CTE1 C1 WHERE C1.RN=1 
    UNION ALL 
    SELECT RN2+1 as RN2,C2.LOGID 
    FROM CTE1 C1 INNER JOIN CTE2 C2 ON C1.RN = C2.RN2+1 AND C1.LOGID = C2.LOGID 
) 

    SELECT DISTINCT LOGID FROM CTE2 
    WHERE RN2>(CASE WHEN (SELECT MAX(RN2) FROM CTE2)=1 THEN 0 ELSE 1 END) 
1

Примечание: Это будет работать только с SQL Server 2 008+

Вот запрос я придумал:

DECLARE @RuleID INT 
SELECT @RuleID = 30 

SELECT LogID 
FROM LogTag lt 
    INNER JOIN (
     SELECT TagID, MatchingRuleID, COUNT(*) OVER (PARTITION BY MatchingRuleID) TagCount 
     FROM MatchingRule 
    ) mr 
    ON lt.TagID = mr.TagID 
     AND mr.MatchingRuleID = @RuleID 
GROUP BY LogID, TagCount 
HAVING COUNT(*) = TagCount 

Поэтому в основном я сопрягать все TagID «S в течение указанного правила сопоставления, а затем, как только я знаю, что все теги совпадают я проверяю, если количество тегов из таблицы MatchingRule соответствует количеству (теперь отфильтровано и сгруппировано) меток из таблицы LogTag.

1

должен быть

; with rules as 
(
    select TagID, cnt = sum(count(*)) over() 
    from dbo.MatchingRule 
    where MatchingRuleID = @MatchingRuleID 
    group by TagID 
) 
select LogID 
from rules r 
    inner join LogTag lt on r.TagID = lt.TagID 
group by LogID, cnt 
having count(*) = r.cnt 
0
select l.LogID 
from dbo.MatchingRule r 
inner join dbo.LogTag l on l.TagID = r.TagID 
where r.MatchingRuleID = 31 

другой подход, чтобы идентифицировать все теги, а затем:

select l.LogID 
from dbo.LogTag l 
where exists(select 1 from @Tags t where t.TagID = l.TagID) 
Смежные вопросы