0

This is the database:

id_user nama id_sikap kelancaran tahsin
10000000 Budi B 90 90
10000000 Budi A 89 78
10000000 Budi A 90 90
10000001 Ina A 84 44

I want to combine some conditions in one query such as average (tahsin, kelancaran, and akhir), and display the most repeated value in id_sikap

    <?php 
        
        $no = 1;
        
        $sql = "SELECT setoran.id_user, user.id_user, user.nama, setoran.id_sikap, 
AVG(kelancaran) AS avg_kelancaran, AVG(tahsin)  AS avg_tahsin, (tahsin + kelancaran)/2 AS akhir, 
ROW_NUMBER() OVER (PARTITION BY setoran.id_user ORDER BY COUNT(setoran.id_sikap) DESC LIMIT 1) 
FROM setoran INNER JOIN user ON setoran.id_user=user.id_user GROUP BY setoran.id_user, 
setoran.id_sikap ";
        
        $get = mysqli_query($conn,$sql);
                        
        // output data of each row
        while($row = mysqli_fetch_assoc($get)) {
                                  
        ?>
             <tr>
                <td><?php echo $no ?></td>                      
                <td><?php echo $row['nama']  ?></td>                      
                <td><?php echo $row['id_user'] ?></td>                      
                <td><?php echo $row['cabang'] ?></td>                   
                <td><?php echo $row['avg_kelancaran']?></td>
                <td><?php echo $row['avg_tahsin'] ?></td>
                <td><?php echo $row['id_sikap'] ?></td>
                <td><?php echo $row['akhir'] ?></td>
              </tr>                    
        <?php $no++;} 
        ?>

but the result end up like this, it can be seen that there is a repetition in id 10000000 and it's not showing the most repeated but show all the data of id_sikap in id 10000000, I've tried using LIMIT and TOP 1 but it's not working

This is the existing result

id_user nama id_sikap avg_kelancaran avg_tahsin akhir
10000000 Budi B 90 90 90
10000000 Budi A 89.5 84 83.5
10000001 Ina A 84 44 50

The needed result

id_user nama id_sikap avg_kelancaran avg_tahsin akhir
10000000 Budi A 89.75 87 88.375
10000001 Ina A 84 44 50

Is it possible to combine all the conditions that I need in one query? and how?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
NJL
  • 1
  • 1

1 Answers1

0

The way I see it, you'll need to make another query with AVG() function after. So, let's take your current query and inspect where you need to fix:

SELECT setoran.id_user, user.id_user, user.nama, setoran.id_sikap, 
       AVG(kelancaran) AS avg_kelancaran, AVG(tahsin)  AS avg_tahsin, 
       /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         the part above is fine */

       (tahsin + kelancaran)/2 AS akhir, 
       /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         I'm a bit curious how you calculate this but I think you're looking for the 
        combined value of avg and divide by 2. I'll do this on the second part of the 
        query*/

       ROW_NUMBER() 
             OVER (PARTITION BY setoran.id_user 
                         ORDER BY COUNT(setoran.id_sikap) DESC LIMIT 1) 
       /*I assume that you already discovered ROW_NUMBER() can't use LIMIT and as 
         far as I know MySQL don't have TOP function. I'll not be using this.*/

FROM setoran 
INNER JOIN user 
ON setoran.id_user=user.id_user 
GROUP BY setoran.id_user, setoran.id_sikap 
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I'm pretty sure that you turned off only_full_group_by mode otherwise this query 
won't successfully run with these columns in GROUP BY*/

So let's go to the first part of the query:

SELECT s.id_user, u.nama, s.id_sikap,
            AVG(s.kelancaran) AS avg_k, AVG(s.tahsin) AS avg_t
      FROM setoran AS s 
    INNER JOIN user AS u
       ON s.id_user=u.id_user
    GROUP BY s.id_user,
             u.nama,
             s.id_sikap

I retain most of your original query, added and removed a few parts. I've assigned aliases on the table so it's easier to write (s. instead of setoran.) and I also added s.id_sikap in the GROUP BY. We make this base query as a derived table and do another AVG() operation on the initial average result:

SELECT id_user,
       nama,
       AVG(avg_k) AS avg_kelancaran, 
       AVG(avg_t) AS avg_tahsin,
       (AVG(avg_k)+AVG(avg_t))/2 AS akhir
 FROM
    (SELECT s.id_user, u.nama, s.id_sikap,
            AVG(s.kelancaran) AS avg_k, AVG(s.tahsin) AS avg_t
      FROM setoran AS s 
    INNER JOIN user AS u
       ON s.id_user=u.id_user
    GROUP BY s.id_user,
             u.nama,
             s.id_sikap) AS dt1
GROUP BY id_user,
         nama;

I did not include id_sikap column here because it made little sense to show the result as A for Budi since the final average calculation is based on combined id_sikap value. However, if you still want to show them, I suggest you add GROUP_CONCAT(id_sikap) in SELECT instead of just id_sikap.

This is the result of the final query:

id_user nama avg_kelancaran avg_tahsin akhir
10000000 Budi 89.75 87 88.375
10000001 Ina 84 44 64

Notice the akhir value for Ina is different than your expected result of 50 but everything else is the same? I assume that you've made a slight mistake on the calculation since the value akhir for Budi is correct. If not, then maybe there's a logic behind that. If that's the case, we need to know ya.

Demo fiddle

SELECT id_user,
       nama,
       SUBSTRING_INDEX(
           GROUP_CONCAT(id_sikap 
                ORDER BY id_skp_cnt DESC, id_sikap),
                ',',1) AS Most_id_sikap,
       AVG(avg_k) AS avg_kelancaran, 
       AVG(avg_t) AS avg_tahsin,
       (AVG(avg_k)+AVG(avg_t))/2 AS akhir
 FROM
    (SELECT s.id_user, u.nama, s.id_sikap,
            COUNT(s.id_sikap) AS id_skp_cnt,
            AVG(s.kelancaran) AS avg_k, AVG(s.tahsin) AS avg_t
      FROM setoran AS s 
    INNER JOIN user AS u
       ON s.id_user=u.id_user
    GROUP BY s.id_user,
             u.nama,
             s.id_sikap) AS dt1
GROUP BY id_user,
         nama

So what I did on the query was adding COUNT(s.id_sikap) AS id_skp_cnt in the derived table. The purpose for this is to count how many of the said id_sikap appeared in the row based on the grouping condition. Here's the result:

+---------+-----+---------+-----------+--------+--------+
|id_user  |nama |id_sikap |id_skp_cnt |avg_k   |avg_t   |
+---------+-----+---------+-----------+--------+--------+
|10000000 |Budi |    B    |    1      |90.0000 |90.0000 |
|10000000 |Budi |    A    |    2      |89.5000 |84.0000 |
|10000001 |Ina  |    A    |    1      |84.0000 |44.0000 |
+---------+-----+---------+-----------+--------+--------+
                              ^^^
                 count results by existing GROUP BY

Once I did that, I use a combination of two function; the first one is GROUP_CONCAT() where I return id_sikap value with ORDER BY of the count result in descending order (DESC) from the derived table. The result of that will look like this:

nama GROUP_CONCAT(id_sikap ORDER BY id_skp_cnt DESC, id_sikap)
Budi A, B The id_sikap=A for Budi gets the first position in the GROUP_CONCAT() because the count result is larger and I do order by descending
Ina A

As for the second column of id_sikap appeared in the GROUP_CONCAT() order by, the reason is simple if some of the id_sikap have same count then the smaller alphabet value will take the first spot; I leave it for you to decide whether to include or not.

The last function is using SUBSTRING_INDEX() to get the first position value in the GROUP_CONCAT().

FanoFN
  • 6,815
  • 2
  • 13
  • 33