PHP查询指定年月未提交付款用户数据问题求助
问题:获取指定年月未提交付款的用户信息
问题描述
我是PHP新手,开发项目时需要获取指定年月未提交付款的用户信息,现有代码无法正常运行,仅需展示该年月未付款的用户名,不知道如何实现IF/else逻辑,恳请帮助。
数据表信息
表名:credit
字段:usename(唯一)、month、Year
示例数据:
| usename | month | Year |
|---|---|---|
| A | November | 2022 |
| B | November | 2022 |
| B | December | 2022 |
场景说明
用户A和B均在2022年11月提交过付款,2022年12月仅B提交了付款,需查询出用户A,期望输出:
| usename | month | Year |
|---|---|---|
| A | November | 2022 |
现有前端表单代码
<form method="post" accept="" class="row g-3"> <div class="col-lg-4"> <label for="val-select2">Month <span class="text-danger">*</span></label> <select class="js-select2 form-select form-control" id="val-select2" name="pay_month" style="width: 100%;" required> <option value="<?php echo date("F")?>" hidden><?php echo date("F")?></option> <option value="January">January</option> <option value="February">February </option> <option value="March">March</option> <option value="April">April</option> <option value="May">May</option> <option value="June">June</option> <option value="July">July</option> <option value="August">August</option> <option value="September">September</option> <option value="October">October</option> <option value="November">November</option> <option value="December">December</option> </select> </div> <div class="col-lg-4"> <label >Year<span class="text-danger">*</span></label> <input type="text" class="form-control" name="pay_year" placeholder="year" required value="<?php echo date("Y");?>"> </div> <div class="col-lg-2"> <label></label> <button class="form-control btn btn-primary" name="save" type="submit"><i class="fa fa-eye" aria-hidden="true"></i> View</button> </div> </form>
现有PHP查询代码
<?php if (isset($_POST['save'])) { $sql = "SELECT * FROM credit WHERE NOT (pay_month='" . $_POST['pay_month'] . "' AND pay_year='" . $_POST['pay_year'] . "') ORDER BY username"; if($result = mysqli_query($db, $sql)){ if(mysqli_num_rows($result) > 0){ $count = 0; while($row = mysqli_fetch_array($result)){ if(mysqli_num_rows($result) > 0) $count ++; echo '<tr id="'.$row["id"].'">'; echo '<td class="fw-semibold">'.$count.'</td>'; echo '<td class="fw-semibold fs-sm text-success"> <a href="single_info.php?name='.$row["usename"].'">'.$row["username"].'</a></td>'; echo "</tr>"; } mysqli_free_result($result); } else{ echo "No data found."; } } else{ echo "ERROR: Could not able to execute $sql. " . mysqli_error($db); } } ?>
问题分析与解决方案
现有代码核心问题
- SQL逻辑错误:原查询
NOT (pay_month='xxx' AND pay_year='xxx')会返回所有非指定年月的记录,比如用户A的11月记录、用户B的11月记录都会被返回,无法精准筛选出未在指定年月付款的用户。 - 字段名不匹配:数据表字段是
usename、month、Year,但SQL中误用pay_month、pay_year,PHP输出中又错写username,导致数据无法正确读取。 - SQL注入风险:直接拼接用户输入到SQL语句,存在安全漏洞。
修正后的实现方案
1. 正确的SQL查询逻辑
要筛选出有付款记录但未在指定年月提交付款的用户,推荐使用子查询或左连接:
方式一:子查询实现
SELECT DISTINCT usename, month, Year FROM credit WHERE usename NOT IN ( SELECT usename FROM credit WHERE month = ? AND Year = ? ) ORDER BY usename
先找出指定年月已付款的用户,再从所有用户中排除这些人,返回他们的历史付款记录。
方式二:左连接实现
SELECT c.usename, c.month, c.Year FROM credit c LEFT JOIN credit c_paid ON c.usename = c_paid.usename AND c_paid.month = ? AND c_paid.Year = ? WHERE c_paid.usename IS NULL GROUP BY c.usename ORDER BY c.usename
通过左连接匹配指定年月的付款记录,筛选出无匹配结果的用户(即未付款用户)。
2. 优化后的PHP代码(带SQL注入防护)
<?php if (isset($_POST['save'])) { // 获取表单输入 $target_month = $_POST['pay_month']; $target_year = $_POST['pay_year']; // 使用预处理语句防止SQL注入 $sql = "SELECT DISTINCT usename, month, Year FROM credit WHERE usename NOT IN ( SELECT usename FROM credit WHERE month = ? AND Year = ? ) ORDER BY usename"; $stmt = mysqli_prepare($db, $sql); mysqli_stmt_bind_param($stmt, "ss", $target_month, $target_year); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if(mysqli_num_rows($result) > 0){ $count = 0; while($row = mysqli_fetch_assoc($result)){ $count++; echo '<tr>'; echo '<td class="fw-semibold">'.$count.'</td>'; echo '<td class="fw-semibold fs-sm text-success"> <a href="single_info.php?name='.$row["usename"].'">'.$row["usename"].'</a> </td>'; // 可选:显示用户最后一次付款的年月 echo '<td>'.$row["month"].'</td>'; echo '<td>'.$row["Year"].'</td>'; echo '</tr>'; } mysqli_free_result($result); } else{ echo "No data found."; } mysqli_stmt_close($stmt); } ?>
关键说明
- 字段名统一:确保代码中使用的字段名与数据表完全一致(
usename、month、Year)。 - 安全防护:必须使用预处理语句,避免直接拼接用户输入,杜绝SQL注入风险。
- 逻辑精准:通过子查询或左连接,准确筛选出未在指定年月付款的用户,而非返回所有非目标年月的记录。
内容的提问来源于stack exchange,提问作者Shamim Hossain
相关产品推荐
相关产品推荐

