我怎么能这样做1数据库查询?

时间:2014-08-23 22:37:29

标签: sql ruby-on-rails database postgresql ruby-on-rails-4

我有一个查询,在USER表中查找根据其联属代码的频率查找前9个分支机构。

affs = User.group(:affiliate).limit(9).count.sort_by{|i,j|-j}
(0.6ms)  SELECT  COUNT(*) AS count_all, affiliate AS affiliate FROM "users"  GROUP BY affiliate LIMIT 9
 #=> [["ACE5", 5], ["2YY", 3], ["MB7", 2], ["GB2", 2], ["GHCYM", 1], ["L6EOC", 1], ["15F2", 1], ["R0TK", 1], ["DSB", 1]] 

现在我接受这个结果并在其上枚举以获得与用户对应的行。

affs.map do |aff|
  user = User.find_by(qcode: aff.first)
  [user.avatar.url(:thumb), user.name.match(/\w*/)[0], aff.first, aff.last]
end

 #LOG OUTPUT
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'ACE5' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = '2YY' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'GB2' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'MB7' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'L6EOC' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = '15F2' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'R0TK' LIMIT 1
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."qcode" = 'DSB' LIMIT 1

运行此查询9次似乎非常多余。如何将其优化为1或2个数据库查询?

示例USERS表DB模式:

id | email              | name         | qcode  | affiliate
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 | kjones@aol.com     | Ken Lu       | DSB    | L6EOC         
26 | broks2@aol.com     | Brains Bruk  | AJ8U   | DSB           
27 | joanshu@aol.com    | Joan Hu      | K9UE   | DSB               

从上面的例子。 " Ken Lu"是联属会员DSB,他负责注册ID 26和ID 27.因此上述aff查询将产生:

 #=> [["DSB", 2], ...] 

1 个答案:

答案 0 :(得分:1)

要一次获取所有9个关联企业的user表信息,您可以直接进入原始SQL。

从我上面的代码中我可以推断出你的模式,这样的东西应该有效(如果你的模式与我推断的不同,你可能需要调整一下;没有测试,因为我没有你的数据也没有 Rails 设置方便):

WITH top_affiliates AS
(
    SELECT  COUNT(*) AS count_all, affiliate AS affiliate
    FROM users
    GROUP BY affiliate
    ORDER BY count_all
    LIMIT 9
)
SELECT *
FROM top_affiliates AS t
JOIN users AS u ON (t.affiliate = u.qcode)
ORDER BY count_all

请注意,JOIN假定t.affiliate = u.qcode将在表之间提供唯一的映射(基于从迭代地图时得到的SQL,这似乎是一个公平的假设)。但是,如果它不是唯一的,则需要在JOIN中添加其他参数,使得映射是唯一的,否则您将在结果中获得您不想要的其他行。

如果您正在使用 ActiveRecord ,则可以在包含此信息的模型上使用connection.execute,并将上述SQL作为字符串传递(或者首先将它分配给变量并将其传递给。)。

根据OP的评论进行编辑:

@ f1f5:WITH查询定义了所谓的 CTE ,它本质上是一个限定为查询本身而不是连接的临时表。然后我可以在WITH下面的查询中使用 CTE ,这就是我上面所做的。基本上,WITH查询取代了您的User.group(:affiliate).limit(9).count.sort_by{|i,j|-j}代码,然后使用内联结果来提取相关的user记录。

编辑以响应来自OP的架构信息:

所以,是的,我认为上面的代码会做你想要的。这是我使用架构数据,查询和结果创建的sqlfiddle