查询具有匹配明细记录的主记录

时间:2021-05-10 07:42:00

标签: sql tsql

目前我有以下表格结构。

主表文档

<头>
ID 文件名
1 document1.pdf
2 document2.pdf
3 document3.pdf

明细表关键字

<头>
ID 文档ID 关键字
1 1 关键字A
2 1 关键字B
3 1 关键字C
4 2 关键字B
5 3 关键字A
6 3 关键字D

创建此代码的代码:

CREATE TABLE Documents (
    ID int IDENTITY(1,1) PRIMARY KEY,
    Filename nvarchar(255) NOT NULL
);

CREATE TABLE Keywords (
    ID int IDENTITY(1,1) PRIMARY KEY,
    DocumentID int NOT NULL,
    Keyword nvarchar(255) NOT NULL
);

INSERT INTO Documents(Filename) VALUES 
  ('document1.pdf'), ('document2.pdf'), ('document3.pdf');

INSERT INTO Keywords(DocumentID, Keyword) VALUES 
  (1, 'KeywordA'),
  (1, 'KeywordB'),
  (1, 'KeywordC'),
  (2, 'KeywordB'),
  (3, 'KeywordA'),
  (3, 'KeywordD');

SQL Fiddle for this

用一个关键字查找

我正在寻找一种方法来获取与某个关键字匹配的所有文档。

这可能是例如使用以下 T-SQL 查询编写:

SELECT Documents.* 
FROM Documents 
WHERE Documents.ID IN
(
  SELECT Keywords.DocumentID 
  FROM Keywords 
  WHERE Keywords.Keyword = 'KeywordA'
)

这成功了。

使用多个关键字查找

我目前遇到的问题是,我想查找与多个关键字匹配的所有文档,并结合逻辑 AND。

例如查找包含三个详细记录的文档,关键字为 A、B 和 C。

我认为以下可能有效,但我根本不知道这是否高效或优雅:

SELECT Documents.* 
FROM Documents 
WHERE Documents.ID IN
(
  SELECT Keywords.DocumentID 
  FROM Keywords
  WHERE 
    Keywords.Keyword = 'KeywordA' OR 
    Keywords.Keyword = 'KeywordB' 
  GROUP BY Keywords.DocumentID HAVING COUNT(*) = 2 
)

SQL Fiddle for that

我的问题

如何编写(高性能)SQL 查询来查找所有关联多个关键字的文档。

如果更简单,一个具有恒定关键字数量(例如 3 个)的解决方案就足够了。

2 个答案:

答案 0 :(得分:1)

希望以下查询对您有帮助

SELECT D.ID
FROM Documents D
JOIN Keywords K ON K.DocumentID = D.ID
WHERE K.Keyword IN ('KeywordA', 'KeywordB', 'KeywordC')  
GROUP BY D.ID
HAVING COUNT(DISTINCT K.Keyword) = 3 

Demo

答案 1 :(得分:1)

您尝试使用的技术称为 Relational Division With Remainder,换句话说:查找包含一组特定行的所有组。

您当前的查询是执行此操作的标准方法之一,还有其他方法。

如果您在表变量或 TVP 中有关键字,...

DECLARE @keywords AS TABLE (Keyword varchar(50));
INSERT @keywords VALUES
('KeywordA'), ('KeywordB'), ('KeywordC');

...您可以使用以下内容使其更整洁:

SELECT d.* 
FROM Documents d
WHERE d.ID IN
(
  SELECT k.DocumentID 
  FROM Keywords k
  JOIN @keywords kt ON kt.Keyword = k.Keyword
  GROUP BY k.DocumentID
  HAVING COUNT(*) = (SELECT COUNT(*) FROM @keywords)
);

另一种选择:

SELECT d.* 
FROM Documents d 
WHERE EXISTS (SELECT 1
  FROM @keywords kt
  LEFT JOIN Keywords k ON kt.Keyword = k.Keyword
      AND k.DocumentID = d.ID
  HAVING COUNT(*) = COUNT(k.keywords) -- there are no missing matches
);

另一个,有点令人困惑:

SELECT d.* 
FROM Documents d
WHERE NOT EXISTS (SELECT 1
    FROM @keywords kt
    WHERE NOT EXISTS (SELECT 1
        FROM Keywords k
        WHERE k.Keyword = kt.Keyword
        AND K.DocumentID = d.ID
    )
);
-- For each document, there are no keywords for which there is no match
相关问题