编写SQL查询以查找排名

时间:2015-04-26 05:49:23

标签: sql postgresql

我正在努力为某个人确定有多少人获得了比他们更好的分数,并将其归入他们所属的不同团队。因此,在下表中,我从team_id表中抓取team_person列表,其中person_id与我关心的人匹配。这将使我成为我所有的球队。

然后我需要知道我所属的任何团队中的每个person_id,以便我可以从score表中找出他们的最大performances

一旦我拥有了这一点,我终于想确定,对于每个team_id,该团队中有多少人拥有比我更好的分数,其中更好的被简单地定义为具有更大的价值。

此时我已经超越了SQL的能力。到目前为止我所拥有的,似乎让我所关心的所有人的最高分,(基本上除了我的最终“团队”要求之外的一切)是这样的:

    SELECT person_id, MAX(score) m
    FROM performances
    WHERE category_id = 7 AND person_id IN (
        -- Find all the people on the teams I belong to
        SELECT DISTINCT person_id
        FROM team_person 
        WHERE team_id IN (
            -- Find all the teams that I belong to
            SELECT DISTINCT team_id 
            FROM team_person
            WHERE person_id = 2
        )
    )
    GROUP BY person_id
    ORDER BY 2 DESC

我的两个相关表是这样定义的,我正在使用psql 9.1.15

                                     Table "public.team_person"
   Column   |           Type           |                          Modifiers                      
------------+--------------------------+-------------------------------------------------------------
 ident      | integer                  | not null default nextval('team_person_ident_seq'::regclass)
 team_id    | integer                  | not null
 person_id  | integer                  | not null
 *chop extraneous columns*
Indexes:
    "team_person_pkey" PRIMARY KEY, btree (ident)
    "teamPersonUnique" UNIQUE CONSTRAINT, btree (team_id, person_id)
Foreign-key constraints:
    "team_person_person_id_fkey" FOREIGN KEY (person_id) REFERENCES person(ident) ON DELETE CASCADE
    "team_person_team_id_fkey" FOREIGN KEY (team_id) REFERENCES team(ident) ON DELETE CASCADE
Referenced by:
    TABLE "roster" CONSTRAINT "roster_team_person_id_fkey" FOREIGN KEY (team_person_id) REFERENCES team_person(ident) ON DELETE SET NULL
Triggers:
    update_team_person_modified BEFORE INSERT OR UPDATE ON team_person FOR EACH ROW EXECUTE PROCEDURE update_modified_column()



                                      Table "public.performances"
   Column    |           Type           |                          Modifiers                       
-------------+--------------------------+--------------------------------------------------------------
 ident       | bigint                   | not null default nextval('performances_ident_seq'::regclass)
 category_id | integer                  | not null
 person_id   | integer                  | not null
 score       | real                     | not null
 *chop extraneous columns*
Indexes:
    "performances_pkey" PRIMARY KEY, btree (ident)
Foreign-key constraints:
    "performances_category_id_fkey" FOREIGN KEY (category_id) REFERENCES performance_categories(ident) ON DELETE CASCADE
    "performances_person_id_fkey" FOREIGN KEY (person_id) REFERENCES person(ident) ON DELETE CASCADE

1 个答案:

答案 0 :(得分:1)

首先,说明问题,而不是关于如何获得解决方案的假设。你做得很好:

  

确定某个人有多少人获得比他们更好的分数,并根据他们所属的不同团队对其进行分组。

但我改了一下:

  

对于每个团队,某个人是其中的一员,该团队中有多少人的得分高于该人?

我不了解你,但现在突然变得简单了。参加团队排名,左外连接team_person并过滤我们所属的团队,左外连接表演以找到我们与该团队一起玩的游戏,再次离开外部加入team_person以获得其他成员的成员每个团队,左外连接表演,过滤团队主体人员不是成员,团体和聚合。

对于一些极端情况(例如你是唯一的成员,或者你没有玩过游戏的团队),它没有明确规定,但 eh ,无论如何。

问题:

没有团队表。由于您不关心团队表中的任何内容,因此您可以从联接中省略它,只需使用team_person作为连接根。

顺便说一句,您的team_person表格有缺陷。它应该UNIQUE上有(team_id, person_id约束。或者,更好的是,应该是primary key。对于此查询而言实际上并不重要,因为重复的团队成员资格不会改变结果,但却是错误的数据建模。您不能多次成为团队成员。

performances还应该有一个标识特定游戏或其他内容的列。由于你没有表现出一个,我会假设你在寻找那些在任何游戏中,在该游戏或其他游戏中至少表现过一次的人比其他人更好的人游戏。如果您确实想要找到在特定游戏中做得更好的人,那么您需要performances上的合适密钥。

致命问题performances也缺少一个将效果与团队相关联的列。这使得无法正确解决问题,因为您无法在特定团队中获得特定人员的表演。我假设team_id实际上有一个performances,你就把它遗弃了。

因此,考虑到上述问题,我首先使用大型连接获取数据,然后对其进行分组和聚合。对于我们参加的每个团队,这次参加将为我们的每个演出,每个其他演奏者,每个其他演出,一行提供所有相关信息。然后,您可以比较性能和聚合。

以下内容完全未经测试,因为您没有提供示例数据,并且您从架构中删除了重要部分(或架构有缺陷),但我尝试过类似的事情:

SELECT
  my_performances.team_id,

  -- Find how many distinct people scored better than us at least once,
  -- no matter how many times or in which game.
  COUNT(distinct other_team_person.person_id)

-- Start the join with our team memberships and how we scored in each.
-- If we didn't play any games for this team don't produce a result row
-- for it, so INNER JOIN.
FROM team_person my_team_person
INNER JOIN performances my_performances ON 
  (my_performances.person_id = my_team_person.person_id 
   AND my_performances.team_id = my_team_person.team_id)

-- Other members of teams we're also a member of, skipping
-- ourselves. An `INNER JOIN` is fine here because we know
-- a team with only ourselves as a member isn't interesting
-- and we might as well skip it.
INNER JOIN team_person others_team_person ON (
  my_team_person.team_id = other_team_person.team_id
  AND my_team_person.person_id <> other_team_person.person_id)

-- How each of those people performed in each team they're in
-- (because of previous filter, only considers teams we're in too).
-- INNER JOIN because if they never played they can't beat us.
INNER JOIN performances other_performances ON (
  other_team_person.person_id = other_performances.person_id
  AND other_team_person.team_id = other_performances.team_id)

-- Make sure `my_team_person` is only teams we're a member of
WHERE my_team_person.person_id = $1

-- Also discard rows where the other person didn't do better than us
  AND my_performances.score < other_performances.score

-- Emit one row per team we're a member of
GROUP BY my_performances.team_id;

如果你想展示你从未参加过比赛的球队和你唯一的球员,那么你需要将INNER JOIN更改为LEFT OUTER JOIN

如果您想进行比较以查找仅在特定游戏中击败您的人,您需要在performances上添加一个额外的列,然后在{{1}的联接中添加一个额外的列限制它仅在与other_performances相同的游戏中匹配。