0

再次使用我的小型数据库练习有关 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

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

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

干杯!

编辑1:


我注意到大多数答案都显示了我已经尝试过的内容。所以我再做一个更深入的例子:
假设你有以下 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,所以他摔倒了。

编辑2:


为了说明我想使用查询

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 在集合上。

4

3 回答 3

0

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

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

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

SELECT * FROM numberoftags ORDER BY numberoftags DESC

我收到所有歌曲及其标签总数,我一一显示。在我关闭 php 中的连接后,这个临时表会自动被删除,所以不用担心。

于 2012-06-11T18:39:09.360 回答
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);
于 2012-06-10T12:13:01.643 回答
0

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

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

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

并尽可能避免使用首字母缩略词!每个表中的主键列只能称为Id,而SongTag表(或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
于 2012-06-10T12:14:07.223 回答