【问题标题】:Efficient approach to get two-dimensional datau using使用获取二维数据的有效方法
【发布时间】:2017-05-26 19:27:40
【问题描述】:

为了举例,假设我有以下模型:

  • 团队
  • 每支球队都有任意数量的球迷

在 SQL 中,这意味着您最终会得到以下表格:

  • 团队:标识符、名称
  • 粉丝:标识符、名称
  • team_fan: team_identifier, fan_identifier

我正在寻找一种检索方法:

  • 所有团队,以及
  • 对于每支球队,前 5 名球迷的名字以“A”开头。

什么是有效的方法?

  1. 在我目前的幼稚方法中,我做<# teams> + 1查询,这很麻烦:

    • 第一:SELECT * FROM team
    • 然后,对于标识符为 X 的每个团队:

      SELECT * 
      FROM fan 
        INNER JOIN team_fan 
          ON fan.identifier = team_fan.fan_identifier AND team_fan.team_identifier = X 
      WHERE fan.name LIKE 'A%' 
      ORDER BY fan.name LIMIT 5
      
  2. 应该有更好的方法来做到这一点。

我可以像现在一样首先检索所有团队,然后执行以下操作:

SELECT * 
FROM fan 
WHERE fan.name LIKE 'A%' 
AND fan.identifier IN (
    SELECT fan_identifier 
    FROM team_fan 
    WHERE team_identifier IN (<all team identifiers from first query>)) 
ORDER BY fan.name

但是,这种方法忽略了我需要每支球队的前 5 名球迷的要求,他/她的名字以“A”开头。只是在上面的查询中添加LIMIT 5 是不正确的。

另外,使用这种方法,如果我有大量团队,我会在第二个查询中将相应的团队标识符发送回数据库(对于IN (&lt;all team identifiers from first query&gt;)),这可能会影响性能吗?

我正在针对 PostgreSQL、Java、Spring 和普通 JDBC 进行开发。

【问题讨论】:

  • 如果某些球队的名字以 A 开头的球迷少于五个怎么办?您不想按字母顺序排列前五个粉丝吗?
  • Mick:不,我希望每支球队的前 5 名球迷的名字以A 开头。如果一个团队没有这样的 5 个粉丝,那么这个粉丝列表会更小。

标签: sql postgresql


【解决方案1】:

你需要一个三表连接

SELECT team.*, fan.*
FROM team 
JOIN team_fan
  ON team.team_identifier = team_fan.team_identifier
JOIN fan
  ON fan.fan_identifier = team_fan.fan_identifier

现在要过滤,您需要这样做。

with cte as (
    SELECT team.*, fan.*, 
           row_number() over (partition by team.team_identifier 
                              order by fan.name) as rn
    FROM team 
    JOIN team_fan
      ON team.team_identifier = team_fan.team_identifier
    JOIN fan
      ON fan.fan_identifier = team_fan.fan_identifier
    WHERE fan.name LIKE 'A%' 
)
SELECT *
FROM cte 
WHERE rn <= 5

【讨论】:

  • 感谢您的查询示例。在这里,您在同一查询中获取 team.*fan.*。这意味着,在此示例中,每个团队的所有属性将出现 5 次。首先获取所有球队(简单地SELECT * FROM team),然后像您在查询中所做的那样分别获取这些球队的所有粉丝不是更好吗?这里的“常识”是什么?我很难评估这一点。
  • 您可以更改为SELECT team.id, fan.* 以减少数据并在不同的查询中使用SELECT team.*。但我不会太担心它。您可以使用EXPLAIN ANALYZE 测试这两个查询,并查看 postgres 执行每个查询需要多少时间。我敢打赌,差异很小。并且可能需要更多时间在 UI 上合并数据。
  • 好的,很有趣。我将与explain analyze 进行比较。 (我希望我能接受你的回答,但到目前为止我的声望点还不够。)
  • 我会说这是获取数据的最有效方式——单次查询(您的应用程序和数据库之间只有一次往返)并使用分析窗口函数(数据库执行最少的每个查询工作)。团队信息重复五次这一事实可能会对性能产生边际影响。一般来说,您希望尽量减少对数据库运行的查询数量,因为每次查询执行都会带来开销。
  • 您可以接受答案(绿色勾号),您可以尽快投票 (我相信是 10 个代表)。我赞成您的问题,以帮助您实现目标。
【解决方案2】:

通常,RDBMS 对标准 SQL 有自己的技巧,允许您在某些分组/排序条件下进行计数。

Postgres 也不例外,它有ROW_NUMBER() 函数。

您需要正确划分行号,按字母排序并将查询限制为行号

【讨论】:

  • 好的,窗口函数,我明白了。在为第一个查询返回的球队获取球迷时应该这样做吗?这意味着我将发回所有团队标识符。这不会影响性能吗?
  • ROW_NUMBER() 和窗口函数通常在 SQL 标准中指定,它不是“绕过标准 SQL”。
  • @KatrineYelen 我说的是在一个查询中获取所有内容,不像第一种情况。然后,我不会使用“杀死”这个词——评估者“影响”。或不。取决于你如何实现它。尝试不同的,解释它们,衡量性能,做出你的选择。
  • 如果你在一个查询中做这样的逻辑,我们不是重复5次团队信息(例如名称)吗?
  • 是的,我们是——那又如何?这就是关系表数据库的工作方式。获取整行很便宜,不用担心 - 定位它很昂贵。然后由您决定如何将其呈现给最终用户,剪掉什么,留下什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 2013-01-10
  • 2020-06-21
  • 1970-01-01
  • 2014-11-30
  • 1970-01-01
相关资源
最近更新 更多