views:

74

answers:

3

This query will be done in a cached autocomplete text box, possibly by thousands of users at the same time. What I have below works, bit I feel there may be a better way to do what I am doing.

Any advice?

UPDATED -- it can be 'something%':

SELECT a.`object_id`, a.`type`, 
    IF( b.`name` IS NOT NULL, b.`name`,
        IF( c.`name` IS NOT NULL, c.`name`, 
            IF( d.`name` IS NOT NULL, d.`name`,
                IF ( e.`name` IS NOT NULL, e.`name`, f.`name` )
            )
        )
    ) AS name
FROM `user_permissions` AS a 
   LEFT JOIN `divisions` AS b 
       ON ( a.`object_id` = b.`division_id` 
           AND a.`type` = 'division' 
           AND b.`status` = 1 ) 
   LEFT JOIN `departments` AS c 
       ON ( a.`object_id` = c.`department_id` 
           AND a.`type` = 'department'  
           AND c.`status` = 1 ) 
   LEFT JOIN `sections` AS d 
       ON ( a.`object_id` = d.`section_id` 
           AND a.`type` = 'section'  
           AND d.`status` = 1 ) 
   LEFT JOIN `units` AS e 
       ON ( a.`object_id` = e.`unit_id` 
           AND a.`type` = 'unit'  
           AND e.`status` = 1 ) 
   LEFT JOIN `positions` AS f 
       ON ( a.`object_id` = f.`position_id` 
           AND a.`type` = 'position'  
           AND f.`status` = 1 ) 
WHERE a.`user_id` = 1 AND ( 
    b.`name` LIKE '?%' OR
    c.`name` LIKE '?%' OR
    d.`name` LIKE '?%' OR
    e.`name` LIKE '?%' OR
    f.`name` LIKE '?%'
)
+1  A: 

There are 500 things you can do. Optimize once you know where your bottlenecks are. Until then, work on getting those users onto your app. Its a much higher priority.

Zak
+1  A: 

Other than changing the nested Ifs to use a Coalesce() function (MySql has Coalesce() doesn't it)? There is not much you can do as long as you need to filter on that input parameter with a like expresion. Putting a filter on a column using a Like expression, where the Like parameter has a wildcard at the begining, as you do, makes the query argument non-SARG-able, which means that the query processor must do a complete table scan of all the rows in the table to evaluate the filter predicate.

It cannot use an index, because an index is based on the column values, and with your Like parameter, it doesn't know which index entries to read from (since the parameter starts with a wild card)

if MySql has Coalesce, you can replace your Select with:

SELECT a.`object_id`, a.`type`,
Coalesce(n.name, c.name, d.Name, e.Name) name

If you can replace the search argument parameter so that it does not start with a wildcard, then just ensure that there is an index on the name column in each of the tables, and (if there are not indices on that column now), the query performance will increase enormously.

Charles Bretana
What is SARG? I googled but what I found seemed unrelated -- I can remove the beginning * and COALESCE was perfect.
Kerry
SARG stands for Search ARGument and ir refers to the ability of the query processor to use the search argument directly against an existing index. check http://en.wikipedia.org/wiki/Sargable or http://stackoverflow.com/questions/799584/what-makes-a-sql-statement-sargable
Charles Bretana
+3  A: 

Two simple, fast queries is often better than one huge, inefficient query.

Here's how I'd design it:

First, create a table for all your names, in MyISAM format with a FULLTEXT index. That's where your names are stored. Each of the respective object type (e.g. departments, divisions, etc.) are dependent tables whose primary key reference the primary key of the main named objects table.

Now you can search for names with this much simpler query, which runs blazingly fast:

SELECT a.`object_id`, a.`type`, n.name, n.object_type
FROM `user_permissions` AS a 
JOIN `named_objects` AS n ON a.`object_id = n.`object_id`
WHERE MATCH(n.name) AGAINST ('name-to-be-searched')

Using the fulltext index will run hundreds of times faster than using LIKE in the way you're doing.

Once you have the object id and type, if you want any other attributes of the respective object type you can do a second SQL query joining to the table for the appropriate object type:

SELECT ... FROM {$object_type} WHERE object_id = ?

This will also go very fast.


Re your comment: Yes, I'd create the table with names even if it's redundant.

Bill Karwin
Redesigning the tables isn't really an option -- this isn't going into a new system, its a new part to an existing person and there are many calls to the existing tables. Unless you think it would be efficient to create a table for just that purpose which would duplicate the other table's values and use triggers to keep them in sync...?
Kerry