Jeff Drew wrote:
> I have a query with a subquery which does not throw an error, but does not
> return either.   I've been testing the query using mysql Query Browser and
> the poor dolphin jumps only once a minute or so ;)   I use MYSQL's
> excellent error documentation heavily so if the query doesn't throw an
> error, I'm at a disadvantage.  Is there an optimization or just better
> syntax to use?
>
> THE DATA
> I have a table that contains an abbreviated identifier and a full
> identifier.  The real data is a bit messy so here's a sanitized example:

Please don't do that. When you don't show us the real problem, you increase the odds of getting the wrong solution.

> Abbreviated Column contents:   TR123, RG456
> Full Identifier Column contents: TR 123 abc, RG 456 def

Do I understand correctly that your table looks something like

  AbbreviatedIdentifier  FullIdentifier     Name           Address
  ---------------------  --------------     ------------   ---------------
  TR123                  TR 123 abc         a name         an address
  RG456                  RG 456 def         another name   another address

with FullIdentifier as the primary key? If so, that's a poor table design, which is almost certainly causing your problems. See below.

> THE QUERY
> My intent is to:
> 1. select some of the Abbreviated Column and convert that to a selection
> for the Full Identifier Column by:
>        - extracting the first 2 characters
>        - inserting a space
>        - selecting the last 3 characters
>        - appending "%" so I can match any of the last 3 characters in the
> Full Identifier

...in the subquery. The % character is the wildcard for LIKE matches. There is no wildcard for IN. IN is followed by a list of values, one of which must match exactly for the row to be selected, so this won't work as intended.

> 2. select rows from Full Identifier Column based on #1

That's very convoluted.  How do you expect this to help?

I'm guessing that FullIdentifier is your primary key. Because it's a long, messy string, you are finding it slow to use it to select rows, particularly when you need to find rows with a particular substring buried in the key. You hoped that a shorter string might match more quickly, enabling you to narrow down the number of rows where the full id has to be examined. That will never work as you've described it. The subquery might run faster, but then you must do the full id comparison anyway to find rows which match the subquery list (FullIdentifier IN ...). You've actually added overhead. This *might* work in some situations if you had an integer primary key to use to do the matching between inner and outer query, but there would probably still be a better way.

In any case, the reality is that MySQL doesn't optimize subqueries all that well, so they are seldom the best way to speed up a query. It may be the case that your subquery is being treated as DEPENDENT, in which case it is being run once for each row found in the outer query. That is, once for every row in your table. Since your query never seems to return, I'd bet that's the case here.

> PROBLEMS
> I think I have two problems:
> 1. "in... %" syntax  is not present in any examples I've seen.  They are
> all "like... %"  so "in" may not work.

Right, it won't.

> 2. Here's query that runs, but does not return:
<reformatted>
>
> SELECT name, address
> FROM testTable
> WHERE FullIdentifier
>       IN ( SELECT CONCAT(SUBSTRING(AbbreviatedIdentifier,1,2), " " ,
>                          SUBSTRING(AbbreviatedIdentifier FROM 3) , "%" )
>            FROM testTable
>            WHERE name LIKE 'Daisy'));

Is this really your query? As I've already mentioned, the % won't work for IN. Also, neither FullIdentifier nor AbbreviatedIdentifier is mentioned in the selection criteria, so the problem you tried to fix with the subquery is not present in this query. Finally, there is no wildcard in the string that name is supposed to match, so there is no need for LIKE. The intent of this query is equivalent to

  SELECT name, address
  FROM testTable
  WHERE name = 'Daisy';

which should be as fast as possible if there is an index on name.

> My left join attempt complained  because the data is all in one table. Is
> there a better solution than my  FullIdentifier in(select...  ?
>
> I am not an SQL expert so I'd appreciate any ideas on how to correct this
> query.

When I see a string primary key that looks like "TR 123 abc", I find it is usually a safe bet that the "TR" means something (a code, a manufacturer, ...), the "123" means something (a production run, a part number, a plant number, ...) and the "abc" means something (some detail, a team code, ...). In other words, you've crammed the answers to 3 questions into one column. That's almost never a good idea.

I would (for example):

#Create an auto_inc id column and 3 separate columns for each of the id parts
  ALTER TABLE testTable
  DROP PRIMARY KEY,
  ADD id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST,
  ADD code CHAR(2) NOT NULL AFTER id,
  ADD part INTEGER UNSIGNED NOT NULL AFTER code,
  ADD detail CHAR(3) NOT NULL AFTER model;

#Split the identifier into the new columns
  UPDATE testTable
  SET code = SUBSTRING(AbbreviatedIdentifier,1,2),
      part = SUBSTRING(AbbreviatedIdentifier FROM 3),
      detail = SUBSTRING(FullIdentifier FROM 8);

#Delete the no-longer-needed identifier columns
#and require the code-model-detail combination is unique
  ALTER TABLE testTable
  DROP AbbreviatedIdentifier,
  DROP FullIdentifier,
  ADD UNIQUE (code, model, detail);

Now your table looks like this:

 id  code  model  detail  name         ...
 --  ----  -----  ------  ------------
  1  TR      123  abc     a name       ...
  2  RG      456  def     another name ...

(Of course, you'll want to change "code", "model", and "detail" to column names that actually describe the three parts of your FullIdentifier, and you'll probably need to change the column definitions to match your needs.)

Now you can run queries like:

  SELECT CONCAT(code, " ", model, " ", detail) AS FullIdentifier, name, address
  FROM testTable
  WHERE code = 'TR' AND model = 123
    AND detail LIKE '%b%';

The multi-column index on (code, model, detail) should make this efficient.

Of course, I've based all this on your "sanitized" query. If I'm off-target, include the output of "SHOW CREATE TABLE testTable" in your next post, along with the results of putting EXPLAIN in front of your real query.

Michael

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

Reply via email to