0

我有两个选择语句,由UNION ALL. 在第一个语句中,where 子句仅收集先前已向用户显示的行。第二条语句收集所有未显示给用户的行,因此我首先得到查看的结果,之后得到未查看的结果。

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

考虑以下结构和数据。

+----+------+-----+--------+------+
| 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  | .... |
+----+------+-----+--------+------+

基本上,我希望语句选择所有未查看的行,这是通过检查viewed列是trueor的天气来完成的false,非常简单明了,这里不用担心。

但是,对于已查看的行,即 column viewed is TRUE,对于那些记录,我只希望为每个组返回 3 行。

在这种情况下,适当的结果应该是每组中最近的 3 行。

+----+------+-----+--------+------+
| 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 行。在这种情况下,这些分组是 10 和 1 和 8 和 6,两者都有三行要显示,而另一组 2 和 3 只显示一行。

请注意, wherefrom = x和, 与andto = y进行相同的分组。因此考虑第一个分组(10 和 1),如果是 和 ,则 是同一组。from = yto = xfrom = 10to = 1from = 1to = 10

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

谢谢你的帮助。

注意:idfrom和已编入索引toviewed这应该有助于提高性能。

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

4

1 回答 1

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 to=m 与 from=m to=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

于 2012-10-08T21:17:33.413 回答