我正在尝试编写一个SQL语句,允许我根据关键字从表中选择一系列文章。到目前为止,我得到的是一个令牌表,一个文章表,以及一个用于令牌和表格的多对多表格。文章:
tokens
rowid
token
token_article
token_rowid
article_rowid
articles
rowid
我正在做的是搜索查询,按空格拆分,然后选择包含这些关键字的所有文章。到目前为止,我已经想出了这个:
select * from
(select * from tokens
inner join token_article on
tokens.rowid = token_article.token_rowid and
token = 'ABC'
) as t1,
(select * from tokens
inner join token_article on
tokens.rowid = token_article.token_rowid and
token = 'DEF'
) as t2
where t1.article_rowid = t2.article_rowid and t2.article_rowid = articles.rowid
哪个有效,但当然它会对所有匹配ABC的文章和DEF然后选择它们的所有文章进行选择。
现在我想找出一个更好的方法。在我看来,我想象的是选择所有与ABC匹配的文章,以及那些与DEF匹配的文章。这是我想象它看起来像但不起作用(收到错误消息“没有这样的列:tokens.rowid”)
select * from
(select * from
(select * from tokens
inner join token_article on
tokens.rowid = token_article.token_rowid and
token = 'ABC'
)
inner join token_article on
tokens.rowid = token_article.token_rowid and
token = 'DEF'
)
答案 0 :(得分:2)
因为有多种方法可以执行此操作...此方法使用GROUP BY和HAVING子句。查询正在查找具有ABC或DEF标记的所有文章,然后按文章ID进行分组,其中文章的标记数等于要查询的标记数。
请注意,我在这里使用了MSSQL语法,但这个概念应该适用于大多数SQL实现。
编辑:我应该指出,当您向查询添加更多令牌时,它具有相当干净的语法。如果您添加更多令牌,则只需修改t.token_in
条件并相应地调整HAVING COUNT(*) = x
子句。
DECLARE @tokens TABLE
(
rowid INT NOT NULL,
token VARCHAR(255) NOT NULL
)
DECLARE @articles TABLE
(
rowid INT NOT NULL,
title VARCHAR(255) NOT NULL
)
DECLARE @token_article TABLE
(
token_rowid INT NOT NULL,
article_rowid INT NOT NULL
)
INSERT INTO @tokens VALUES (1, 'ABC'), (2, 'DEF')
INSERT INTO @articles VALUES (1, 'This is article 1.'), (2, 'This is article 2.'), (3, 'This is article 3.'), (4, 'This is article 4.'), (5, 'This is article 5.'), (6, 'This is article 6.')
INSERT INTO @token_article VALUES (1, 1), (2, 1), (1, 2), (2, 3), (1, 4), (2, 4), (1, 5), (1, 6)
-- Get the article IDs that have all of the tokens
-- Use this if you just want the IDs
SELECT a.rowid FROM @articles a
INNER JOIN @token_article ta ON a.rowid = ta.article_rowid
INNER JOIN @tokens t ON ta.token_rowid = t.rowid
WHERE t.token IN ('ABC', 'DEF')
GROUP BY a.rowid
HAVING COUNT(*) = 2 -- This should match the number of tokens
rowid
-----------
1
4
-- Get the articles themselves
-- Use this if you want the articles
SELECT * FROM @articles WHERE rowid IN (
SELECT a.rowid FROM @articles a
INNER JOIN @token_article ta ON a.rowid = ta.article_rowid
INNER JOIN @tokens t ON ta.token_rowid = t.rowid
WHERE t.token IN ('ABC', 'DEF')
GROUP BY a.rowid
HAVING COUNT(*) = 2 -- This should match the number of tokens
)
rowid title
----------- ------------------
1 This is article 1.
4 This is article 4.
答案 1 :(得分:1)
这是一种方法。该脚本在SQL Server 2012数据库中进行了测试。
脚本:
CREATE TABLE dbo.tokens
(
rowid INT NOT NULL IDENTITY
, token VARCHAR(10) NOT NULL
);
CREATE TABLE dbo.articles
(
rowid INT NOT NULL IDENTITY
, name VARCHAR(10) NOT NULL
);
CREATE TABLE dbo.token_article
(
token_rowid INT NOT NULL
, article_rowid INT NOT NULL
);
INSERT INTO dbo.tokens (token) VALUES
('ABC'),
('DEF');
INSERT INTO dbo.articles (name) VALUES
('Article 1'),
('Article 2'),
('Article 3');
INSERT INTO dbo.token_article (token_rowid, article_rowid) VALUES
(1, 2),
(2, 3),
(1, 3),
(1, 1),
(2, 2);
SELECT out1.rowid
, out1.token
, out1.token_rowid
, out1.article_rowid
, ta2.token_rowid
, ta2.article_rowid
, t2.rowid
, t2.token
FROM
(
SELECT t.rowid
, t.token
, ta1.token_rowid
, ta1.article_rowid
FROM dbo.tokens t
INNER JOIN dbo.token_article ta1
ON ta1.token_rowid = t.rowid
WHERE t.token = 'ABC'
) out1
INNER JOIN dbo.token_article ta2
ON ta2.article_rowid = out1.article_rowid
INNER JOIN dbo.tokens t2
ON t2.rowid = ta2.token_rowid
AND t2.token = 'DEF';
输出:
rowid token token_rowid article_rowid token_rowid article_rowid rowid token
----- ----- ----------- ------------- ----------- ------------- ----- -----
1 ABC 1 2 2 2 2 DEF
1 ABC 1 3 2 3 2 DEF