Jump to content
JDevOnline

Query for One To Many Relation In The Same Table

Recommended Posts

Posted (edited)

Hi Barand,

I am seeking you help to make an appropriate query to fetch data from monthly fee table of mysql db, whereby data is stored in such a way that each row stores the data of a student for one month, there are several rows per student.

I want to fetch the data such that the result set should have the one row per student with multiple columns for months, each column showing the status of fee, paid or not.

I have created the following query but the problem is that it is showing only one month which is '2020-03-01'.

 

SELECT DISTINCT name, month, paid from mfee WHERE month >= '2020-03-01' GROUP BY name order by name

 

Desired-Result.jpg

Edited by JDevOnline

Share this post


Link to post
Share on other sites

Easiest way I know is first to store your query data in an array that has the same structure as your required output

<?php
$start = '2020-03-01';

//
// Query to get column headings
//
$res = $conn->prepare("SELECT DISTINCT
                              month(month)     as mno
                            , monthname(month) as mname
                       FROM fee
                       WHERE month >= ?
                       ORDER BY mno    
                       ");
$res->execute([$start]);
$heads = [];
foreach ($res as $row) {
    $heads[$row['mno']]  = $row['mname'];
}
$empty_array = array_fill_keys(array_keys($heads), '');

//
//  now process the fee data and store in an array
//
$data = [];
$res = $conn->prepare("SELECT name
                            , month(month) as mno
                            , paid
                       FROM fee
                       WHERE month >= ? 
                       ORDER BY name, month
                      ");
$res->execute([$start]);
foreach ($res as $row) {
    if (!isset($data[$row['name']])) {
        $data[$row['name']] = $empty_array;
    }
    $data[$row['name']][$row['mno']] = $row['paid'];
}

//
//  now output the array
//
$theads = "<tr><th>Name</th><th>" . join('</th><th>', $heads) . "</th></tr>\n";
$tdata = '';
foreach ($data as $name => $paid_arr) {
    $tdata .= "<tr><td>$name</td><td>" . join('</td><td>', $paid_arr) . "</td></tr>\n";
}
?>
<html>
<head>
<title>Sample</title>
<style type="text/css">
     body, table {
         font-family: verdana, sans-serif;
         font-size: 11pt;
     }
     table {
         width: 400px;
     }
     th {
         background-color: black;
         color: white;
     }
     td {
         text-align: center;
     }
</style>
</head>
<body>
    <table>
    <?=$theads?>
    <?=$tdata?>
    </table>
</body>
</html>

image.png.ad1513dfb69be5354e32581e93aaaf1c.png

Share this post


Link to post
Share on other sites

Barand thank you very much for the prompt reply. After make 2 changes to your code I saved the code into a php file and called it into the browser, but it is showing few errors, kindly let me know how to remove these errors.

Changes:

Table name changed to "mfee" at both the occurrences.

In both the queries "WHERE month >= ?" is changed to "WHERE month >= '$start'"

 

Output:

error.png

Share this post


Link to post
Share on other sites

I want to complete the task today, I would therefore be extremely grateful if you could help me remove the error asap, thanks.

Share this post


Link to post
Share on other sites

Hello Barand,

Thank you very much! That perfectly resolved my issue, I am once again very grateful to you.

Share this post


Link to post
Share on other sites

Hi Barand,

Using CSS I want to highlight the cells where paid is equal to "N". I can't figure out where to insert this code due to arrays. Please help:

<td 
    <?php
    		if($row['paid']=='N'){echo 'class="highlight"';}
    ?>
</td>

 

Share this post


Link to post
Share on other sites

You need to rewrite this line ...

 $tdata .= "<tr><td>$name</td><td>" . join('</td><td>', $paid_arr) . "</td></tr>\n";

so you output the name then loop through the $paid_array.

Share this post


Link to post
Share on other sites

Hello Barand,

I've tried to change the code you mentioned as below but it is still now working. Please help me modify the code to achieve the desired results:

if($paid_arr == 'Y'){$cls='class="hilite"';}else{$cls='class="centex"';}
echo "<tr><td class='centex'>$index</td><td>$name</td><td $cls>" . join("</td><td $cls>", $paid_arr) . "</td></tr>\n";

 

Share this post


Link to post
Share on other sites
foreach ($data as $name => $paid_arr) {
    $tdata .= "<tr><td>$name</td>";
    foreach ($paid_arr as $p) {
        $cls = $p=='N' ? 'hilite' : '';
        $tdata .= "<td class='$cls'>$p</td>";
    }
    $tdata .= "</tr>\n";
}

image.png.0ecab09218fc2aa0da00cb432dcadafb.png

Share this post


Link to post
Share on other sites

Hi Barand,

I now want to extract the details of students who have paid all 2 months, i.e. march, april and may. I have written the following query but it is not bringing any records, however there are many students who have paid all 3 months.

 

SELECT nroll.id as id, nroll.sname, nroll.ctclass, parents.memail, parents.femail FROM nroll 
INNER JOIN parents on nroll.id=parents.id 
INNER JOIN mfee on nroll.id=mfee.sid 
WHERE nroll.ctstudent = 'Y' and nroll.ctclass='1 Reception-1' and nroll.shift='1' and 
(mfee.month='2020-01-01' and mfee.month='2020-02-01') 
ORDER BY nroll.sname

 

Share this post


Link to post
Share on other sites
3 hours ago, JDevOnline said:

who have paid all 2 months, i.e. march, april and may.

Ah yes , the three types of programmer, those who can count and those who can't

Use a table subquery to find those who paid all two and match against it with a join

SELECT name
	, month(month) as mno
	, paid
FROM fee
     JOIN (
            SELECT name
                 , count(*)
            FROM fee
            WHERE month(month) IN (3,4,5) AND paid = 'Y'
            GROUP BY name
            HAVING count(*) = 3
          ) paid3 USING (name)
     
WHERE month >= '2020-03-01' 
ORDER BY name, month

 

Share this post


Link to post
Share on other sites
7 hours ago, Barand said:

Ah yes , the three types of programmer, those who can count and those who can't

😇

Share this post


Link to post
Share on other sites

Hi Barand, thanks for the help. How can we show just one row per student's name and only two columns, name and "Y", those who paid all 3 months.

Share this post


Link to post
Share on other sites
Posted (edited)
4 hours ago, JDevOnline said:

How can we show just one row per student's name and only two columns, name and "Y"

In a very to similar manner to the code already given.

SELECT name
     , 'Y' as paid
FROM fee
WHERE month(month) IN (3,4,5) AND paid = 'Y'
GROUP BY name
HAVING count(*) = 3;

 

Edited by Barand

Share this post


Link to post
Share on other sites
On 5/28/2020 at 1:48 PM, Barand said:

foreach ($data as $name => $paid_arr) {
    $tdata .= "<tr><td>$name</td>";
    foreach ($paid_arr as $p) {
        $cls = $p=='N' ? 'hilite' : '';
        $tdata .= "<td class='$cls'>$p</td>";
    }
    $tdata .= "</tr>\n";
}

image.png.0ecab09218fc2aa0da00cb432dcadafb.png

Hi Barand,

I want to insert one more value to the above table after "Name", like email or phone number. Please tell me how can that value be put inside your loops?

 

Share this post


Link to post
Share on other sites

Change your query to retrieve the extra item and restructure the $data array to store it.

$data = [];
$res = $conn->prepare("SELECT name
                            , email                             -- extra item
                            , month(month) as mno
                            , paid
                       FROM fee
                       WHERE month >= ? 
                       ORDER BY name, month
                      ");
$res->execute([$start]);
foreach ($res as $row) {
    if (!isset($data[$row['name']])) {
        $data[$row['name']] = [ 'email' => $row['email'], 
                                'pays' => $empty_array ];
    }
    $data[$row['name']]['pays'][$row['mno']] = $row['paid'];
}

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.