theFinges
theFinges

Reputation: 1

SELECT LEFT JOIN with USING and WHERE AND OR

I'm struggling with the following sql query to display a search result from a mysql db

This works

SELECT
    *
FROM
    lagerbestand
LEFT JOIN
    verkaufspreis USING (materialnummer)
WHERE
    lagerbestand.level='2' AND (lagerbestand.materialnummer='$suche' OR lagerbestand.materialkurztext LIKE '%$suche%')

This works too

SELECT
    *
FROM
    lagerbestand
LEFT JOIN
    verkaufspreis USING (materialnummer)
WHERE
    lagerbestand.materialnummer='$suche' OR verkaufspreis.bezeichnung_fr LIKE '%$suche%'

But this doesn't work

SELECT
    *
FROM
    lagerbestand
LEFT JOIN
    verkaufspreis USING (materialnummer)
WHERE
    lagerbestand.level='2' AND (lagerbestand.materialnummer='$suche' OR verkaufspreis.bezeichnung_fr LIKE '%$suche%')

The only difference is the last bit.

lagerbestand.materialkurztext is changed to verkaufspreis.bezeichnung_fr

Then the script just loads for ever.

Upvotes: 0

Views: 73

Answers (2)

Gordon Linoff
Gordon Linoff

Reputation: 1269753

I would start by using indexes to speed the query. Also, drop the quotes from 2 if it is not necessary:

SELECT *
FROM lagerbestand l LEFT JOIN
     verkaufspreis v
     USING (materialnummer)
WHERE l.level = 2 AND (l.materialnummer = '$suche' OR v.materialnummer LIKE '%$suche%');

For this query, you want an index on lagerbestand(level, bezeichnung_fr) and verkaufspreis(materialnummer, bezeichnung_fr).

I'm not guaranteeing this will result in great performance, but it is a place to start.

EDIT:

OR can be hard to optimize for a query. The next idea is to use union all:

SELECT *
FROM lagerbestand l LEFT JOIN
     verkaufspreis v
     USING (materialnummer)
WHERE l.level = 2 AND l.materialnummer = '$suche'
UNION ALL
SELECT *
FROM lagerbestand l JOIN
     verkaufspreis v
     USING (materialnummer)
WHERE l.level = 2 AND (l.materialnummer <> '$suche' AND v.materialnummer LIKE '%$suche%');

This might be faster, because the subqueries might be optimized more efficiently. You can try each one independently to see if there still a performance problem.

Upvotes: 1

spencer7593
spencer7593

Reputation: 108400

If I wanted to return set specified in the last query, I think the best bet for optimal performance is going to be to break that up into two distinct sets, and combine those with a UNION ALL set operator.

(If $such is meant to be variable that is substituted into the SQL text, I would make sure that it has been properly escaped, before it is included.)

 SELECT l.*, v.*
   FROM lagerbestand l
   LEFT JOIN verkaufspreis v USING (materialnummer)
  WHERE l.level = '2'
    AND l.materialnummer = '$suche'

  UNION ALL

 SELECT l.*, v.*
   FROM lagerbestand l
   JOIN verkaufspreis v USING (materialnummer)
  WHERE l.level = '2'
    AND NOT ( l.materialnummer <=> '$suche' )
    AND v.bezeichnung_fr LIKE CONCAT('%','$suche','%')

Then I would tune each of those SELECT separately, using EXPLAIN, making sure appropriate indexes are available.

 ON verkaufspreis (materialnummer, bezeichnung_fr, ... )

 ON lagerbestand (level, materialnummer, ... )

Upvotes: 0

Related Questions