我想根据这两个表显示这样的摘要

My two MYSQL tables are as follows:

Table 1 : citizen

=============================
ID |  Name | Sex    | Address |
=============================
5  | James | Male   | India
6  | Shella|Female  | India
7  | Jan   | Male   | NY
8  | May   | Female | USA
==============================

Table 2: benefits

========================== 
ID| citizen_ID | benefits
==========================
1 | 5          | SSS
2 | 6          | Coco Life
3 | 7          | SSS
4 | 7          | Sunlife
==========================

I want to display that looks like this:

====================================================================
Address | Total Citizen | Male | Female | SSS | Coco Life | Others |
====================================================================
India   | 2             |  1   |  1     |  1  |    1      |   0    |
NY      | 1             |  1   |  0     |  1  |    0      |   1    |
USA     | 1             |  0   |  1     |  0  |    0      |   0    | 
==================================================================

Anybody can give me a hint on how to do this? My initial code:

        $sql = "SELECT  Address,
            count(case when Sex='Male' then 1 end) as male_cnt,
            count(case when Sex='Female' then 1 end) as female_cnt,
            count(*) as total_cnt FROM citizen
            GROUP BY Address";

You are on the right track. You now simply need to do a Left Join from the Address table to the benefits table. Left join will allows us to consider a Address even when there is no corresponding benefits entry for any of its citizens.

In order to count total citizens, male count and female count, you now need to use COUNT(DISTINCT ID) after the join. As Joining may create duplicate rows, as a citizen may have more than one benefits.

Also, in order to count "Other" benefits, we need to ensure that the benefit IS NOT NULL and it is NOT IN ('SSS', 'Coco Life').

In multi-table queries, it is advisable to use Aliasing for Code clarity (readability) and avoiding ambiguous behaviour.

SELECT  
  c.Address,
  COUNT(DISTINCT CASE WHEN c.Sex = 'Male' THEN c.ID END) AS male_cnt,
  COUNT(DISTINCT CASE WHEN c.Sex = 'Female' THEN c.ID END) AS female_cnt,
  COUNT(DISTINCT c.ID) AS total_citizen_cnt, 
  COUNT(CASE WHEN b.benefits = 'SSS' THEN 1 END) AS SSS_cnt, 
  COUNT(CASE WHEN b.benefits = 'Coco Life' THEN 1 END) AS Coco_Life_cnt, 
  COUNT(CASE WHEN b.benefits IS NOT NULL AND 
                  b.benefits NOT IN ('SSS', 'Coco Life') THEN 1 END) AS Others_cnt 
FROM citizen AS c 
LEFT JOIN benefits AS b 
  ON b.citizen_ID = c.ID 
GROUP BY c.Address

After countless hours of testing, getting help from a cat in code writing, singing shaman songs and ect... I think i made an exapmle that gives desired output. I tested it a bit, and it seems to work.

However, no matter what i did SQL alone was not enogh to get the job done. And in desperation, I had to resort to using php for realization. And even using php it proved to be a challange.

Below I am pasting my take on this problem, using mainly php. I only hope that it may help some one or give someone good idea about refactoring the code and making logic easier.

<?php

//Connect to DB
$mysql = new mysqli("127.0.0.1", "root", "", "test");

if ($mysql->connect_error) {
    die("Connection failed: " . $mysql->connect_error);
} 

/*Simplified query - we only get id, address, sex and benefits from our two tabels.
  LEFT JOIN is necessary, because we do not want to miss citizenz that have no benefits.
*/
$sql_statement = "select citizen.ID, citizen.Address, citizen.Sex, benefits.benefits from citizen left join benefits on citizen.ID=benefits.citizen_id";

$results = $mysql->query($sql_statement);
if(!$results)
{
    die("Result is empty! Check the query or data inside tables");
}

/*
* Here we initialize some variables to keep track of further calculations
* 
*   @total_women - array($counry => $amount) - Total number of women, divided by countries, and number of men in each country
*   @total_men - array($counry => $amount) - Total number of men, divided by countries, and number of men in each country
*   @total_benefits - array($counry => array($benefit => $amount)) - Total number of benefits, divided by countries, benefit types and number of benefits in each country
*   @ids - array() - Citizen id's from DB. We have to keep track of them, in order to avoid "extra" people in outr results
*   @Addresses - array() - Addresses from DB. We have to keep track of them to avoid duplicate addresses in our results
*   @$benefit_names() - array() - Keeps the names of benefits, whick we want to count seperatly, All other benefits are "Others"
*/
$total_women = array();
$total_men = array();
$total_benefits = array();
$ids = array();
$Addresses = array();
$benefit_names=array('SSS', 'Coco Life');

//Iterating over recieved results from DB 
foreach($results as $result) {

    //Getting all necessary data
    $id = $result['ID'];
    $address = $result['Address'];
    $sex = $result['Sex'];
    $benefit = $result['benefits'];

    //Ensuring that we don't get duplicate Addresses
    if(!in_array($address, $Addresses)) {
        array_push($Addresses, $address);
    }

    //Ensuring we don't get extra people
    if(!in_array($id, $ids))
    {
        array_push($ids, $id);
        if($sex=='Male') {
            //Dividing men by address
            if(array_key_exists($address, $total_men))
            {
                $total_men[$address]++;
            }
            else
            {
                $total_men[$address] = 1;
            }
        }
        else if($sex=='Female') {
            //Dividing women by address
            if(array_key_exists($address, $total_women))
            {
                $total_women[$address]++;
            }
            else
            {
                $total_women[$address] = 1;
            }
        }
    }

    //Ensuring a person has a benefit
    if($benefit) {
        //Dividing benefits by address
        if(array_key_exists($address, $total_benefits)) {
            //Dividing benefits by benefit name.
            if(in_array($benefit, $benefit_names))
            {
                if(array_key_exists($benefit, $total_benefits[$address])) {
                    $total_benefits[$address][$benefit]++;
                }
                else {
                    $total_benefits[$address][$benefit]=1;
                }
            }
            else if(array_key_exists('Others', $total_benefits[$address]))
            {
                $total_benefits[$address]['Others']++;
            }
            else {
                $total_benefits[$address]['Others'] = 1;
            }
        }
        else {
            if(in_array($benefit, $benefit_names)) {
                $total_benefits[$address][$benefit] = 1;
            }
            else {
                $total_benefits[$address]['Others'] = 1;
            }
        }
    }    
}

//Here after all the manipulations, our hash map looks ugly, to make your life eaier later we fill up the missing parts with 0
foreach($Addresses as $address) {
    foreach($benefit_names as $name) {
        if(!isset($total_benefits[$address][$name])) {
            $total_benefits[$address][$name]=0;
        }
    }
    if(!isset($total_benefits[$address]['Others'])) {
        $total_benefits[$address]['Others']=0;
    }
}


/*
*  At this point all the neseccary calculations are made. We only have to take care of outputting the data.
*/
?>

<html>
    <body>
    <table>
        <tr>
            <th>Counry</th>
            <th>Total citizen</th>
            <th>Male</th>
            <th>Female</th>
            <?php
            //Here we make sure that all our separate benefit names get theit own column.
            foreach($benefit_names as $benefit) { ?>
                <th><?php echo $benefit; ?></th>
            <?php } 
            //After we displayed all our separate benefits, we display the "Others" column 
            ?>
            <th>Others</th>
        </tr>
    <?php
        $temp;
        //Here we go through all the unique addresses that we met earlier, ad make a roww for each of them
        foreach($Addresses as $address) { ?>
            <tr>
                <td><?php echo $address; //Outputting current address?></td>
                <td>
                <?php
                    //Here we take care of calculating total citizens based on our current address and output them
                    $total_citizen = 0;
                    if(array_key_exists($address, $total_men)) $total_citizen+=$total_men[$address];
                    if(array_key_exists($address, $total_women)) $total_citizen+=$total_women[$address];
                    echo $total_citizen;
                ?>
                </td>
                <!--Here we display number of women, based on our corrent address-->
                <td><?php if(array_key_exists($address, $total_men)) echo $total_men[$address]; else echo "0"; ?></td>
                <!--Here we display number of men, based on our corrent address-->
                <td><?php if(array_key_exists($address, $total_women)) echo $total_women[$address]; else echo "0"; ?></td>
                <?php
                /*
                *  Here is probably the maddest piece of php code.
                *  We have to make sure that the layout of our data values corresponds with our Column headers layout.
                *  For that, we first iterate over our separate benefit names.
                *  By doing this we ensure that all our seperate benefit names are filled with some data
                *  After we filled all seperate benefits are filled, we make sure that "Others" column is also filled with data
                */
                foreach($benefit_names as $benefit) {
                    //Here we have to make sure that there were some benefits. If there were no benefits, then we fill them with 0.
                    if($total_benefits) { ?>
                        <td><? echo $total_benefits[$address][$benefit]; ?>
                    <?php } else { ?>
                            <td>0</td>
                    <?php }
                }
                //Again, have to make sure that there were some benefits. If there were no benefits, then we fill them with 0.
                if($total_benefits) { ?>
                    <td><?php echo $total_benefits[$address]['Others'] ?></td>
                <?php } else { ?>
                    <td>0<td/>
                <?php } ?>
            </tr>
        <?php } ?>
    </table>
    <body>
</html>

I placed comments as much as I could, in order to make it easier to understand, what this piece of code does.