【问题标题】:Better SQL - :group vs. :select => 'DISTINCT'更好的 SQL - :group 与 :select => 'DISTINCT'
【发布时间】:2009-09-06 12:32:02
【问题描述】:

让我们假设三个模型,标准连接:

class Mailbox < ActiveRecord::Base
  has_many :addresses
  has_many :domains, :through => :addresses
end

class Address < ActiveRecord::Base
  belongs_to :mailbox
  belongs_to :domain
end

class Domain < ActiveRecord::Base
  has_many :addresses
  has_many :mailboxes, :through => :addresses
end

现在显然,如果您想知道任何给定邮箱的地址在哪些域中,您有两种可能的方法:

m = Mailbox.first
# either: SELECT DISTINCT domains.id, domains.name FROM "domains" INNER JOIN 
#         "addresses" ON "domains".id = "addresses".domain_id WHERE 
#         (("addresses".mailbox_id = 1))
m.domains.all(:select => 'DISTINCT domains.id, domains.name')
# or: SELECT domains.id, domains.name FROM "domains" INNER JOIN "addresses" ON
#     "domains".id = "addresses".domain_id WHERE (("addresses".mailbox_id = 1))
#      GROUP BY domains.id, domains.name
m.domains.all(:select => 'domains.id, domains.name', 
  :group => 'domains.id, domains.name')

对我来说,问题是我不知道哪种解决方案更好。当我不指定任何其他条件时,PostgreSQL 查询规划器倾向于第二个解决方案(按预期工作),但如果我向查询添加条件,则归结为“唯一”与“组”:

使用“DISTINCT”:

 Unique  (cost=16.56..16.57 rows=1 width=150)
   ->  Sort  (cost=16.56..16.56 rows=1 width=150)
         Sort Key: domains.name, domains.id
         ->  Nested Loop  (cost=0.00..16.55 rows=1 width=150)
               ->  Index Scan using index_addresses_on_mailbox_id on addresses  (cost=0.00..8.27 rows=1 width=4)
                     Index Cond: (mailbox_id = 1)
               ->  Index Scan using domains_pkey on domains  (cost=0.00..8.27 rows=1 width=150)
                     Index Cond: (domains.id = addresses.domain_id)
                     Filter: (domains.active AND domains.selfmgmt)
(9 rows)

使用“GROUP BY”:

Group  (cost=16.56..16.57 rows=1 width=150)
   ->  Sort  (cost=16.56..16.56 rows=1 width=150)
         Sort Key: domains.name, domains.id
         ->  Nested Loop  (cost=0.00..16.55 rows=1 width=150)
               ->  Index Scan using index_addresses_on_mailbox_id on addresses  (cost=0.00..8.27 rows=1 width=4)
                     Index Cond: (mailbox_id = 1)
               ->  Index Scan using domains_pkey on domains  (cost=0.00..8.27 rows=1 width=150)
                     Index Cond: (domains.id = addresses.domain_id)
                     Filter: (domains.active AND domains.selfmgmt)
(9 rows)

我真的不确定如何确定检索这些数据的更好方法。我的直觉告诉我选择“GROUP BY”,但我找不到任何足够具体的文档来解决这个问题。

我应该使用“:group”还是“:select => 'DISTINCT'”?这种选择与其他现代 RDBMS 一样吗? Oracle、DB2 或 MySQL(我无权访问这些,因此无法执行测试)?

【问题讨论】:

    标签: sql ruby-on-rails postgresql activerecord


    【解决方案1】:

    如果您使用的是 GROUP BY 而不是 DISTINCT,因为它的计划更有效。

    在 8.4 中没有区别,因为 DISTINCT 被“教授”为也能够使用组运算符。

    【讨论】:

    • 感谢您的回答。但是,对于其他数据库系统,例如 e.g. DB2?我编辑了这个问题,强调我不仅对 pgsql 特定的提示感兴趣。
    • Oracle(至少我从 9i 开始就知道)以相同的方式运行 - 使用 GROUP BY 或 DISTINCT 时,解释计划没有区别。我必须查看 asktom 以获取支持它的文章。
    【解决方案2】:

    SQL 必须执行相同的步骤来收集所需的信息以准备 GROUP BY 或“等效”DISTINCT 查询:相同的过滤器、相同的排序等。区别在于最后一步,这里 Postgre 调用“组”或“唯一”。

    当 GROUP BY 查询在其 SELECT 中不包含任何聚合时,“Group”和“Unique”本质上是相同的(即使它们归结为服务器代码中的不同方法)。在存在一些聚合(例如 COUNT(*)、MAX(some_field) 等)的情况下,“组”步骤会占用更多资源,因为它需要额外的存储空间来存储计数、最小值等,并且需要递增或与之比较等。

    我会采用 GROUP BY 方法,因为它可能会在 SELECT 中添加聚合,如果在某个时候需要此类信息,而不会对查询进行太多更改。即使未显示实际计数,应用程序也可以按此计数的降序显示域。

    【讨论】:

    • 区别在于实现,在 PostgreSQL
    猜你喜欢
    • 2023-03-10
    • 2020-11-05
    • 2010-09-30
    • 1970-01-01
    • 2021-02-09
    • 2010-10-09
    • 1970-01-01
    • 2011-12-14
    • 1970-01-01
    相关资源
    最近更新 更多