[英]Optimize Advance SQL Query
學生表:存儲學生的基本信息
fee_received表:存儲一年中每個月每個學生支付的每月學費的信息。
問題:
我需要讓所有在2018年最后7個月內成為Fee_defaulters的學生。最重要的是,我需要為每個學生提供所有這些月(例如:3、4、6),以便我可以向管理員顯示學生是哪個月份的費用拖欠者。
表架構
CREATE TABLE `fee_received` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`challan_no` int(11) NOT NULL DEFAULT '0',
`student_id` int(11) NOT NULL,
`class_id` int(2) NOT NULL,
`amount` int(5) NOT NULL,
`arrears` int(6) NOT NULL DEFAULT '0' COMMENT 'arrears in monthly tuition fee.',
`fine` int(11) NOT NULL DEFAULT '0' COMMENT 'total fine in current challan',
`fee_month` int(2) NOT NULL,
`fee_year` int(4) NOT NULL,
`dt` date NOT NULL,
`multi_fee` tinyint(11) NOT NULL DEFAULT '0',
`submitted_with_admission` tinyint(1) NOT NULL DEFAULT '0',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 = received , 2 = pending, 3 = deleted'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`reg_no` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
`f_name` varchar(50) NOT NULL,
`cnic1` varchar(20) DEFAULT NULL,
`cnic` varchar(20) DEFAULT NULL,
`caste` varchar(50) DEFAULT NULL,
`occupation` varchar(100) DEFAULT NULL,
`dob` date NOT NULL,
`gender` enum('M','F','O','') NOT NULL DEFAULT 'M',
`contact` varchar(13) NOT NULL,
`address` varchar(500) NOT NULL,
`admission_id` int(11) DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
當前未優化的工作解決方案: 注意:我已經非常簡化了StackOverflow的解決方案,只是為了顯示問題區域
以下sub_query獲取所有已支付全部7個月費用的學生。
$already_paid_student_ids_query = "SELECT t.student_id
FROM (
SELECT COUNT(*) AS count, student_id
FROM fee_received
WHERE fee_month IN (1, 2, 3, 4, 5, 6, 7)
AND fee_year = 2018
AND class_id = 10
AND status = 1
GROUP BY student_id
) AS t
WHERE t.count >= 7";
然后,我有以下主要查詢,這些查詢使我成為所有默認收費的學生。
$fee_defaulters_query = "SELECT student.* FROM student
WHERE student.id NOT IN ( {$already_paid_student_ids_query} )";
到此為止,我已經收到所有違約金。 但是我不知道哪個學生哪個月是費用拖欠者。 因此,我采用了非常昂貴的解決方案。
我已經遍歷了每個默認費用者,並請求fee_received表來為學生獲取幾個月的時間。
$paid_months_query = "SELECT fee_month FROM fee_received WHERE student_id = {$student_id} AND fee_year = {$fee_year} AND fee_month IN (1,2,3,4,5,6,7) AND status = 1 ";
然后使用一些編程,我得到每個學生的無薪月份,並顯示給管理員。
所需的解決方案
我需要擺脫循環內的查詢。 並且需要一個解決方案,這樣我就可以在單個查詢中獲得所有fee_defaulter學生和每個學生的無薪月份列表。
SQL Fiddle 鏈接
對於每個學生,您期望在某些月份內付款。 因此,將所有月份與所有學生結合在一起。 然后查看缺少付款的地方。
select student.id as student_id, months.year, months.month
from
(
select 2018 as year, 1 as month
union all
select 2018 as year, 2 as month
union all
select 2018 as year, 3 as month
union all
select 2018 as year, 4 as month
union all
select 2018 as year, 5 as month
union all
select 2018 as year, 6 as month
union all
select 2018 as year, 7 as month
) months
cross join student
where (student.id, months.year, months.month) not in
(
select student_id, fee_year, fee_month
from fee_received
where class_id = 10
and status = 1
)
order by student_id, months.year, months.month;
(我只是從您的查詢中復制了class_id條件。如果這是並非所有學生都參加的特定課程,則您可能希望以某種方式限制學生-可能使用您未提及的某些student_class表。)
如果您希望每個學生一行,那么您必須匯總:
select
student.id as student_id,
group_concat(concat(months.year, '/', months.month)) as unpaid_months
from
(
select 2018 as year, 1 as month
union all
select 2018 as year, 2 as month
union all
select 2018 as year, 3 as month
union all
select 2018 as year, 4 as month
union all
select 2018 as year, 5 as month
union all
select 2018 as year, 6 as month
union all
select 2018 as year, 7 as month
) months
cross join student
where (student.id, months.year, months.month) not in
(
select student_id, fee_year, fee_month
from fee_received
where class_id = 10
and status = 1
)
group by student.id
order by student.id;
我創建了一個SQL Fiddle,該SQL Fiddle生成了一個缺少付款的學生列表。 它還顯示他們已付款的月份。
MySQL 5.6模式設置 :
CREATE TABLE `fee_received` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`challan_no` int(11) NOT NULL DEFAULT '0',
`student_id` int(11) NOT NULL,
`class_id` int(2) NOT NULL,
`amount` int(5) NOT NULL,
`arrears` int(6) NOT NULL DEFAULT '0' COMMENT 'arrears in monthly tuition fee.',
`fine` int(11) NOT NULL DEFAULT '0' COMMENT 'total fine in current challan',
`fee_month` int(2) NOT NULL,
`fee_year` int(4) NOT NULL,
`dt` date NOT NULL,
`multi_fee` tinyint(11) NOT NULL DEFAULT '0',
`submitted_with_admission` tinyint(1) NOT NULL DEFAULT '0',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 = received , 2 = pending, 3 = deleted'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`reg_no` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
`f_name` varchar(50) NOT NULL,
`cnic1` varchar(20) DEFAULT NULL,
`cnic` varchar(20) DEFAULT NULL,
`caste` varchar(50) DEFAULT NULL,
`occupation` varchar(100) DEFAULT NULL,
`dob` date NOT NULL,
`gender` enum('M','F','O','') NOT NULL DEFAULT 'M',
`contact` varchar(13) NOT NULL,
`address` varchar(500) NOT NULL,
`admission_id` int(11) DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `student`
(`reg_no`, `name`, `f_name`, `cnic1`, `cnic`, `caste`, `occupation`, `dob`, `gender`, `contact`, `address`, `admission_id`)
VALUES
(1123,'John Doe','John','1','1','1','student','2000-05-01','M','123-456-7890','123 S Western Ave',123),
(1533,'Jane Doe','Jane','1','1','1','student','2000-05-01','F','123-456-7890','123 S Western Ave',123),
(2341,'Fred Smith','Fred','1','1','1','student','2000-05-01','M','123-456-7890','123 S Western Ave',123),
(6541,'Tanya Edilstien','Tanya','1','1','1','student','2000-05-01','M','123-456-7890','123 S Western Ave',123);
INSERT INTO `fee_received`
(`challan_no`, `student_id`, `class_id`, `amount`, `arrears`, `fine`, `fee_month`, `fee_year`, `dt`, `multi_fee`, `submitted_with_admission`, `status`)
VALUES
(1,1,1,123,0,0,1,2018,'2018-01-04',0,0,1),
(1,1,1,123,0,0,2,2018,'2018-02-04',0,0,1),
(1,1,1,123,0,0,3,2018,'2018-03-04',0,0,1),
(1,1,1,123,0,0,4,2018,'2018-04-04',0,0,1),
(1,1,1,123,0,0,5,2018,'2018-05-04',0,0,1),
(1,1,1,123,0,0,6,2018,'2018-06-04',0,0,1),
(1,1,1,123,0,0,7,2018,'2018-07-04',0,0,1),
(2,2,2,123,0,0,1,2018,'2018-01-04',0,0,1),
(2,2,2,123,0,0,4,2018,'2018-04-04',0,0,1),
(2,2,2,123,0,0,5,2018,'2018-05-04',0,0,1),
(2,2,2,123,0,0,6,2018,'2018-06-04',0,0,1),
(2,2,2,123,0,0,7,2018,'2018-07-04',0,0,1),
(3,3,3,123,0,0,1,2018,'2018-01-04',0,0,1),
(3,3,3,123,0,0,2,2018,'2018-02-04',0,0,1),
(3,3,3,123,0,0,3,2018,'2018-03-04',0,0,1),
(3,3,3,123,0,0,4,2018,'2018-04-04',0,0,1),
(3,3,3,123,0,0,5,2018,'2018-05-04',0,0,1),
(3,3,3,123,0,0,7,2018,'2018-07-04',0,0,1),
(4,4,4,123,0,0,1,2018,'2018-01-04',0,0,1),
(4,4,4,123,0,0,2,2018,'2018-02-04',0,0,1),
(4,4,4,123,0,0,3,2018,'2018-03-04',0,0,1),
(4,4,4,123,0,0,4,2018,'2018-04-04',0,0,1),
(4,4,4,123,0,0,5,2018,'2018-05-04',0,0,1),
(4,4,4,123,0,0,6,2018,'2018-06-04',0,0,1),
(4,4,4,123,0,0,7,2018,'2018-07-04',0,0,1);
查詢1 :
select
a.`id`,
a.`name`,
a.`contact`,
a.`address`,
count(b.`id`) as `num_payments`,
GROUP_CONCAT(b.`fee_month` ORDER BY b.`fee_month`) as `Months_Paid`
FROM `student` a
LEFT JOIN `fee_received` b
ON a.`id` = b.`student_id` AND b.`status` = 1 AND b.`dt` BETWEEN '2018-01-01' AND '2018-07-31'
GROUP BY a.`id`
HAVING `num_payments` < 7
結果 :
| id | name | contact | address | num_payments | Months_Paid |
|----|------------|--------------|-------------------|--------------|-------------|
| 2 | Jane Doe | 123-456-7890 | 123 S Western Ave | 5 | 1,4,5,6,7 |
| 3 | Fred Smith | 123-456-7890 | 123 S Western Ave | 6 | 1,2,3,4,5,7 |
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.