Search code examples
mysqlgroup-concat

Using GROUP_CONCAT creatively to avoid a query...can groups of records be retrieved?


I run a website with a small number of users that I hope will soon grow. I am using a shared web hosting service, and I cannot change MySQL default settings on the web server. I am trying to minimize queries and have found one recurring pair of queries (occurs every 10 seconds via setTimeout to Ajax call) where GROUP_CONCAT could help reduce the following 2 queries:

$logid = mysqli_real_escape_string($cxn, $_SESSION['login_id']);

$sql1 = mysqli_query($cxn, "SELECT COUNT(bid) AS count FROM table1 WHERE login_id = '$logid'");

$sql2 = mysqli_query($cxn, "SELECT cid FROM table2 WHERE (login_id1 = '$logid' OR login_id2 = '$login_id')");

to one query:

SELECT COUNT(table1.bid) AS count,
GROUP_CONCAT(table2.cid) AS cidList 
FROM table1 
LEFT JOIN table2
ON (table1.login_id = table2.login_id1 OR table1.login_id = table2.login_id2)
WHERE table1.login_id = '$logid';

The problem is that group_concat_max_len is set by default to 1024, and I called my shared hosting service and I cannot change it. Every table2.cid in the query above is 20 characters, so (including the comma separator) GROUP_CONCAT can only return 48 table2.cid records, but I need to be able to return as many as 1000. I already know that I can change this default limit with a query like this:

SET SESSION group_concat_max_len = 1000000;

but this added query defeats the purpose, because even if the two queries above are condensed to one query, I have now added this latest query, so every time the Ajax call is made two queries are still made (and I haven't saved anything).

Is there some way to cluster the GROUP_CONCAT where you retrieve some of the records (table2.cid) for each GROUP_CONCAT? For example, something like (say for 100 records):

SELECT COUNT(table1.bid) AS count,
GROUP_CONCAT(table2.cid ORDER BY table2.cid LIMIT 0,47) AS cidList1,
GROUP_CONCAT(table2.cid ORDER BY table2.cid LIMIT 48,95) AS cidList2,
GROUP_CONCAT(table2.cid ORDER BY table2.cid LIMIT 96,99) AS cidList3
FROM table1 
LEFT JOIN table2
ON (table1.login_id = table2.login_id1 OR table1.login_id = table2.login_id2)
WHERE table1.login_id = '$logid';

By the way I tried a test case and the query fails by adding the LIMIT, because without the LIMIT the correct PHP echoed string is returned and with the LIMIT, an empty PHP echoed string is returned plus error log messag:

PHP Warning:  mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, boolean given in

Solution

  • Just issue a regular SELECT table1.bid, table2.cid FROM .... And if bid is never NULL, then SELECT cid will suffice (in this case, COUNT(bid) = COUNT(cid))

    Counting bid's and joining the list of cid's in your application layer will be virtually instant. The building of the comma-separated list of cid definitely belongs to this layer anyways.

    As for the counting of bid's, this is less obvious. If you really want to have MySQL do the counting and still save one database call, I would rather do something like this:

    SELECT COUNT(table1.bid) FROM table1 WHERE blah blah
    UNION ALL
    SELECT cid FROM table2 WHERE blah blah
    

    This is theory, though. Seriously, do not do that.


    On second thought, there actually is a less awkward solution:

    SELECT COUNT(table1.bid), table2.cid FROM ...
    GROUP BY table2.cid WITH ROLLUP
    

    The last row, where table2.cid is NULL, contains the total count.