Hi Doug,
I looked at it again and noticed a relationship that I missed before. You
*do* have loc_countries_lang associated to the other tables through the
loc_countries table. Sorry I missed it last time.
I also noticed that you wrote this from the "bottom up" you started with
the most detailed element, "veg" and added information from its parents and
some of its children to complete your query (Wow! what a good night's sleep
can do to help you think, eh?)


I am going to try to diagram the "dependency" tree of the JOINS in this
query. Problem is I have never done this without graphics so it may look
silly.

veg <-> loc_states <-> loc_districts <-> loc_countries <->
loc_countries_lang
    <-> users <-> users_intros
    <- veg_titles
    <- tech_equip


Since the objects seem related in a geographic hierarchy, let me see what
it looks like if I flip the tree around like:

loc_countries <-> loc_countries_lang
              <-> loc_states <-> loc_districts
                             <-> veg <-> users <-> users_intros
                                                       <- veg_titles
                                     <- tech_equip


The top two branches of this tree seems to be driven by the "fact" that
lang_ID = 0. The bottom branch is looking for particular "veg" records.
We could narrow part of our search list (improving our join performance) if
we start with a query like:

CREATE TEMPORARY TABLE tmpStateList
SELECT s.state_id, ctrl.country_name , ctr.nice_country_name
FROM loc_countries ctr
INNER JOIN loc_countries_lang ctrl
      ON ctr.country_id = ctrl.country_id
      AND ctrl.lang_id =0
INNER JOIN loc_districts d
      on d.district_id = s.district_id
      and d.lang_id = 0
INNER JOIN loc_states AS s
      ON s.district_id = d.district_ID
      AND s.lang_ID = 0

This gives us a list of all states that speak language 0 along with their
country_name and nice_country_name. Now we need to get at the veg-based
information in order to get the rest of the data asked for in the original
query. Optionally, you can add an index to the temp table to speed up the
next stage's query (I usually do).

ALTER TABLE tmpStateList add key(state_ID);

SELECT v.veg_name
      , v.veg_id
      , u.user_id
      , u.user_name
      , IFNULL( t.title_name, 'Untitled' ) AS title_name
      , tsl.country_name
      , tsl.nice_country_name
      , te.equip_name
      , CONCAT( ui.first_name, ' ',ui.last_name ) AS full_name
FROM tmpStateList tsl
INNER JOIN veg AS v
      tsl.state_id = v.state_id
INNER JOIN users AS u
      ON u.user_id = v.user_id
      AND u.acct_status = 'Enabled'
INNER JOIN user_intros AS ui
      ON ui.user_id = u.user_id
      AND ui.lang_id =0
LEFT JOIN veg_titles AS t
      ON t.veg_id = v.veg_id
      AND t.lang_id =0
LEFT JOIN tech_equip AS te
      ON te.equip_id = v.equip_id
WHERE  v.latest_version = 'Y'
      AND v.cur_status = 'Active'
ORDER BY v.date_submitted DESC
LIMIT 0 , 10

You could flip the deconstruction/reconstruction  process I just thought
through. Create a temp table that contains the state_id and the other
veg-based columns (where state.lang_id=0) then JOIN back to it the
district, country, and country language tables to fill in the rest of the
query.

Sometimes deconstructing a complex, multi-stage join like this one and
analyzing each branch of the join separately, it is possible to further
minimize the number of rows at each stage of the join. It just requires a
little more work as you are, in essence, forcing yourself to think like the
query optimizer. Each stage has the potential to be optimized (like the
joins between veg and the user_xx tables, perhaps?). By breaking a single
larger statement down into smaller, more manageable joins, you can get
significant performance enhancement at the expense of having to hand-code
the additional steps that the optimizer would normally *try* to do for you
automatically.  Personally, I have improved the performance of a rather
complex join from about 20 minutes to just about 2 seconds by using this
technique so I know it works if done correctly.

If you could, would you please let me know how the split-up query operates
for you? Thanks in advance.

Yours,
Shawn Green
Database Administrator
Unimin Corporation - Spruce Pine





|---------+---------------------------->
|         |           "Doug V"         |
|         |           <[EMAIL PROTECTED]>|
|         |                            |
|         |           07/09/2004 04:31 |
|         |           PM               |
|         |                            |
|---------+---------------------------->
  
>--------------------------------------------------------------------------------------------------------------------------------|
  |                                                                                    
                                            |
  |       To:       [EMAIL PROTECTED]                                                  
                                        |
  |       cc:                                                                          
                                            |
  |       Fax to:                                                                      
                                            |
  |       Subject:  Re: How to Speed this Query Up?                                    
                                            |
  
>--------------------------------------------------------------------------------------------------------------------------------|




Thank you for your detailed response.

>You might get better performance just from using the explicit INNER JOINS
>but I make no assumptions.

I tried INNER JOINS and did not see any difference in speed.

>You may also get better performance if you had
>composite indexes (not just several individual field indexes) on the
tables
>that contain all of the columns used in each JOIN clause.  For example you
>could have an index on user_intros with (user_id, lang_id) and the engine
>won't have to read the table to check for either condition as they would
>both be integer values that exist in an index.

I am already using composite indexes for every table with a lang_id field,
like user_intros.

>ALSO NOTE: there is no condition in the ON clause of loc_countries_lang
>that relates that table to any other.  This means that for all values in
>the veg table you will need to match one row from the loc_countries_lang
>table that has lang_id=0. If there are more than one languages that match
>that key, you will get multiple sets of matches.

Well, I was thinking, since this table will never really change, and there
only a couple of hundred entries, I should just store this as an array and
get the country name directly from the array. However, when I removed the
loc_countries_lang table from the query, it was still slow, i.e. 3-5
seconds
on production server.

Would changing the LEFT JOIN to an INNER JOIN improve things? It would be
possible to change the logic such that the veg_titles table is used in an
INNER JOIN instead, but when I tried that it was still very slow (3-5
seconds). In this instance the EXPLAIN returned the following:

t ALL veg_lang_id NULL NULL NULL 76001 Using where; Using temporary; Using
filesort

Every other table in the EXPLAIN returned one row with type eq_ref

>Please, let me know if I helped or not, OK?

Yes, thank you for your response, however the respone time is still too
slow. Now I'm thinking that maybe my underlying database structure is not
correct. Am I joining too many tables? Is there anything else I can do
before I try increasing the sort_buffer? Thanks.

_________________________________________________________________
MSN Toolbar provides one-click access to Hotmail from any Web page ? FREE
download! http://toolbar.msn.click-url.com/go/onm00200413ave/direct/01/


--
MySQL General Mailing List
For list archives: http://lists.mysql.com/mysql
To unsubscribe:    http://lists.mysql.com/[EMAIL PROTECTED]







--
MySQL General Mailing List
For list archives: http://lists.mysql.com/mysql
To unsubscribe:    http://lists.mysql.com/[EMAIL PROTECTED]

Reply via email to