Jonathon
Jonathon

Reputation: 798

Query efficiency (multiple selects)

I have two tables - one called customer_records and another called customer_actions.

customer_records has the following schema:

CustomerID (auto increment, primary key)
CustomerName
...etc...

customer_actions has the following schema:

ActionID (auto increment, primary key)
CustomerID (relates to customer_records)
ActionType
ActionTime (UNIX time stamp that the entry was made)
Note (TEXT type)

Every time a user carries out an action on a customer record, an entry is made in customer_actions, and the user is given the opportunity to enter a note. ActionType can be one of a few values (like 'designatory update' or 'added case info' - can only be one of a list of options).

What I want to be able to do is display a list of records from customer_records where the last ActionType was a certain value.

So far, I've searched the net/SO and come up with this monster:

SELECT * FROM (
    SELECT * FROM (
        SELECT * FROM `customer_actions` ORDER BY `EntryID` DESC
    ) list1 GROUP BY `CustomerID`
) list2 WHERE `ActionType`='whatever' LIMIT 0,30

Which is great - it lists each customer ID and their last action. But the query is extremely slow on occasions (note: there are nearly 20,000 records in customer_records). Can anyone offer any tips on how I can sort this monster of a query out or adjust my table to give faster results? I'm using MySQL. Any help is really appreciated, thanks.

Edit: To be clear, I need to see a list of customers who's last action was 'whatever'.

Upvotes: 3

Views: 148

Answers (4)

MatBailie
MatBailie

Reputation: 86706

To filter customers by their last action, you could use a correlated sub-query...

SELECT
  *
FROM
  customer_records
INNER JOIN
  customer_actions
    ON  customer_actions.CustomerID = customer_records.CustomerID
    AND customer_actions.ActionDate = (
           SELECT
             MAX(ActionDate)
           FROM
             customer_actions  AS lookup
           WHERE
             CustomerID = customer_records.CustomerID
        )
WHERE
  customer_actions.ActionType = 'Whatever'

You may find it more efficient to avoid the correlated sub-query as follows...

SELECT
  *
FROM
  customer_records
INNER JOIN
  (SELECT CustomerID, MAX(ActionDate) AS ActionDate FROM customer_actions GROUP BY CustomerID) AS last_action
    ON customer_records.CustomerID = last_action.CustomerID
INNER JOIN
  customer_actions 
    ON  customer_actions.CustomerID = last_action.CustomerID
    AND customer_actions.ActionDate = last_action.ActionDate
WHERE
  customer_actions.ActionType = 'Whatever'

Upvotes: 5

xQbert
xQbert

Reputation: 35323

Maybe I'm missing something but what's wrong with a simple join and a where clause?

Select ActionType, ActionTime, Note
FROM Customer_Records CR
INNER JOIN customer_Actions CA 
  ON CR.CustomerID = CA.CustomerID
Where ActionType = 'added case info'

Upvotes: 1

Michael A
Michael A

Reputation: 9900

Please note that I've adapted Lieven's answer (I made a separate post as this was too long for a comment). Any credit for the solution itself goes to him, I'm just trying to show you some key points for improving performance.

If speed is a concern then the following should give you some suggestions for improving it:

select top 100 -- Change as required
        cr.CustomerID ,
        cr.CustomerName,
        cr.MoreDetail1,
        cr.Etc
from    customer_records cr
        inner join customer_actions ca 
            on ca.CustomerID = cr.CustomerID
where   ca.ActionType = 'x'
order by cr.CustomerID

A few notes:

  • In some cases I find left outer joins to be faster then inner joins - It would be worth measuring performance for both for this query
  • Avoid returning * wherever possible
  • You don't have to reference 'cr.x' in the initial select but it's a good habit to get into for when you start working on large queries that can have multiple joins in them (this will make a lot of sense once you start doing this
  • When using joins always join on a primary key

Upvotes: 2

Lieven Keersmaekers
Lieven Keersmaekers

Reputation: 58431

I'm not sure if I understand the requirements but it looks to me like a JOIN would be enough for that.

SELECT  cr.CustomerID, cr.CustomerName, ...
FROM    customer_records cr
        INNER JOIN customer_actions ca ON ca.CustomerID = cr.CustomerID
WHERE   `ActionType` = 'whatever'
ORDER BY
        ca.EntryID

Note that 20.000 records should not pose a performance problem

Upvotes: 2

Related Questions