我有一个(有点)复杂的查询,返回60K +客户记录。我有两个左外连接,我需要使用查询来搜索相关记录:
customers = customers.
left_outer_joins(:phones, :emails).
select("customers.id, customers.name, ...")
if params[:sSearch].present?
params[:sSearch] = parse_phone_number(params[:sSearch])
customers = customers.where(
"customers.name like :search OR
..
phones.number like :search OR
emails.email like :search",
search: "%#{params[:sSearch]}%"
)
end
customers = customers.
group('customers.id').
order("#{sort_column} #{sort_direction}).
page(page).
per(per_page)
(注意:这是在数据表ajax调用中使用的,因此sort_column,sort_direction,page和per_page都是其中的参数。)
我的所有索引都符合要求。
这是我面临的主要问题:如果我不使用该组('customers.id'),由于左外连接,它将返回重复的客户记录。但添加group子句似乎会将查询时间增加至少2倍。在查询结尾使用.distinct似乎比使用group更慢。
是否有更好/更快的方法不返回带有左外连接的重复项而不显着增加查询时间?现在这需要超过1000毫秒。
编辑:要回答下面的Shadow评论 - 我正在加入多个电话/电子邮件,因为我需要搜索它们。我期望的是,如果客户记录与搜索匹配(比如在加入的手机上),它只返回一个客户 - 而不是两个。答案 0 :(得分:1)
如果您只想搜索地址/电话号码,但不想显示它们,请使用带有子查询的exists运算符而不是连接。
sql中的代码看起来像下面这样:
select *
from customers c
where c.name like '%...%'
or exists (select 1 from emails e where e.email like '%...%' and e.customer_id=c.id) ...
但是,如果您确实要显示地址和电话号码,则必须使用联接。在这种情况下,您可能希望使用MySQL的内置group_concat()函数将各种地址和电话号码连接成一个值。
需要考虑的其他事项:
union
代替一系列or
条件like
,因为like '%...%'
过滤器无法使用索引来加速查询。