6

考虑下表:

[Table: talks]
talkID | title        | starred
-------+--------------+--------
1      | talk1-title  | 1
2      | talk2-title  | 1
3      | talk3-title  | 0
4      | talk4-title  | 0
5      | talk5-title  | 0

[Table: talkspeaker]
talkID | speaker
-------+---------
1      | Speaker1
1      | Speaker2
2      | Speaker3
3      | Speaker4
3      | Speaker5
4      | Speaker6
5      | Speaker7
5      | Speaker8

[Table: similartalks]
talkID | similarTo
-------+----------
1      | 3
1      | 4
2      | 3
2      | 4
2      | 5
3      | 2
4      | 5
5      | 3
5      | 4

我想要做的是:给定已加星标的演讲集,我想选择未加星标的演讲中的前 2 个(已加星标 = 0)及其与加星标演讲集最相似的标题和演讲者。问题是获得演讲者需要使用聚合函数,获得最相似的演讲也是如此。

在没有发言者的情况下,我可以使用以下查询获得最相似的谈话:

select t2.talkID, t2.title, count(*) as count 
from similarTalks s, talks t1, talks t2
where s.talkID = t1.talkID
and t1.Starred = 1
and s.similarTo = t2.TalkID
and t2.Starred = 0
group by t2.title, t2.talkID
order by count desc
limit 2

通常,我使用以下聚合函数来获取扬声器,并按列进行适当的分组(假设 t =talkpeaker):

group_concat(t.speaker, ', ') as Speakers

如在

select t1.title, group_concat(t2.speaker, ', ') as Speakers 
from talks t1, talkspeaker t2
where t1.talkID = t2.talkID
group by t1.title

但我无法将这两件事结合在一起。我计划在 sqlite 数据库中运行这个查询可能很重要(这就是 group_concat 函数的来源)。与已加星标的对话最相似的前 2 个未加星标的对话的答案似乎与 talkID 3 和 4 相关。

4

2 回答 2

6

首先,您可能想阅读这篇文章,了解使用 ANSI 92 连接而不是上面使用的旧 ANSI 89 的原因。其次,SQLLite 确实支持 GROUP_CONCAT 函数,因此您可以使用它。

您只需将第二个查询作为子查询添加到第一个查询中即可获得所需的结果:

SELECT  Talks.TalkID, 
        Talks.Title, 
        ts.Speakers, 
        COUNT(*) AS SimilarTalks
FROM    Talks
        INNER JOIN SimilarTalks 
            ON Talks.TalkID = SimilarTalks.SimilarTo
        INNER JOIN Talks t2
            ON SimilarTalks.TalkID = t2.TalkID
            AND t2.Starred = 1
        INNER JOIN
        (   SELECT  TalkID, GROUP_CONCAT(Speaker, ',') AS Speakers
            FROM    TalkSpeaker
            GROUP BY TalkID
        ) ts
            ON ts.TalkID = Talks.TalkID
WHERE   Talks.Starred = 0
GROUP BY Talks.TalkID, Talks.Title, ts.Speakers
ORDER BY COUNT(*) DESC
LIMIT 2;

SQL Fiddle 示例

编辑

您也可以在没有子查询的情况下使用DISTINCT

SELECT  Talks.TalkID, 
        Talks.Title, 
        GROUP_CONCAT(DISTINCT ts.Speaker) AS Speakers,
        COUNT(DISTINCT t2.TalkID) AS SimilarTalks
FROM    Talks
        INNER JOIN SimilarTalks 
            ON Talks.TalkID = SimilarTalks.SimilarTo
        INNER JOIN Talks t2
            ON SimilarTalks.TalkID = t2.TalkID
            AND t2.Starred = 1
        INNER JOIN TalkSpeaker ts
            ON ts.TalkID = Talks.TalkID
WHERE   Talks.Starred = 0
GROUP BY Talks.TalkID, Talks.Title
ORDER BY COUNT(DISTINCT t2.TalkID) DESC
LIMIT 2;

但是我认为这种方法没有任何好处,而且效率可能较低(我没有测试过,所以不能确定)

于 2013-01-23T08:54:10.653 回答
3

首先,要仅获取所需谈话的 ID,请从您的第一个查询中删除其他字段:

SELECT unstarred.talkID
FROM talks AS starred
  JOIN similarTalks AS s ON starred.talkID = s.talkID
  JOIN talks AS unstarred ON s.similarTo = unstarred.talkID
WHERE starred.starred
  AND NOT unstarred.starred
GROUP BY unstarred.talkID
ORDER BY COUNT(*) DESC
LIMIT 2

然后,将其用作子查询以获取有关所需谈话的信息:

SELECT t.title AS Title,
       group_concat(s.speaker, ', ') AS Speakers
FROM talks AS t JOIN talkspeaker AS s ON t.talkID = s.talkID
WHERE t.talkID IN (SELECT unstarred.talkID
                   FROM talks AS starred
                     JOIN similarTalks AS s ON starred.talkID = s.talkID
                     JOIN talks AS unstarred ON s.similarTo = unstarred.talkID
                   WHERE starred.starred
                     AND NOT unstarred.starred
                   GROUP BY unstarred.talkID
                   ORDER BY COUNT(*) DESC
                   LIMIT 2)
GROUP BY t.talkID
于 2013-01-23T08:46:53.453 回答