简体   繁体   中英

MySQL — Better way to do this query?

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 '?%'
)

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 (eg 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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM