我想基于两个表显示这样的摘要

时间:2018-11-08 10:14:16

标签: php mysql

我的两个MYSQL表如下:

表1:公民

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

表2:好处

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

我想显示如下:

====================================================================
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    | 
==================================================================

任何人都可以给我提示如何执行此操作? 我的初始代码:

        $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";

2 个答案:

答案 0 :(得分:2)

您处在正确的轨道上。现在,您只需要从Left Join表到Address表执行benefits。即使没有任何公民的相应Address条目,左联接也使我们可以考虑使用benefits

要计算公民总数,男性人数和女性人数,您现在需要在加入后使用COUNT(DISTINCT ID)。由于加入可能会创建重复的行,因此作为公民可能会获得多个好处。

此外,为了计算“其他”收益,我们需要确保benefit IS NOT NULL确实是NOT IN ('SSS', 'Coco Life')

在多表查询中,建议使用Aliasing来提高代码的清晰度(可读性)并避免模棱两可的行为。

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

答案 1 :(得分:1)

经过无数小时的测试,在代码编写,唱歌萨满歌曲等方面得到了猫的帮助……我想我做了个例子,给出了期望的输出。我测试了一下,它似乎可以正常工作。

但是,无论我做什么,SQL都不足以完成工作。无奈之下,我不得不求助于使用php来实现。甚至使用php也被证明是一个挑战。

下面,我主要使用php粘贴我对这个问题的看法。我只希望它可以对某些人有所帮助,或者给别人一个有关重构代码并使逻辑更容易的好主意。

<?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>

为了尽可能容易地理解这段代码的作用,我尽可能地添加了注释。