子查询返回多行问题

时间:2012-06-10 11:35:22

标签: mysql

再一次关于使用我的小型数据库进行mysql的问题:

我得到了如下表格:

Songs       Link        Tags
=======     =====       =======
Sid          Sid        Tid
Songname     Tid        Tagname

现在我想要做的是,一旦我通过输入一些标签查询(或搜索)一首歌曲,就可以显示该歌曲创建匹配百分比的标签数量。

感谢非常棒的stackoverflow社区,我得到了查询

SELECT s.Sid, s.Songname
FROM Songs s
JOIN Link l ON ( l.Sid = s.Sid )
JOIN Tags t ON ( t.Tid = s.Tid )
WHERE t.Tagname IN ( 'X', 'Y' )
GROUP BY s.Sid, s.Songname
HAVING COUNT(1) = 2

其中搜索与这两个标签完全匹配的歌曲。
假设你现在有了这首歌有一个标签G,X,Y和Z的歌 查询找到这首歌,因为它中有X和Y. 然而这首歌得到了另外两首,因此我想创建(用php)一个匹配%,显示这一个的50%。

我对php部分没有任何问题,但是我无法找出一个查询来获取与这些标签匹配的所有歌曲以及它们在一个结果集中的总标签数量。

干杯!

EDIT1:

<小时/> 我注意到大多数答案显示了我已经尝试过的内容。所以我将再做一个更深刻的例子:
假设您有以下4首歌曲:

A带有标签A,B,X和Y
B带有标签A,B,C,D,E,X,Y
C带有标签A,B,C,D,F,X
D带有标签X和Y

现在输入搜索带有标签X和Y的歌曲的查询。我想要以下输出:

TotalNumberOfTags:    Song:
4                      A
7                      B
2                      D

C没有X和Y所以他失败了。

EDIT2:

<小时/> 为了说明我想使用查询

SELECT COUNT(t.Tagname) FROM FROM Songs s 
JOIN Link l ON ( l.Sid = s.Sid ) 
JOIN Tags t ON ( t.Tid = s.Tid ) 
WHERE s.Songname="A"

在由上述查询引起的整个返回的歌曲集上,在一个查询中使用ORDER BY COUNT(t.Tagname)ASC。

3 个答案:

答案 0 :(得分:0)

我认为你也可以通过计算每组中的记录数(即每首歌曲)来计算...

SELECT s.Sid, s.Songname, COUNT(t.Tagname)
FROM Songs s
JOIN Link l ON ( l.Sid = s.Sid )
JOIN Tags t ON ( t.Tid = s.Tid ) AND t.Tagname IN ( 'X', 'Y' )
GROUP BY s.Sid, s.Songname;

此外,使用标记名发送单个字符串然后使用Find_In_Set

可能更容易
SELECT s.Sid, s.Songname, COUNT(t.Tagname)
FROM Songs s
JOIN Link l ON ( l.Sid = s.Sid )
JOIN Tags t ON ( t.Tid = s.Tid ) AND FIND_IN_SET(t.Tagname,"G,X,Y,Z")>0
GROUP BY s.Sid, s.Songname;

我显然不明白,因为我认为搜索中使用的标记名列表是动态元素。我相信您可以使用内部查询来获取符合搜索条件的歌曲(例如“X”,“Y”),然后对歌曲使用外部查询来获取每个标签的数量

select COUNT(to.Tagname), so.Songname FROM FROM Songs so 
JOIN Link lo ON ( lo.Sid = so.Sid ) 
JOIN Tags to ON ( to.Tid = so.Tid ) 
WHERE so.SongName in
   ( SELECT si.SongName
      FROM Songs si
      JOIN Link li ON ( li.Sid = si.Sid )
      JOIN Tags ti ON ( ti.Tid = si.Tid )
      WHERE ti.Tagname IN ( 'X', 'Y' )
      GROUP BY si.Sid, si.Songname
      HAVING COUNT(1) = 2
   )
order by COUNT(to.Tagname);

答案 1 :(得分:0)

删除HAVING子句 - 不要认为是必需的。在SELECT语句中添加COUNT(l.Sid) AS MatchedTags

SELECT s.Sid, s.Songname, COUNT(l.Sid) AS MatchedTags
FROM Songs s
JOIN Link l ON ( l.Sid = s.Sid )
JOIN Tags t ON ( t.Tid = s.Tid )
WHERE t.Tagname IN ( 'X', 'Y' )
GROUP BY s.Sid, s.Songname
HAVING COUNT(1) = 2

此外 - 这取决于个人观点,但作为一般规则,我将多个关系中的“链接”表命名为所涉及的两个表名的组合。例如,您将拥有Song,Tag和中间表SongTag。

并且尽可能避免使用首字母缩略词!每个表中的主键列只能称为Id,而SongTag表(Or Link表)将具有SongId和TagId而不是sid和tid。

如果需要HAVING COUNT(1) = 2,请更改(1)以实际引用列名称以便于阅读。

糟糕的习惯,但这只是我个人的意见。

我的查询最终看起来像这样:(有2首歌曲和5个标签)。

SELECT 
    s.Id, 
    s.Name,
    COUNT(st.Id) AS MatchedTags
FROM 
    SongTag st
        INNER JOIN Song s ON st.SongId = s.Id
        INNER JOIN Tag t ON st.TagId = t.Id
WHERE 
    t.Name IN ('Tag2', 'Tag3', 'Tag4', 'Tag5')
GROUP BY
    s.Id,
    s.Name

答案 2 :(得分:0)

我找到的答案是一种解决方法,但它有效。 我创建了一个像这样的临时表

CREATE TEMPORARY TABLE numberoftags ( songname VARCHAR(50) NOT NULL, numberoftags INT);

我填写了我在问题中显示的2个单独的查询 然后使用简单的查询

SELECT * FROM numberoftags ORDER BY numberoftags DESC

我收到所有带有标签总数的歌曲,我会一个一个地显示。 在我关闭php中的连接后,这个临时表会被自动删除,所以没有麻烦。