如何编写MySQL SELECT Query来实现这个结果?

时间:2017-10-03 19:07:07

标签: mysql select

我有一个抽象问题,可以简化为以下问题:假设我们有两个表personsnames,如下所示:

SELECT * FROM persons;
+----+-------+--------+
| id | name  | fan_of |
+----+-------+--------+
|  1 | alice |      2 |
|  2 | bob   |      4 |
|  3 | carol |      1 |
|  4 | dave  |      3 |
|  5 | bob   |      2 |
+----+-------+--------+

SELECT * FROM names;
+----+-------+--------+
| id | name  | active |
+----+-------+--------+
|  1 | alice |      1 |
|  2 | bob   |      1 |
|  3 | carol |      0 |
|  4 | dave  |      1 |
+----+-------+--------+

每个人(persons)表中的一行都是自己或其他人的粉丝(由id列中的其他人fan_of表示)。 names表包含可以处于活动状态或非活动状态的名称。

对于给定的偏移k,我想SELECT persons个人的k+1个活动名称作为他们的名字或者其中一个人是他们的粉丝。例如,如果偏移量为1,则第二个活动名称为bob,因此我想选择名称为bob的所有人以及具有其中一个bob的人作为他们的粉丝,在此示例中是一行人id = 4。这意味着我想得到结果:

+----+------+--------+
| id | name | fan_of |
+----+------+--------+
|  2 | bob  |      4 |
|  4 | dave |      3 |
|  5 | bob  |      2 |
+----+------+--------+

到目前为止,我得到的是以下查询:

  1 SELECT * FROM persons WHERE
  2     EXISTS (
  3         SELECT * FROM (
  4             SELECT * FROM names WHERE active=true LIMIT 1 OFFSET 1
  5         ) AS selectedname WHERE (selectedname.name=persons.name)
  6     )
  7     OR
  8     EXISTS (
  9         SELECT * FROM(
 10             SELECT * FROM persons WHERE EXISTS (
 11                 SELECT * FROM (
 12                     SELECT * FROM names WHERE active=true LIMIT 1 OFFSET 1
 13                 ) AS selectedname WHERE (selectedname.name=persons.name)
 14             )
 15         ) AS personswiththatname  WHERE persons.id=personswiththatname.fan_of
 16     );

它从上面给出了我想要的结果,但请注意它是低效的,因为第3-5和11-13行是相同的。

我有以下两个问题:

  1. 可以采取哪些措施来避免这种低效率?
  2. 我实际上需要区分来自的那些行 name条件(这里是name = bob的行)和那些来的 来自fan_of条件(此处为name = dave的行)。这个 可以在应用程序代码中完成,但后来我需要另一个 数据库查询之前找出k+1 - 活动名称,这可能 慢一点(如果这是更好的解决方案,请纠正我)。我会 而更喜欢一个帮助我区分的附加列z

    +----+------+--------+---+
    | id | name | fan_of | z |
    +----+------+--------+---+
    |  2 | bob  |      4 | 1 |
    |  4 | dave |      3 | 0 |
    |  5 | bob  |      2 | 1 |
    +----+------+--------+---+
    

    如何实现这样的输出?

1 个答案:

答案 0 :(得分:1)

看起来我可以使用参数获得你想要达到的最小值(如果这是一个选项)。

它不漂亮,但我看不到一种简单的方法来实现你所要求的东西,所以这就是我到目前为止......(设置@offset以适应'k')

SET @offset = 1;
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset));
select
a.*
From persons a
where (a.name = @name) OR (a.id IN (SELECT fan_of from persons where name = @name));

如果在我吃完食物的时候你仍然没有答案,我会看第2部分。

(希望我能正确阅读你的简报)

P.S。我把@name SQL保存在一行中,因为在这种情况下它似乎更好。

编辑:这是一个非常混乱但功能性的源指标,使用您的示例。 Z = 1是行来自name,'0'来自fan_of

SET @offset = 1;
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset));
select
a.*,'1' as z
From persons a
where (a.name = @name)
union
select
a.*,'0' as z
From persons a
where (a.id IN (SELECT fan_of from persons where name = @name));

不同的ID查询:

SET @offset = 1;
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset));
SELECT id, name, fan_of, z FROM
(select
distinct a.id,
a.name,
a.fan_of,
1 as z
From persons a
where (a.name = @name)
union
select
distinct a.id,
a.name,
a.fan_of,
0 as z
From persons a
where (a.id IN (SELECT fan_of from persons where name = @name))
ORDER BY z desc) qry
GROUP BY id;

这会产生:

+----+------+--------+---+
| id | name | fan_of | z | 
+----+------+--------+---+
| 2  | bob  |    4   | 1 | 
| 5  | bob  |    2   | 1 | 
| 4  | dave |    3   | 0 |
+----+------+--------+---+