选择每个组的行数,其中两个列值构成一个组

时间:2012-10-08 17:34:44

标签: mysql sql

我有UNION ALL加入的两个select语句。在第一个语句中,where子句仅收集先前向用户显示的行。第二个语句收集所有未向用户显示的行,因此我最终得到的结果是查看结果,之后是未查看结果。

当然这可以通过使用简单ORDER BY的相同select语句来实现,但是在您意识到我希望实现的目标之后,两个单独选择的原因很简单。

考虑以下结构和数据。

+----+------+-----+--------+------+
| id | from | to  | viewed | data |
+----+------+-----+--------+------+
| 1  | 1    | 10  | true   | .... |
| 2  | 10   | 1   | true   | .... |
| 3  | 1    | 10  | true   | .... |
| 4  | 6    | 8   | true   | .... |
| 5  | 1    | 10  | true   | .... |
| 6  | 10   | 1   | true   | .... |
| 7  | 8    | 6   | true   | .... |
| 8  | 10   | 1   | true   | .... |
| 9  | 6    | 8   | true   | .... |
| 10 | 2    | 3   | true   | .... |
| 11 | 1    | 10  | true   | .... |
| 12 | 8    | 6   | true   | .... |
| 13 | 10   | 1   | false  | .... |
| 14 | 1    | 10  | false  | .... |
| 15 | 6    | 8   | false  | .... |
| 16 | 10   | 1   | false  | .... |
| 17 | 8    | 6   | false  | .... |
| 18 | 3    | 2   | false  | .... |
+----+------+-----+--------+------+

基本上我希望语句选择所有未查看的行,这是通过检查viewedtrue列为falseviewed is TRUE的天气来完成的,非常简单明了,没有任何内容担心在这里。

但是,对于已经查看过的行,意为列+----+------+-----+--------+------+ | id | from | to | viewed | data | +----+------+-----+--------+------+ | 6 | 10 | 1 | true | .... | | 7 | 8 | 6 | true | .... | | 8 | 10 | 1 | true | .... | | 9 | 6 | 8 | true | .... | | 10 | 2 | 3 | true | .... | | 11 | 1 | 10 | true | .... | | 12 | 8 | 6 | true | .... | +----+------+-----+--------+------+ ,对于这些记录,我只想为每个组返回3行。

此实例中的相应结果应为每组中最近的3行。

from = x

从理想的结果集中可以看出,我们有三组。因此,查看结果的所需查询应为其找到的每个分组显示最多3行。在这种情况下,这些分组为10,其中1为1,8为6,两个都有三行显示,而另一组3只有3,只有一行显示。

请注意,在to = yfrom = y的位置,进行相同的分组,就好像to = xfrom = 10一样。因此,考虑到第一个分组(10个为1),to = 1from = 1如果是to = 10id则属于同一组。

但是整个表中有很多组我只希望在select语句中返回最近的3个组,这就是我的问题,我不知道如何以最有效的方式完成考虑到这个表在某些时候会有数百条甚至数千条记录。

感谢您的帮助。

注意:fromtoviewed和{{1}}已编入索引,这有助于提升效果。

PS:我不确定如何准确地命名这个问题,如果你有更好的主意,请成为我的客人并编辑标题。

1 个答案:

答案 0 :(得分:3)

真是个毛球!随着您从最近的,最近的第二次到最近的第三次,这种情况越来越难。

让我们通过获取我们需要的ID列表将它们放在一起。然后我们可以通过ID从表中提取项目。

这个相对简单的查询可以获得最新项目的ID

 SELECT id FROM
    (SELECT max(id) id, fromitem, toitem
       FROM stuff
      WHERE viewed = 'true'
      GROUP BY fromitem, toitem
    )a

小提琴:http://sqlfiddle.com/#!2/f7045/27/0

接下来,我们需要获取第二个最新项目的ID。为此,我们需要一个自联接样式查询。我们需要做同样的摘要,但是在省略最新项目的虚拟表上。

select id from (
  select max(b.id) id, b.fromitem, b.toitem
    from stuff a
    join
           (select id, fromitem, toitem
            from stuff
           where viewed = 'true'
            ) b on (    a.fromitem = b.fromitem 
                    and a.toitem = b.toitem
                    and b.id < a.id)
   where a.viewed = 'true'
   group by fromitem, toitem
  )c

小提琴:http://sqlfiddle.com/#!2/f7045/44/0

最后,我们需要获取第三个最新项目的ID。怜悯!我们需要将我们刚才的查询加入到表中。

select id from
(
  select max(d.id) id, d.fromitem, d.toitem
    from stuff d
     join 
    (
       select max(b.id) id, b.fromitem, b.toitem
          from stuff a
          join
            (
               select id, fromitem, toitem
                 from stuff
                where viewed = 'true'
            ) b on  (    a.fromitem = b.fromitem 
                     and a.toitem = b.toitem
                     and b.id < a.id)
          where a.viewed = 'true'
          group by fromitem, toitem
     ) c on (    d.fromitem = c.fromitem
             and d.toitem = c.toitem
             and d.id < c.id)
    where d.viewed='true'
  group by d.fromitem, d.toitem
 ) e

小提琴:http://sqlfiddle.com/#!2/f7045/45/0

所以,现在我们采用所有这些id的联合,并使用它们从表中抓取正确的行,我们就完成了。

SELECT * 
  FROM STUFF
 WHERE ID IN
(

SELECT id FROM
    (SELECT max(id) id, fromitem, toitem
       FROM stuff
      WHERE viewed = 'true'
      GROUP BY fromitem, toitem
    )a
UNION
select id from (
  select max(b.id) id, b.fromitem, b.toitem
    from stuff a
    join
           (select id, fromitem, toitem
            from stuff
           where viewed = 'true'
            ) b on (    a.fromitem = b.fromitem 
                    and a.toitem = b.toitem
                    and b.id < a.id)
   where a.viewed = 'true'
   group by fromitem, toitem
  )c
UNION
select id from
(
  select max(d.id) id, d.fromitem, d.toitem
    from stuff d
     join 
    (
       select max(b.id) id, b.fromitem, b.toitem
          from stuff a
          join
            (
               select id, fromitem, toitem
                 from stuff
                where viewed = 'true'
            ) b on  (    a.fromitem = b.fromitem 
                     and a.toitem = b.toitem
                     and b.id < a.id)
          where a.viewed = 'true'
          group by fromitem, toitem
     ) c on (    d.fromitem = c.fromitem
             and d.toitem = c.toitem
             and d.id < c.id)
    where d.viewed='true'
  group by d.fromitem, d.toitem
 ) e
UNION
select id from stuff where viewed='false'
)
order by viewed desc, fromitem, toitem, id desc
嘻嘻。 SQL太多了。小提琴:http://sqlfiddle.com/#!2/f7045/47/0

现在,我们需要应对您的上一个要求,即图表无序的要求。也就是说,from = n到= m与from = m到= n相同。

为此,我们需要一个虚拟表而不是物理表。这样就可以了。

 SELECT id, least(fromitem, toitem) fromitem, greatest(fromitem,toitem) toitem, data
   FROM stuff

现在我们需要使用这个虚拟表,这个视图,到处都是物理表出现的地方。让我们使用一个视图来做到这一点。

CREATE VIEW 
AS 
SELECT id,
       LEAST(fromitem, toitem) fromitem,
       GREATEST (fromitem, toitem) toitem,
       viewed,
       data;

所以,我们的最终查询是:

SELECT *
      FROM stuff
     WHERE ID IN
    (

    SELECT id FROM
        (SELECT max(id) id, fromitem, toitem
           FROM STUFF_UNORDERED
          WHERE viewed = 'true'
          GROUP BY fromitem, toitem
        )a
    UNION
    SELECT id FROM (
      SELECT max(b.id) id, b.fromitem, b.toitem
        FROM STUFF_UNORDERED a
        JOIN
               (SELECT id, fromitem, toitem
                FROM STUFF_UNORDERED
               WHERE viewed = 'true'
                ) b ON (    a.fromitem = b.fromitem
                        AND a.toitem = b.toitem
                        AND b.id < a.id)
       WHERE a.viewed = 'true'
       GROUP BY fromitem, toitem
      )c
    UNION
    SELECT id FROM
    (
      SELECT max(d.id) id, d.fromitem, d.toitem
        FROM STUFF_UNORDERED d
         JOIN
        (
           SELECT max(b.id) id, b.fromitem, b.toitem
              FROM STUFF_UNORDERED a
              JOIN
                (
                   SELECT id, fromitem, toitem
                     FROM STUFF_UNORDERED
                    WHERE viewed = 'true'
                ) b ON  (    a.fromitem = b.fromitem
                         AND a.toitem = b.toitem
                         AND b.id < a.id)
              WHERE a.viewed = 'true'
              GROUP BY fromitem, toitem
         ) c ON (    d.fromitem = c.fromitem
                 AND d.toitem = c.toitem
                 AND d.id < c.id)
        WHERE d.viewed='true'
      GROUP BY d.fromitem, d.toitem
     ) e
    UNION
    SELECT id FROM STUFF_UNORDERED WHERE viewed='false'
    )
    ORDER BY viewed DESC,
            least(fromitem, toitem),
            greatest(fromitem, toitem),
            id DESC

小提琴:http://sqlfiddle.com/#!2/8c154/4/0