Reputation: 13
On this database (employees from MySQL samples) I have to optimize this query using index:
SELECT t.title,
Avg(s.salary) salario_medio
FROM titles t,
salaries s
WHERE t.emp_no = s.emp_no
AND t.to_date > Now()
AND s.to_date > Now()
GROUP BY t.title
ORDER BY salario_medio DESC;
I have already create this index on the "salaries" table:
CREATE INDEX to_date_idx ON salaries(to_date);
But the EXPLAIN
gives me those rows:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: s
type: range
possible_keys:PRIMARY,emp_no,to_date_idx
key: to_date_idx
key_len: 3
ref: NULL
rows: 370722
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t
type: ref
possible_keys: PRIMARY,emp_no
key: emp_no
key_len: 4
ref: employees.s.emp_no
rows: 1
Extra: Using where
I would like not to use Using temporary
and Using filesort
.
INFO:
SHOW CREATE TABLE salaries;
CREATE TABLE `salaries`
(
`emp_no` INT(11) NOT NULL,
`salary` INT(11) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE NOT NULL,
PRIMARY KEY (`emp_no`, `from_date`),
KEY `emp_no` (`emp_no`),
KEY `to_date_idx` (`to_date`)
) engine=myisam DEFAULT charset=latin1 show CREATE TABLE titles;
CREATE TABLE `titles`
(
`emp_no` INT(11) NOT NULL,
`title` VARCHAR(50) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE DEFAULT NULL,
PRIMARY KEY (`emp_no`, `title`, `from_date`),
KEY `emp_no` (`emp_no`)
)
engine=myisam
DEFAULT charset=latin1
Upvotes: 0
Views: 138
Reputation: 142296
That may have been a good example 10-15 years ago, but times have changed.
Use InnoDB instead of MyISAM. Consider using utf8 instead of latin1. Add an index on titles (to_date)
. Get rid of the redundant index on only (emp_no)
in both tables. Add INDEX(title)
.
(I agree that you ought to use the newer JOIN
syntax.)
And, the AVG
is likely to be wrong without converting to Gordon's subquery version. (That construct probably did not exist when the sample was developed.)
Upvotes: 0
Reputation: 15150
You should use explicit join
syntax, rather than implicit. That won't help performance though. What would help, is capturing the result of the function NOW()
in a variable, that way it only has to be evaluated once, instead of twice per row:
DECLARE @dtNOW DATETIME = NOW()
SELECT t.title
, AVG(s.salary) salario_medio
FROM titles t
INNER JOIN salaries s
ON t.emp_no = s.emp_no
WHERE t.to_date > dtNOW
AND s.to_date > dtNOW
GROUP BY t.title
ORDER BY salario_medio DESC;
Upvotes: 0
Reputation: 1269973
I would suggest writing the query like this:
SELECT t.title,
(SELECT AVG(s.salary)
FROM salaries s
WHERE t.emp_no = s.emp_no AND
s.to_date > NOW()
) as salario_medio
FROM titles t
WHERE t.to_date > NOW()
ORDER BY salario_medio DESC;
This query can take advantage of indexes on titles(to_date, title, emp_no)
and salaries(emp_no, to_date)
.
This eliminates the sorting needed for the aggregation. The query still needs to sort the final results.
Upvotes: 1