簡體   English   中英

優化高級SQL查詢

[英]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生成了一個缺少付款的學生列表。 它還顯示他們已付款的月份。

SQL小提琴

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM