0

用户表有

id, email, password, gender, dob 

等等。性别默认为空。我有另一张桌子user_gender,里面有first_namegender。我的 sql 查询是UserUser_Gender基于first_name. 用户表很大,大约有 300,000 多行。我正在运行下面提到的查询,但它花费了太多时间。如何优化此查询?-

select 
   count(*) 
from user u 
left outer join user_gender ug on ug.name = 
  case when locate(' ', u.name) > 0 then
     substring(u.name, 1,locate(' ', u.name))
  else
     u.name 
  end 
where 
  ug.gender != 'mf' and u.gender is null
4

3 回答 3

6

首先,我建议彻底重组。300000 行开始达到“中等数据集”大小......

  • 正确规范化表格
    • 不要使用存储多个单独值的列 - 特别是 name 列是一个很好的例子。让它成为两列:first_name 和 last_name。
    • DavidB 提到了性别分离。这完全是胡说八道。至少,每个人都有一个性别……不知道,它总是NULL……
  • 使用(最好是数字!!)ID,而不是使用数据字段(特别是那些像名字的)
    • 这样,如果名称更改(可能发生 IRL),您只需更新一行...
    • 两个人甚至可能有完全一样的名字……

其次,在重组之后,您必须应用索引,并检查查询的执行计划,以确保对其进行适当的优化。

于 2013-02-28T13:48:52.053 回答
3

首先设计这两个表将帮助您更好地解决性能问题。性能问题出现在您的 join 子句上:

当 locate(' ', u.name)>0 then substring(u.name, 1,locate(' ', u.name)) else u.name end

为您的 User 表使用主键 (user_id) 并将其放在您的 user_gender 表上并相应地加入。

或者

由于您可能正在使用遗留数据库设计并且无法添加或使用 user_id 字段,您可以使用临时 first_name 字段并使用您的 join 子句填充它

update users u set u.first_name = case when locate(' ', u.name)>0 
then substring(u.name, 1,locate(' ',> u.name)) else u.name end

在此之后,您可以将查询重写为

select count(*) from user u left outer join user_gender ug 
on ug.name=u.first_name 
where ug.gender != 'mf' and u.gender is null

这将帮助您的查询运行得更快,但我会提出第一个解决方案,无论如何添加/使用主键。

于 2013-02-28T13:54:21.180 回答
0

我注意到的第一件事是查询没有正确编写。或者,至少,它没有按照您的意愿行事。!=in子句是“where撤消”左外连接。我想你想在on条款中这样做。

有一个索引user_gender(firstname, gender),我认为这个版本应该运行得很快:

select count(*) 
from (select u.*,
             (case when locate(' ', u.name) > 0 then substring(u.name, 1,locate(' ', u.name))
                   else u.name
              end) as FirstName
      from user u
     ) u
where not exists (select 1 from user_gender ug where ug.name = u.FirstName and ug.gender <> 'mf')

它应该可以用户表,计算名字,并检查索引以查看是否有性别。

于 2013-02-28T14:16:38.787 回答