在MYSQL中从每个组中选择N行

时间:2013-12-27 01:10:27

标签: php mysql sql

我需要搜索赠送用户的朋友发送的更新。

有一个名为friendship的表格。它有一个名为profile1的列,另一个名为profile2。它代表了这个网络系统中两个用户之间的友谊,友谊是两个赠送ID的存在,无论在什么位置。因此,ID为1的个人资料可能包含2个朋友,个人资料ID为id 2,ID为3,如下所示:

friendship           
profile1 profile2            
1         2 <--                
3         1 <--        
2         5
...

现在我想搜索某些用户的朋友发送的更新。有这个表update

update
id  content  time  profile
1   A text   ...   2
2   A text   ...   2
3   A text   ...   3
4   A text   ...   2
5   A text   ...   3
6   A text   ...   2
7   A text   ...   10
8   A text   ...   11

如果我的个人资料/用户被id 1标识,并且它只有2个朋友(由id 2和3标识的个人资料),而且我需要我的搜索只返回每个用户的2个结果,我的SELECT必须返回更新1,2,3和5.

最好更新应按其作者进行分组,如果我可以设置在此搜索中要考虑的不同配置文件的数量,那将是很好的(例如,如果配置文件1有10个朋友,我只想要3个配置文件的更新,最近必须首先出现。)

你知道我怎么能做到这一点?

非常感谢你!

@EDIT

这将返回配置文件1的朋友发送的所有更新。但我不确定我是否在正确的方向

SELECT u.*
FROM  `update` u
INNER JOIN friendship f1 ON f1.profile1 = u.author
WHERE f1.profile2 =1
UNION 
SELECT u.* 
FROM  `update` u
INNER JOIN friendship f2 ON f2.profile2 = u.author
WHERE f2.profile1 =1

3 个答案:

答案 0 :(得分:1)

我建议在一个事务中为每个作者执行一系列查询,这样就不需要进行分组 - 您可以简单地将结果附加到SQL之外。

SELECT * FROM `update` WHERE 
    profile IN (SELECT profile2 FROM `friendship` WHERE profile1=1) OR
    profile IN (SELECT profile1 FROM `friendship` WHERE profile2=1);

答案 1 :(得分:1)

如果您愿意在两个查询中执行此操作,则可以这样做。首先,根据您的约束条件获取最近发布的三个配置文件:

-- Get the three latest updated profiles from here.
-- (we can't use a CTE because MySQL doesn't support
-- them yet).
SELECT DISTINCT p.profile FROM 
  (
    SELECT ui.profile, ui.time  FROM
    (
      SELECT u.profile, u.time 
      FROM `update` u 
      INNER JOIN `friendship` f ON f.profile2 = u.profile 
      WHERE f.profile1 = 1 
      UNION ALL
      SELECT u.profile, u.time 
      FROM `update` u 
      INNER JOIN `friendship` f ON f.profile1 = u.profile 
      WHERE f.profile2 = 1
    ) ui ORDER BY ui.time DESC 
  ) p LIMIT 0, 3;

从该查询中获取三个个人资料ID,并将其替换为以下查询中的<id1><id2><id3>

-- Use a union to get the result set back
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id1>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL 
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id2>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL 
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id3>
ORDER BY a.time DESC
LIMIT 0, 2);

如果返回的配置文件少于三个,请删除PHP代码中的部分查询,或者将WHERE子句设置为0之类,以便始终评估为错误(假设您的配置文件ID不为零) )

如果您希望每个配置文件获得更多或更少的结果,则可以更改上面限制条款中的2。

示例SQL小提琴:http://sqlfiddle.com/#!2/22e57/1(更新小提琴以使内容更有意义并使用时间)

答案 2 :(得分:0)

试试这个sqlFiddle

SELECT T1.profile,T1.content,T1.time
FROM
    (SELECT UPD.profile,UPD.content,UPD.time,
           IF (@prevProfile != UPD.profile,@timeRank:=1,@timeRank:=@timeRank+1) as timeRank,
           @prevProfile := UPD.profile
    FROM
         (SELECT UP.profile,UP.content,UP.time
         FROM
            (SELECT profile,max(time) as latestUpdateTime
             FROM friendship F INNER JOIN updates U
             ON (F.profile1 = 1 AND U.profile = profile2) /* <-- specify profile on this line */
             OR(F.profile2 = 1 AND U.profile = profile1)  /* <-- specify profile on this line */
             GROUP BY profile
             ORDER BY latestUpdateTime DESC
             LIMIT 3 /* limit to 3 friends profiles that have the most recent updates */
             )as LU
          INNER JOIN updates UP
          ON (UP.profile = LU.profile)
          ORDER BY profile,time DESC
          )as UPD,(SELECT @prevProfile:=0,@timeRank:=0)variables
     )T1
WHERE T1.timeRank BETWEEN 1 AND 2 /* grab 2 lastest updates for each profile */      
ORDER BY T1.time DESC

在我的示例中,个人资料ID 1有超过3个朋友,但我只抓住了3个最近更新的朋友。

上述查询的解释。

LU抓取3个个人资料,这些资料是个人资料ID为1的朋友,可以进行最新更新。

UPD抓取属于这3个朋友的所有内容。

T1为每个内容返回内容以及timeRank个数字,从每个个人资料的time DESCENDING开始向上计数

最后我们只为每个配置文件抓取2个内容更新

然后我们最终根据TIME从最近开始对这些更新进行订购。