简体   繁体   中英

Possible to reduce number of queries from 5 to 1?

I have a gallery of "Leaders" on my site, where the top users in 5 different categories are selected by a DESC LIMIT 1; the gallery is generated with 5 separate INNER JOIN between the stats and login tables, but all the SELECT clauses are identical:

$cat1wins="SELECT stats.login_id, stats.cat1wins, login.user, login.picture,      
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id ORDER by 
stats.cat1wins DESC LIMIT 1";

$cat2wins="SELECT stats.login_id, stats.cat2wins, login.user, login.picture,      
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id ORDER by 
stats.cat2wins DESC LIMIT 1";

$cat3wins="SELECT stats.login_id, stats.cat3wins, login.user, login.picture,      
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id ORDER by 
stats.cat3wins DESC LIMIT 1";

$cat4wins="SELECT stats.login_id, stats.cat4wins, login.user, login.picture,      
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id ORDER by 
stats.cat4wins DESC LIMIT 1";

$cat5wins="SELECT stats.login_id, stats.cat5wins, login.user, login.picture,      
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id ORDER by 
stats.cat5wins DESC LIMIT 1";

For each category, a WHILE loop extracts the proper table cell (shown here with cat1)

$result1 = mysqli_query($con,$cat1wins);
WHILE ($Category1Wins = mysqli_fetch_assoc($result1)) {
$Cat1User = $Category1Wins['user'];
$Cat1Statement = $Category1Wins['statement'];
echo "code"; //etc
}

I imagine there must be a better way to do this; I imagine I could start like this:

$catAllwins="SELECT stats.cat1wins, stats.login_id, login.user, login.picture,      
login.statement, SELECT stats.cat2wins, stats.login_id, login.user, login.picture, 
login.statement,.....SELECT stats.cat5wins, stats.login_id, login.user, login.picture, 
login.statement FROM stats INNER JOIN login ON stats.login_id=login.login_id";

and then use a WHILE loop on the results of each SELECT statement within $catAllwins to generate 5 separate arrays that can then be sorted with a natsort . However, I am not sure how to extract the proper SELECT statements. I was thinking I could say something like:

$catAllwins="SELECT stats.cat1wins, stats.login_id, login.user, login.picture,      
login.statement AS 'array1', SELECT stats.cat2wins, stats.login_id, login.user,    
login.picture, login.statement AS 'array2',.....SELECT stats.cat5wins, stats.login_id,  
login.user, login.picture, login.statement AS 'array5' FROM stats INNER JOIN login ON 
stats.login_id=login.login_id";

$resultAll = mysqli_query($con, $catAllwins)
$Cat1WinsArray = $resultAll['array1'];
$Cat2WinsArray = $resultAll['array2'];
$Cat3WinsArray = $resultAll['array3'];
$Cat4WinsArray = $resultAll['array4'];
$Cat5WinsArray = $resultAll['array5'];

then sort the arrays with natsort (one example shown)

$SortedCat1WinsArray = natsort($Cat1WinsArray);

then add array_reverse for descending order:

$DescSortedCat1WinsArray = array_reverse($SortedCat1WinsArray);

then WHILE this array to extract each component I need (eg, $row['picture'], $row['login_id'], etc.). Does this sound remotely plausible? I would appreciate any feedback! Is there an easier way?

You can just UNION the individual select statements together and apply a sort to the resulting union. Here is an example:

(SELECT stats.login_id, stats.cat1wins, login.user, login.picture, login.statement
FROM stats INNER JOIN login ON stats.login_id=login.login_id
ORDER by stats.cat1wins DESC LIMIT 1)
UNION
(SELECT stats.login_id, stats.cat2wins, login.user, login.picture, login.statement
FROM stats INNER JOIN login ON stats.login_id=login.login_id
ORDER by stats.cat2wins DESC LIMIT 1)
UNION
(SELECT stats.login_id, stats.cat3wins, login.user, login.picture, login.statement
FROM stats INNER JOIN login ON stats.login_id=login.login_id
ORDER by stats.cat3wins DESC LIMIT 1)
UNION
(SELECT stats.login_id, stats.cat4wins, login.user, login.picture, login.statement
FROM stats INNER JOIN login ON stats.login_id=login.login_id
ORDER by stats.cat4wins DESC LIMIT 1)
UNION
(SELECT stats.login_id, stats.cat5wins, login.user, login.picture, login.statement
FROM stats INNER JOIN login ON stats.login_id=login.login_id
ORDER by stats.cat5wins DESC LIMIT 1)
ORDER BY /* Whatever you want to sort by. Wasn't clear in the questoin */

Sorting by five different variables is expensive. You might find the following approach to be more efficient:

select l.login_id,
       (case when s.cat1wins = smax.max_cat1wins then 'cat1'
             when s.cat2wins = smax.max_cat2wins then 'cat2'
             when s.cat3wins = smax.max_cat3wins then 'cat3'
             when s.cat4wins = smax.max_cat4wins then 'cat4'
             when s.cat5wins = smax.max_cat5wins then 'cat5'
         end) as which,
       (case when s.cat1wins = smax.max_cat1wins then s.cat1wins
             when s.cat2wins = smax.max_cat2wins then s.cat2wins
             when s.cat3wins = smax.max_cat3wins then s.cat3wins
             when s.cat4wins = smax.max_cat4wins then s.cat4wins
             when s.cat5wins = smax.max_cat5wins then s.cat5wins
         end) as numwins,
       l.user, l.picture, l.statement,
from stats s inner join
     (select max(stats.cat1wins) as max_cat1wins,
             max(stats.cat2wins) as max_cat2wins,
             max(stats.cat3wins) as max_cat3wins,
             max(stats.cat4wins) as max_cat4wins,
             max(stats.cat5wins) as max_cat5wins
      from stats
     ) smax
     on s.cat1wins = smax.max_cat1wins or
        s.cat2wins = smax.max_cat2wins or
        s.cat3wins = smax.max_cat3wins or
        s.cat4wins = smax.max_cat4wins or
        s.cat5wins = smax.max_cat5wins join
     login l
     on s.login_id = l.login_id;

There are two important differences in doing it this way, compared to your original five queries. First, if someone is tops in two or more categories, they only appear once here. Second, if mutliple people are tied for the best in one category, all are included.

Updated

getting userid for each max categories

What about this? this has no GROUP BY and needs just 1 full scan.

    SELECT
        IF(cat1wins >= @mxc1, @mxu1 := login_id, @mxu1) AS userid1,
        IF(cat1wins >= @mxc1, @mxc1 := cat1wins, @mxc1) AS max_cat1wins,

        IF(cat2wins >= @mxc2, @mxu2 := login_id, @mxu2) AS userid2,
        IF(cat2wins >= @mxc2, @mxc2 := cat2wins, @mxc2) AS max_cat2wins,

        IF(cat3wins >= @mxc3, @mxu3 := login_id, @mxu3) AS userid3,
        IF(cat3wins >= @mxc3, @mxc3 := cat3wins, @mxc3) AS max_cat3wins,

        IF(cat4wins >= @mxc4, @mxu4 := login_id, @mxu4) AS userid4,
        IF(cat4wins >= @mxc4, @mxc4 := cat4wins, @mxc4) AS max_cat4wins,

        IF(cat5wins >= @mxc5, @mxu5 := login_id, @mxu5) AS userid5,
        IF(cat5wins >= @mxc5, @mxc5 := cat5wins, @mxc5) AS max_cat5wins
    FROM stats, (
        SELECT
          @mxc1 := 0, @mxc2 := 0, @mxc3 := 0, @mxc4 := 0, @mxc5 := 0,
          @mxu1 := 0, @mxu2 := 0, @mxu3 := 0, @mxu4 := 0, @mxu5 := 0
    ) x;

finally we get login information using join with login table

Please note that following query returns 1~5 records. (1 for all max user are same. 5 for each max user are diffrent)

SELECT
    y.*, login.login_id, login.user, login.picture, login.statement,
    IF(y.userid1 = login.login_id, max_cat1wins, NULL) AS max1,
    IF(y.userid2 = login.login_id, max_cat2wins, NULL) AS max2,
    IF(y.userid3 = login.login_id, max_cat3wins, NULL) AS max3,
    IF(y.userid4 = login.login_id, max_cat4wins, NULL) AS max4,
    IF(y.userid5 = login.login_id, max_cat5wins, NULL) AS max5
FROM 
(
    SELECT
        IF(cat1wins >= @mxc1, @mxu1 := login_id, @mxu1) AS userid1,
        IF(cat1wins >= @mxc1, @mxc1 := cat1wins, @mxc1) AS max_cat1wins,

        IF(cat2wins >= @mxc2, @mxu2 := login_id, @mxu2) AS userid2,
        IF(cat2wins >= @mxc2, @mxc2 := cat2wins, @mxc2) AS max_cat2wins,

        IF(cat3wins >= @mxc3, @mxu3 := login_id, @mxu3) AS userid3,
        IF(cat3wins >= @mxc3, @mxc3 := cat3wins, @mxc3) AS max_cat3wins,

        IF(cat4wins >= @mxc4, @mxu4 := login_id, @mxu4) AS userid4,
        IF(cat4wins >= @mxc4, @mxc4 := cat4wins, @mxc4) AS max_cat4wins,

        IF(cat5wins >= @mxc5, @mxu5 := login_id, @mxu5) AS userid5,
        IF(cat5wins >= @mxc5, @mxc5 := cat5wins, @mxc5) AS max_cat5wins
    FROM stats, (
        SELECT
          @mxc1 := 0, @mxc2 := 0, @mxc3 := 0, @mxc4 := 0, @mxc5 := 0,
          @mxu1 := 0, @mxu2 := 0, @mxu3 := 0, @mxu4 := 0, @mxu5 := 0
    ) x
) y INNER JOIN login
WHERE login.login_id IN (userid1, userid2, userid3, userid4, userid5);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM