Sorry for the long post.
Hmm, didn't think about left join. That changes things, obviously.
I don't think DISTINCT is in my interests. That might work for unique names, emails, and so forth. But, I could have multiple customers with the same name. Maybe even the same name and email combo (for whatever reason. Just safeguarding here...).
Anyway, I've been reiterating the functions today. I'm having some problems figuring out how to get the best of both worlds here. I ran about 700 (automated) tests on each of the three methods below. The test being the letters from a-z then all combinations of (a-z)^2 (676 tests + 26 for single chars).
Forgive the naming conventions but the idea is that Fastest was my attempt before posting here. FastestRedux is using your sql and reworking the method I was using to find out if customers were checked in already (Basically reduces it from a 170ms query to 10-15ms). FastestReduxCombo is using my old sql query but now with the new way to find out if customers were checked in already.
Here's the functions in testing form:
Faster
Code:
function searchCustomersFastest($args){
$timingArray = array();
$name = $args['name'];
$limit = $args['limit'];
$eventID = $args['eventID'];
$numInSystemSQL =
"SELECT COUNT(*) as count
FROM customers
WHERE customers.on = '1'
AND name LIKE '%$name%'
";
$start_time = microtime(TRUE);
$numInSystemQuery = mysql_query($numInSystemSQL) or die (returnSQLError($numInSystemSQL));
$numInSystemNumber = mysql_fetch_array($numInSystemQuery);
$end_time = microtime(TRUE);
$timingArray[0] = ($end_time - $start_time);
$numInSystemNumber = $numInSystemNumber['count'];
$highestVisitsAndLikeName =
"SELECT COUNT(ch.on) AS visits, cu.id AS cid, cu.name AS name, cu.email AS email
FROM checkins AS ch
RIGHT OUTER JOIN customers AS cu ON ch.customer_id = cu.id
WHERE cu.name LIKE '%$name%'
AND cu.on = '1'
GROUP BY cu.id
ORDER BY visits DESC, name ASC, cu.id DESC
" . ($numInSystemNumber > ($limit + 1) ? ("LIMIT " . $limit) : "");
$start_time = microtime(TRUE);
$visitquery = mysql_query($highestVisitsAndLikeName) or die (returnSQLError(mysql_error()));
$end_time = microtime(TRUE);
$timingArray[1] = ($end_time - $start_time);
$alreadycheckedinsql =
"SELECT customers.name AS cname, checkins.customer_id AS cid, checkins.payment AS payment
FROM checkins
JOIN customers
ON checkins.customer_id = customers.id
WHERE event_id = '$eventID'
AND checkins.on = '1'
AND customers.on = '1'
AND customer_id IN (
SELECT cu.id AS cid
FROM checkins AS ch
RIGHT OUTER JOIN customers AS cu ON ch.customer_id = cu.id
WHERE cu.name LIKE '%$name%'
AND cu.on = '1'
GROUP BY cu.id
ORDER BY COUNT(ch.on) DESC, name ASC, cu.id DESC
)";
$start_time = microtime(TRUE);
$alreadycheckedinquery = mysql_query($alreadycheckedinsql) or die (returnSQLError($alreadycheckedinsql));
$end_time = microtime(TRUE);
$timingArray[2] = ($end_time - $start_time);
$alreadycheckedin = array();
while($tmp = mysql_fetch_array($alreadycheckedinquery)){
$alreadycheckedin[$tmp['cid']] = $tmp['payment'];
}
$keysAlreadyCheckedIn = array_keys($alreadycheckedin);
$customerArray = array();
$start_time = microtime(TRUE);
while($visit = mysql_fetch_array($visitquery)){
$st = microtime(TRUE);
$cid = $visit['cid'];
$name = $visit['name'];
$visits = $visit['visits'];
$isCheckedIn = in_array($cid, $keysAlreadyCheckedIn);
$checkinID = getCheckinIDForCustomerAndEvent($cid, $eventID);
$usedFreeEntrance = $checkinID ? hasUsedFreeEntrance($cid, $checkinID) : false;
$numberOfFreeEntrances = getNumberOfFreeEntrances($cid);
array_push($customerArray,
array(
"cid" => $cid,
"email" => $visit['email'],
"payment" => ($isCheckedIn ? $alreadycheckedin[$cid] : ''),
"name" => $name,
"visits" => $visits,
"isCheckedIn" => $isCheckedIn,
"usedFreeEntrance" => $usedFreeEntrance,
"numberOfFreeEntrances" => $numberOfFreeEntrances,
)
);
$timingArray[4] = (microtime(TRUE) - $st);
}
$end_time = microtime(TRUE);
$timingArray[3] = ($end_time - $start_time);
$returnJSON = array();
$returnJSON['customers'] = $customerArray;
$returnJSON['timingArray'] = $timingArray;
if($numInSystemNumber > $limit + 1){
$returnJSON['numberOfExtra'] = $numInSystemNumber - $limit;
} else {
$returnJSON['numberOfExtra'] = 0;
}
return json_encode($returnJSON);
}
Here's FastestRedux:
Code:
function searchCustomersFastestRedux($args){
$timingArray = array();
$name = $args['name'];
$limit = $args['limit'];
$eventID = $args['eventID'];
$numInSystemSQL =
"SELECT COUNT(*) as count
FROM customers
WHERE customers.on = '1'
AND name LIKE '%$name%'
";
$start_time = microtime(TRUE);
$numInSystemQuery = mysql_query($numInSystemSQL) or die (returnSQLError($numInSystemSQL));
$numInSystemNumber = mysql_fetch_array($numInSystemQuery);
$end_time = microtime(TRUE);
$timingArray[0] = ($end_time - $start_time);
$numInSystemNumber = $numInSystemNumber['count'];
$highestVisitsAndLikeName =
"SELECT customers.id as cid, customers.name as name, customers.email as email, IFNULL(checkins.visits, 0) as visits
FROM customers
LEFT JOIN
(SELECT COUNT(checkins.on) AS visits, customer_id
FROM checkins
GROUP BY customer_id)
AS checkins
ON checkins.customer_id=customers.id
WHERE customers.name LIKE '%$name%'
AND customers.on = '1'
ORDER BY checkins.visits DESC, customers.name ASC, customers.id DESC
" .($numInSystemNumber > ($limit + 1) ? ("LIMIT " . $limit) : "");
$start_time = microtime(TRUE);
$visitquery = mysql_query($highestVisitsAndLikeName) or die (returnSQLError(mysql_error()));
$end_time = microtime(TRUE);
$timingArray[1] = ($end_time - $start_time);
$customerArray = array();
$start_time = microtime(TRUE);
while($visit = mysql_fetch_array($visitquery)){
$st = microtime(TRUE);
$cid = $visit['cid'];
$name = $visit['name'];
$visits = $visit['visits'];
$isCheckedIn = getCustomerCheckedInPayment($cid, $eventID);
$checkinID = getCheckinIDForCustomerAndEvent($cid, $eventID);
$usedFreeEntrance = $checkinID ? hasUsedFreeEntrance($cid, $checkinID) : false;
$numberOfFreeEntrances = getNumberOfFreeEntrances($cid);
array_push($customerArray,
array(
"cid" => $cid,
"email" => $visit['email'],
"payment" => ($isCheckedIn == null ? "" : $isCheckedIn),
"name" => $name,
"visits" => $visits,
"isCheckedIn" => ($isCheckedIn != null),
"usedFreeEntrance" => $usedFreeEntrance,
"numberOfFreeEntrances" => $numberOfFreeEntrances,
)
);
$timingArray[3] = (microtime(TRUE) - $st);
}
$end_time = microtime(TRUE);
$timingArray[2] = ($end_time - $start_time);
$returnJSON = array();
$returnJSON['customers'] = $customerArray;
$returnJSON['timingArray'] = $timingArray;
if($numInSystemNumber > $limit + 1){
$returnJSON['numberOfExtra'] = $numInSystemNumber - $limit;
} else {
$returnJSON['numberOfExtra'] = 0;
}
return json_encode($returnJSON);
}
Here's fastestReduxCombo:
Code:
function searchCustomersFastestReduxCombo($args){
$timingArray = array();
$name = $args['name'];
$limit = $args['limit'];
$eventID = $args['eventID'];
$numInSystemSQL =
"SELECT COUNT(*) as count
FROM customers
WHERE customers.on = '1'
AND name LIKE '%$name%'
";
$start_time = microtime(TRUE);
$numInSystemQuery = mysql_query($numInSystemSQL) or die (returnSQLError($numInSystemSQL));
$numInSystemNumber = mysql_fetch_array($numInSystemQuery);
$end_time = microtime(TRUE);
$timingArray[0] = ($end_time - $start_time);
$numInSystemNumber = $numInSystemNumber['count'];
$highestVisitsAndLikeName =
"SELECT COUNT(ch.on) AS visits, cu.id AS cid, cu.name AS name, cu.email AS email
FROM checkins AS ch
RIGHT OUTER JOIN customers AS cu ON ch.customer_id = cu.id
WHERE cu.name LIKE '%$name%'
AND cu.on = '1'
GROUP BY cu.id
ORDER BY visits DESC, name ASC, cu.id DESC
" . ($numInSystemNumber > ($limit + 1) ? ("LIMIT " . $limit) : "");
$start_time = microtime(TRUE);
$visitquery = mysql_query($highestVisitsAndLikeName) or die (returnSQLError(mysql_error()));
$end_time = microtime(TRUE);
$timingArray[1] = ($end_time - $start_time);
$customerArray = array();
$start_time = microtime(TRUE);
while($visit = mysql_fetch_array($visitquery)){
$st = microtime(TRUE);
$cid = $visit['cid'];
$name = $visit['name'];
$visits = $visit['visits'];
$isCheckedIn = getCustomerCheckedInPayment($cid, $eventID);
$checkinID = getCheckinIDForCustomerAndEvent($cid, $eventID);
$usedFreeEntrance = $checkinID ? hasUsedFreeEntrance($cid, $checkinID) : false;
$numberOfFreeEntrances = getNumberOfFreeEntrances($cid);
array_push($customerArray,
array(
"cid" => $cid,
"email" => $visit['email'],
"payment" => ($isCheckedIn == null ? "" : $isCheckedIn),
"name" => $name,
"visits" => $visits,
"isCheckedIn" => ($isCheckedIn != null),
"usedFreeEntrance" => $usedFreeEntrance,
"numberOfFreeEntrances" => $numberOfFreeEntrances,
)
);
$timingArray[3] = (microtime(TRUE) - $st);
}
$end_time = microtime(TRUE);
$timingArray[2] = ($end_time - $start_time);
$returnJSON = array();
$returnJSON['customers'] = $customerArray;
$returnJSON['timingArray'] = $timingArray;
if($numInSystemNumber > $limit + 1){
$returnJSON['numberOfExtra'] = $numInSystemNumber - $limit;
} else {
$returnJSON['numberOfExtra'] = 0;
}
return json_encode($returnJSON);
}
Here's samples of run times:
For faster, ignore statements 3-4. Ignore 2-3 for fasterredux, ignore 2-3 for fasterreduxcombo. The statement number references timingArray[>>>>number<<<<] in the code above.
Code:
These next results have a lot of results... check the last one though.
NAME: a, LENGTH: 1
Begin result 0
Benchmarking searchCustomersFastest with param: a
Time to perform: 0.45402598381042
Time to perform Statements 0: 0.024001121520996 Statements 1: 0.246013879776 Statements 2: 0.17800998687744 Statements 3: 0.0039999485015869 Statements 4: 0
Benchmarking searchCustomersFastestRedux with param: a
Time to perform: 0.14100813865662
Time to perform Statements 0: 0.0099999904632568 Statements 1: 0.12700700759888 Statements 2: 0.0040011405944824 Statements 3: 0
Benchmarking searchCustomersFastestReduxCombo with param: a
Time to perform: 0.25001406669617
Time to perform Statements 0: 0.010999917984009 Statements 1: 0.23401403427124 Statements 2: 0.005000114440918 Statements 3: 0.0010001659393311
End result 0
NAME: b, LENGTH: 1
Begin result 1
Benchmarking searchCustomersFastest with param: b
Time to perform: 0.085004806518555
Time to perform Statements 0: 0.0099999904632568 Statements 1: 0.029001951217651 Statements 2: 0.043002843856812 Statements 3: 0.003000020980835 Statements 4: 0
Benchmarking searchCustomersFastestRedux with param: b
Time to perform: 0.096005201339722
Time to perform Statements 0: 0.011000156402588 Statements 1: 0.081004858016968 Statements 2: 0.004000186920166 Statements 3: 0
Benchmarking searchCustomersFastestReduxCombo with param: b
Time to perform: 0.043002843856812
Time to perform Statements 0: 0.010000944137573 Statements 1: 0.029001951217651 Statements 2: 0.0039999485015869 Statements 3: 0
End result 1
NAME: c, LENGTH: 1
Begin result 2
Benchmarking searchCustomersFastest with param: c
Time to perform: 0.099005222320557
Time to perform Statements 0: 0.011000156402588 Statements 1: 0.035001993179321 Statements 2: 0.050003051757812 Statements 3: 0.003000020980835 Statements 4: 0
Benchmarking searchCustomersFastestRedux with param: c
Time to perform: 0.098005056381226
Time to perform Statements 0: 0.0099999904632568 Statements 1: 0.084005117416382 Statements 2: 0.0039999485015869 Statements 3: 0
Benchmarking searchCustomersFastestReduxCombo with param: c
Time to perform: 0.051002979278564
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.036002159118652 Statements 2: 0.0039999485015869 Statements 3: 0
End result 2
NAME: d, LENGTH: 1
Begin result 3
Benchmarking searchCustomersFastest with param: d
Time to perform: 0.12400698661804
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.047002077102661 Statements 2: 0.062004089355469 Statements 3: 0.0039999485015869 Statements 4: 0
Benchmarking searchCustomersFastestRedux with param: d
Time to perform: 0.10000610351562
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.085005044937134 Statements 2: 0.004000186920166 Statements 3: 0
Benchmarking searchCustomersFastestReduxCombo with param: d
Time to perform: 0.062002897262573
Time to perform Statements 0: 0.010999917984009 Statements 1: 0.046002864837646 Statements 2: 0.005000114440918 Statements 3: 0.00099992752075195
End result 3
NAME: e, LENGTH: 1
Begin result 4
Benchmarking searchCustomersFastest with param: e
Time to perform: 0.26201510429382
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.11700701713562 Statements 2: 0.13000702857971 Statements 3: 0.004000186920166 Statements 4: 0
Benchmarking searchCustomersFastestRedux with param: e
Time to perform: 0.11600685119629
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.10100603103638 Statements 2: 0.0039999485015869 Statements 3: 0.00099992752075195
Benchmarking searchCustomersFastestReduxCombo with param: e
Time to perform: 0.13400793075562
Time to perform Statements 0: 0.011001110076904 Statements 1: 0.11800599098206 Statements 2: 0.0050008296966553 Statements 3: 0.00099992752075195
End result 4
...
...
...
...
...
...
This has 0 results.
NAME: qq, LENGTH: 2
Begin result 16
Benchmarking searchCustomersFastest with param: qq
Time to perform: 0.039001941680908
Time to perform Statements 0: 0.0099999904632568 Statements 1: 0.010000944137573 Statements 2: 0.019001007080078 Statements 3: 0
Benchmarking searchCustomersFastestRedux with param: qq
Time to perform: 0.020001173019409
Time to perform Statements 0: 0.010001182556152 Statements 1: 0.0099999904632568 Statements 2: 0
Benchmarking searchCustomersFastestReduxCombo with param: qq
Time to perform: 0.021000862121582
Time to perform Statements 0: 0.011000871658325 Statements 1: 0.0099999904632568 Statements 2: 0
End result 16
Here's results for a full run:
Code:
TOTAL RUNS: 702
Total run time for benchmarkSearchCustomers (Fastest):
37.668162345886,
average run time: 0.053658350920066,
median time: 0.049003124237061,
longest time: 0.45402598381042,
standard deviation: 0.025842059549636
Total run time for benchmarkSearchCustomers2 (FastestRedux):
59.079370260239,
average run time: 0.084158647094357,
median time: 0.093004941940308,
longest time: 0.14100813865662,
standard deviation: 0.024883380426155
Total run time for benchmarkSearchCustomers3 (FastestReduxCombo):
19.38410782814,
average run time: 0.027612689213875,
median time: 0.0260009765625,
longest time: 0.25001406669617,
standard deviation: 0.013567375694995
Clearly, FastestReduxCombo is the quickest. It has a lower average, lower total run time, and smallest standard deviation. Something I noticed while working on this though is that for "statements 1" on FastestRedux (your version of the sql), the statement has consistently high run times. Outside of returning nothing, it frequently takes 80ms+.
The only problem with FastestReduxCombo (my sql query) is that the longest time is nearly double what the quickest is. 250ms is a long time for a query to calculate. And it will happen a lot because this search is pinged every time someone types into a prompt. (It is a _.throttled prompt because of these problematic speed constraints)
Here are the current indexes that I added:
On checkins
*Primary - id
*customer_id, id - index on two columns: customer_id, id
*customer_id, event_id - index on two columns: customer_id, event_id
On customers
*Primary - id
*id, name - index on two columns: id, name
*name - FULLTEXT index on name (This didn't do anything. Cardinality - 1, I can run analyze/repair/check table and it'll populate to 34k or whatever but then it goes back to 1 a few seconds later when I run my tests. Maybe it's because I run RESET QUERY CACHE before all my functions start? (So each one has a fresh start...))
I removed the name index and got the same results.
Thoughts?