我有精神障碍。我很确定这是一个死的简单的noob问题要解决,但我画的是空白:
我有一个文章标记系统。这是通过具有包含文章ID和标签ID的单独表来完成的,因此可以将多个标签分配给一个文章,反之亦然。这一切都运作良好。但现在我想做的是根据文章是否匹配两个或多个标签,或匹配两个或多个标签但不匹配特定标签,或匹配指定的所有标签等来过滤文章。
--------------------
|ID|ArticleID|TagID|
--------------------
|1 |4000 |123 |
|2 |4000 |3532 |
|3 |4000 |4386 |
|4 |4001 |3532 |
etc...
--------------------
所以它应该返回:
到目前为止,我的想法涉及“选择articleid,其中tagid = 123和tagid = 4386”,但很明显,tagid字段不可能是同一记录中的两件事(使用“或”会得到结果,但是它不能确保它只是匹配123和4386的东西)。接下来,我将一次查询一个条件,然后使用PHP来过滤哪些文章应该/不应该匹配,但是在我的脑海里有一种唠叨的感觉,这应该可以轻松完成在数据库级别,我只是想不出如何(或谷歌用于什么)。我希望能够一次过滤1000个标签。
答案 0 :(得分:2)
您可以为AND案例使用多个内部联接:
select distinct a.ArticleID
from Articles as a
inner join Articles AS b on a.ArticleID = b.ArticleID and b.TagID = 123
inner join Articles AS c on a.ArticleID = c.ArticleID and c.TagID = 4386;
对于OR案例,您可以执行以下操作:
select distinct ArticleID
from Articles
where TagID = 123 or TagID = 3532;
要一起完成所有这些案例,您可能最终会使用子查询:
select distinct a.ArticleID
from Articles as a
where exists (select * from Articles as b where a.ArticleID = b.ArticleID and b.TagID = 123);
然后,您可以使用常规SQL逻辑运算符(和,或,不)将多个exists
条件连接在一起。对于大量标签来说,这很可能不会特别有效。
答案 1 :(得分:1)
您有三个查询选项:
SELECT a.articleid
FROM ARTICLES a
JOIN TAGS t ON t.tagid = a.tagid
WHERE t.tagid IN (123, 4386)
GROUP BY a.articleid
HAVING COUNT(DISTINCT t.tagid) = 2
请注意,计数必须等于IN
子句中的参数数量,以及DISTINCT的使用。如果没有这个区别,那么一篇文章与同一个标签有2个关联会被视为误报。这种方法也不像IMO那样轻松地转移到动态SQL ......
SELECT a.articleid
FROM ARTICLES a
JOIN TAGS t1 ON t1.tagid = a.tagid
AND t1.tagid = 123
JOIN TAGS t2 ON t2.tagid = a.tagid
AND t2.tagid = 4386
GROUP BY a.articleid
这可能是最快的选择。
SELECT a.articleid
FROM ARTICLES a
WHERE EXISTS(SELECT NULL
FROM TAGS t
WHERE t.tagid = a.tagid
AND t.tagid = 123)
AND EXISTS(SELECT NULL
FROM TAGS t
WHERE t.tagid = a.tagid
AND t.tagid = 4386)
由于您的要求是动态的,我建议使用MySQL的准备语句:
DECLARE num INT
SET @sql = 'SELECT a.articleid FROM ARTICLES a';
WHILE num > 0
SET @sql = CONCAT(@sql, 'JOIN TAGS t', num, 'ON t', num,'.tagid = a.tagid AND t', num,'.tagid = ', tag, ' ');
SET num = num - 1;
END WHILE;
SET @sql = CONCAT(@sql, 'GROUP BY a.articleid');
PREPARE stmt FROM @sql
EXECUTE stmt
DEALLOCATE PREPARE stmt;
如果您将num
设置为零,您将收到所有文章。 WHILE
循环将追加JOIN,创建一个包含的标签列表。如果您想在同一查询中支持排除项,这足以让您入门。
答案 2 :(得分:0)
您可以使用的第二个条件:
select t1.articleid from table t1, table t2 where t1.tagid = 123 and t2.tagid = 4386
答案 3 :(得分:0)
除了此处的其他答案之外,如果您可以构建使用子查询的查询,则可以通过将逗号分隔的标记ID字符串传递给表值函数来解决SQL通常无法处理数组的问题。该函数会将字符串分解为一个可以查询的整数表。使用它,您将能够查询(max-varchar-size / max-size-of-ID-as-string)标记;通常是数千人。
请原谅MSSQL语法;我不知道mysql。我希望它有表值函数(返回表的函数)或等价函数。
-- the 'OR' query
declare @taglist varchar(8000)
set @taglist = '1,2,3,4'
SELECT DISTINCT a.ArticleID FROM Article a
JOIN Tags t ON t.ArticleID = a.ArticleID
WHERE t.TagID IN (SELECT * FROM arrToTable(tagList))
Here's这样一个函数的样本。
答案 4 :(得分:0)
SELECT DISTINCT ARTICLEID FROM myTable WHERE TagId=3532
SELECT DISTINCT ARTICLEID FROM myTable WHERE TagId=123 AND TagId=4386
SELECT DISTINCT ARTICLEID FROM myTable WHERE TagId=123 OR TagId=3532
SELECT DISTINCT ARTICLEID FROM myTable WHERE TagId=3532 AND TagId <> 123