如何在 GROUP_CONCAT 中做聚合函数

Nar*_*_CH 0 mysql

我正在生成一份报告以显示未结费用,例如哪种费用类型,该费用类型的固定金额,该费用类型支付的金额以及最终该费用类型支付的余额金额。

在这里,我从 5 个表中提取数据,例如来自 Classes 表的 Class Names、来自招生表的 Roll no & Student Name、来自 Feevars 表的 Fee Types、来自 studentfees 表的费用类型固定金额,以及最后从费用收集表中为费用类型支付的金额.

我可以通过加减运算在 select 语句中提及费用类型名称来生成部分结果。

这是完整的数据库和我的查询产生的结果。请在此演示中查看 **@我的查询,在选择语句中我手动提到了费用类型。但是我想生成结果而不将费用类型作为列名。

为此,我做了一件事,我将所有费用类型都放入了这样的 sql 变量中

set @sqlList = null;SELECT GROUP_CONCAT(concat('tsf.', Replace(FeeName,' ',''))) INTO @sqlList FROM tbl_feevars;
Run Code Online (Sandbox Code Playgroud)

这将导致所有费用类型成为单行作为列名。最后,我编写了代码来生成我所期望的输出,但是我收到了类似错误代码 1064:您的 sql 语法错误。

这是我的最终代码

预期输出代码

请任何人告诉我,我的 sql 查询有什么错误。并建议我,如果有的话??其他方式来做这个报告。

我的表:

CREATE TABLE `tbl_admission` (
  `AdmNo` varchar(50) NOT NULL,
  `Grp` varchar(45) NOT NULL,
  `Cls` varchar(45) NOT NULL,
  `rollno` int(11) NOT NULL,  
  `StdNm` varchar(50)
);

CREATE TABLE `tbl_classes` (
  `C_Id` int(11),
  `C_Name` varchar(255) NOT NULL,
  `G_Id` int(11) NOT NULL,
  `status` tinyint(1)
);

CREATE TABLE `tbl_feevars` (
  `id` int(11) NOT NULL,
  `FeeName` varchar(70) NOT NULL
);

CREATE TABLE `tbl_stdfees` (
  `id` int(11),
  `Admno` varchar(50) default NULL,
  `admissionfee` float(10,2) default '0.00',
  `splfee` float(10,2) default '0.00',
  `tutionfee` float(10,2) default '0.00',
  `computerfee` float(10,2) default '0.00',
  `stationary` float(10,2) default '0.00',
  `transport` float(10,2) default '0.00',
  `hostelfee` float(10,2) default '0.00',
  `lab` float(10,2) default '0.00',
  `library` float(10,2) default '0.00',
  `miscell` float(10,2) default '0.00'
  );

CREATE TABLE `tbl_feecollection` (
  `auto_id` int(11) NOT NULL auto_increment,
  `GrupNm` varchar(40) NOT NULL,
  `class` varchar(30) NOT NULL,
  `rollno` int(2) NOT NULL,
  `sadmno` int(11) NOT NULL,
  `PaM` varchar(30) NOT NULL,
  `DDCN` int(25) NOT NULL,
  `bank` varchar(30) NOT NULL,
  `cheqd` date NOT NULL,
  `remarks` varchar(150) NOT NULL,
  `pdate` date NOT NULL,
  `admissionfee` float(10,2) default '0.00',
  `splfee` float(10,2) default '0.00',
  `tutionfee` float(10,2) default '0.00',
  `computerfee` float(10,2) default '0.00',
  `stationary` float(10,2) default '0.00',
  `transport` float(10,2) default '0.00',
  `hostelfee` float(10,2) default '0.00',
  `lab` float(10,2) default '0.00',
  `library` float(10,2) default '0.00',
  `miscell` float(10,2) default '0.00',
  PRIMARY KEY  (`auto_id`)
);
Run Code Online (Sandbox Code Playgroud)

我目前的查询是:

set @sqlList = null;
SET @sqlList1 = null;
set @query = null;
SELECT
  GROUP_CONCAT(concat('tsf.', Replace(FeeName,' ',''))) 
INTO @sqlList
FROM tbl_feevars;
SELECT
  GROUP_CONCAT(concat('tfc.', Replace(FeeName,' ',''))) 
INTO @sqlList1
FROM tbl_feevars;
SET @query 
  = CONCAT('SELECT tsf.Admno,',@sqlList,',ta.StdNm,
                    sum(',@sqlList1,') as ''feepaid'',
                    (',@sqlList,' - sum(',@sqlList1,')) as ''outstanding fee''
                FROM
                    tbl_stdfees tsf,
                    tbl_Admission ta,
                    tbl_feecollection tfc
                where
                    tsf.admno = ta.admno
                    and tfc.sadmno = tsf.admno group by ta.stdnm);');


select @query;

PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Run Code Online (Sandbox Code Playgroud)

Urs*_*ula 5

您的查询(不起作用的查询)是:

   SELECT
      tsf.Admno,tsf.AdmissionFee,tsf.SplFee,
      tsf.TutionFee,tsf.ComputerFee,tsf.Stationary,
      tsf.Transport,tsf.Hostelfee,tsf.Lab,tsf.Library,
      tsf.Miscell,ta.StdNm,
      SUM(
          tfc.AdmissionFee,tfc.SplFee,tfc.TutionFee,
          tfc.ComputerFee,tfc.Stationary,tfc.Transport,
          tfc.Hostelfee,tfc.Lab,tfc.Library,tfc.Miscell ) AS 'feepaid',
      (SUM(
          tsf.Admno,tsf.AdmissionFee,tsf.SplFee,
          tsf.TutionFee,tsf.ComputerFee,tsf.Stationary,
          tsf.Transport,tsf.Hostelfee,tsf.Lab,tsf.Library,
          tsf.Miscell
          ) -
       SUM(
          tsf.Admno,tsf.AdmissionFee,tsf.SplFee,
          tsf.TutionFee,tsf.ComputerFee,tsf.Stationary,
          tsf.Transport,tsf.Hostelfee,tsf.Lab,tsf.Library,
          tsf.Miscell
          )
      ) AS 'outstanding fee'
      FROM tbl_stdfees tsf,
           tbl_admission ta,
           tbl_feecollection tfc
      WHERE
           tsf.admno = ta.admno
           AND tfc.sadmno = tsf.admno
      GROUP BY ta.stdnm;
Run Code Online (Sandbox Code Playgroud)

您正在使用 SUM,这是一个汇总同一列中的值的聚合函数,但您只是将一行中的各个列值相加 - 因此 MySQL 会给您一条错误消息。如果您按如下方式更改查询,它将起作用。

SELECT DISTINCT
      tsf.Admno,tsf.AdmissionFee,tsf.SplFee,
      tsf.TutionFee,tsf.ComputerFee,tsf.Stationary,
      tsf.Transport,tsf.Hostelfee,tsf.Lab,tsf.Library,
      tsf.Miscell,ta.StdNm,
      tfc.AdmissionFee + tfc.SplFee + tfc.TutionFee +
      tfc.ComputerFee + tfc.Stationary + tfc.Transport +
      tfc.Hostelfee + tfc.Lab + tfc.Library + tfc.Miscell AS 'feepaid',
      ( (tsf.Admno + tsf.AdmissionFee + tsf.SplFee +
          tsf.TutionFee + tsf.ComputerFee + tsf.Stationary +
          tsf.Transport + tsf.Hostelfee + tsf.Lab + tsf.Library +
          tsf.Miscell)
          -
          (tfc.AdmissionFee + tfc.SplFee + tfc.TutionFee +
          tfc.ComputerFee + tfc.Stationary + tfc.Transport +
          tfc.Hostelfee + tfc.Lab + tfc.Library + tfc.Miscell)
        ) AS 'outstanding fee'
      FROM tbl_stdfees tsf,
           tbl_admission ta,
           tbl_feecollection tfc
      WHERE
           tsf.admno = ta.admno
           AND tfc.sadmno = tsf.admno;
Run Code Online (Sandbox Code Playgroud)

由于您没有使用聚合函数,因此您也不需要使用 GROUP BY。

结果:

  Admno | AdmissionFee | SplFee | TutionFee | ComputerFee | Stationary | Transport | Hostelfee | Lab  | Library | Miscell | StdNm | feepaid | outstanding fee
  1     |      6000.00 |   0.00 |      0.00 |     2500.00 |    1500.00 |      0.00 |      0.00 | 0.00 |    0.00 | 3000.00 | zzz   | 1400.00 |           11601 
  2     |      5000.00 |   0.00 |      0.00 |     2500.00 |    1500.00 |      0.00 |      0.00 | 0.00 |    0.00 | 5000.00 | xxyy  | 1500.00 |           12502 |
  2 rows in set (0.01 sec)
Run Code Online (Sandbox Code Playgroud)

话虽如此:根据我的经验,数据库更容易维护,如果您重组表并对其进行规范化,每项费用都有一行,然后使用 SUM 和 GROUP BY。然后,您可以在不更改表结构的情况下添加费用类型,并且您的脚本不需要知道预期的费用。

例如,将 tbl_stdfees 更改为:

CREATE TABLE tbl_newfees ( 
   id INT AUTO_INCREMENT,          
   Admno VARCHAR(50) NOT NULL,          
   fee_id int, 
   fee FLOAT(10,2) DEFAULT 0.00,      
   PRIMARY KEY (id, fee_id, fee), 
   CONSTRAINT FOREIGN KEY (fee_id) REFERENCES tbl_feevars.id);
Run Code Online (Sandbox Code Playgroud)

这假设您对每个 id 的每种费用类型只有一项费用。如果可能出现多个相同类型的费用,您可以添加一个额外的标识符,如日期。使用 REPLACE 插入新的几个或覆盖旧的费用。

不考虑日期和不为同一类型的多种费用做准备让我感到困扰,我会在生产中为此做准备。

另外,我不允许 Admno 为 NULL 值 - 特别是因为您按它分组(在这种情况下)。

例子:

mysql> select * from tbl_newfees;
+----+-------+--------+---------+
| id | Admno | fee_id | fee     |
+----+-------+--------+---------+
|  1 | 1     |      1 | 6000.00 |
|  2 | 1     |      4 | 2500.00 |
|  3 | 1     |      5 | 1500.00 |
|  4 | 1     |     10 | 3000.00 |
|  5 | 2     |     10 | 5000.00 |
|  6 | 2     |      5 | 1500.00 |
|  7 | 2     |      4 | 1500.00 |
|  8 | 2     |      1 | 5000.00 |
+----+-------+--------+---------+
Run Code Online (Sandbox Code Playgroud)

这利用了您的 tbl_feevars 表 - 您只需定义 FeeName 一次。

mysql> select Admno,sum(fee) from tbl_newfees group by Admno;
+-------+----------+
| Admno | sum(fee) |
+-------+----------+
| 1     | 13000.00 |
| 2     | 14000.00 |
+-------+----------+
Run Code Online (Sandbox Code Playgroud)

支付费用的另一个表格 - 元信息应该进入一个单独的表格并加入,但是,如果银行、备注、PaM 等特定于每个单独的费用(它们可能是),则可能在已支付的表格中费用表也是如此。如果它们对于费用类型是通用的,则它们可以在 tbl_feevars 表中。您最了解您的系统。

mysql> select * from tbl_paidfees;
+----+-------+--------+---------+
| id | Admno | fee_id | fee     |
+----+-------+--------+---------+
|  1 | 1     |      1 | 6000.00 |
|  3 | 1     |      5 | 1500.00 |
|  4 | 1     |     10 | 3000.00 |
|  5 | 2     |     10 | 5000.00 |
|  6 | 2     |      5 | 1500.00 |
|  8 | 2     |      1 | 5000.00 |
+----+-------+--------+---------+
Run Code Online (Sandbox Code Playgroud)

(这与您的示例数据有点不同,我只是删除了与另一个表相比的 fee_id 4 个条目)

然后我使用左外连接选择总和,它假设所有未支付的费用在付费费用表中的条目已经输入之前已经输入:

SELECT n.Admno, 
       SUM(n.fee) AS "Total Fees", 
       SUM(p.fee) AS "Total Paid", 
       SUM(n.fee)-sum(p.fee) as "Outstanding Balance" 
FROM tbl_newfees n 
       LEFT OUTER JOIN tbl_paidfees p 
       ON n.Admno=p.Admno AND p.fee_id=n.fee_id 
GROUP BY n.Admno;
Run Code Online (Sandbox Code Playgroud)

结果:

+-------+------------+------------+---------------------+
| Admno | Total Fees | Total Paid | Outstanding Balance |
+-------+------------+------------+---------------------+
| 1     |   13000.00 |   10500.00 |             2500.00 |
| 2     |   14000.00 |   11500.00 |             2500.00 |
+-------+------------+------------+---------------------+ 
Run Code Online (Sandbox Code Playgroud)

例如,要查询特定个人的更多详细信息,您可以执行以下操作:

SELECT n.Admno, v.FeeName, n.fee AS "Total Fee", p.fee AS "Fee Paid"     
FROM tbl_feevars v,          
     tbl_newfees n LEFT OUTER JOIN tbl_paidfees p    
                   ON n.fee_id=p.fee_id AND n.Admno = p.Admno 
WHERE v.id = n.fee_id 
ORDER BY n.Admno;

+-------+---------------+-----------+----------+
| Admno | FeeName       | Total Fee | Fee Paid |
+-------+---------------+-----------+----------+
| 1     | Admission Fee |   6000.00 |  6000.00 |
| 1     | Computer Fee  |   2500.00 |     NULL |
| 1     | Stationary    |   1500.00 |  1500.00 |
| 1     | Miscell       |   3000.00 |  3000.00 |
| 2     | Admission Fee |   5000.00 |  5000.00 |
| 2     | Computer Fee  |   2500.00 |     NULL |
| 2     | Stationary    |   1500.00 |  1500.00 |
| 2     | Miscell       |   5000.00 |  5000.00 |
+-------+---------------+-----------+----------+
Run Code Online (Sandbox Code Playgroud)

类似地,您可以离开加入 tbl_feevars 表以查看尚未将学生的哪些费用输入到任何费用表中。

要将结果显示在一行中,您可以使用数据透视表方法,类似于/sf/ask/1237664851/# 17681027,或者只是定期查询并在脚本中从外部组装结果。


Tar*_*ryn 5

好吧,我发现您现有的查询存在一些问题。你当前的sql语句如下:

SELECT tsf.Admno,tsf.AdmissionFee,tsf.SplFee,tsf.TutionFee,
  tsf.ComputerFee,tsf.Stationary,tsf.Transport,tsf.Hostelfee,
  tsf.Lab,tsf.Library,tsf.Miscell,ta.StdNm, 
  sum(tfc.AdmissionFee,tfc.SplFee,tfc.TutionFee,tfc.ComputerFee,tfc.Stationary,tfc.Transport,tfc.Hostelfee,tfc.Lab,tfc.Library,tfc.Miscell) as 'feepaid', 
  (tsf.AdmissionFee,tsf.SplFee,tsf.TutionFee,tsf.ComputerFee,tsf.Stationary,tsf.Transport,tsf.Hostelfee,tsf.Lab,tsf.Library,tsf.Miscell - sum(tfc.AdmissionFee,tfc.SplFee,tfc.TutionFee,tfc.ComputerFee,tfc.Stationary,tfc.Transport,tfc.Hostelfee,tfc.Lab,tfc.Library,tfc.Miscell)) as 'outstanding fee' 
FROM tbl_stdfees tsf, tbl_Admission ta, tbl_feecollection tfc 
where tsf.admno = ta.admno 
  and tfc.sadmno = tsf.admno 
group by ta.stdnm;
Run Code Online (Sandbox Code Playgroud)

问题是您试图在sum(). 在sum()可以一次只聚集一列。

其次,在我看来,你的表tbl_stdfeestbl_feecollection设计不当。您基本上已经在表中创建了一个电子表格,其中包含所需的费用和已支付的费用。问题是如果您需要添加新费用,则必须添加新列,这表明设计很糟糕。

我认为您不需要使用动态 SQL 来获取结果,您需要对这两个表中的数据进行逆透视或规范化。要取消数据透视,您将使用 UNION ALL 查询将多列转换为多行。

取消数据透视的查询类似于以下内容:

tbl_stdfees

select admno, 'admissionfee' col, admissionfee value
from tbl_stdfees
union all
select admno, 'splfee' col, splfee value
from tbl_stdfees
union all
select admno, 'tutionfee' col, tutionfee value
from tbl_stdfees
Run Code Online (Sandbox Code Playgroud)

tbl_feecollection

select sadmno, 'admissionfee' col, admissionfee value
from tbl_feecollection
union all
select sadmno, 'splfee' col, splfee value
from tbl_feecollection
union all
select sadmno, 'tutionfee' col, tutionfee value
from tbl_feecollection
Run Code Online (Sandbox Code Playgroud)

取消透视数据后,您可以轻松获得每个数据的总费用admno和已支付的总费用。获取结果的整个查询将是:

select tsf.admno, tsf.admissionfee, tsf.splfee, tsf.tutionfee,
  tsf.computerfee, tsf.stationary, tsf.transport, tsf.hostelfee,
  tsf.lab, tsf.library, tsf.miscell,
  fc.feepaid,
  tf.Totalfees - fc.feepaid OustandingFees
from tbl_stdfees tsf
inner join tbl_Admission ta
  on tsf.admno = ta.admno
inner join
(
  select admno, sum(value) TotalFees
  from
  (
    select admno, 'admissionfee' col, admissionfee value
    from tbl_stdfees
    union all
    select admno, 'splfee' col, splfee value
    from tbl_stdfees
    union all
    select admno, 'tutionfee' col, tutionfee value
    from tbl_stdfees
    union all
    select admno, 'computerfee' col, computerfee value
    from tbl_stdfees
    union all
    select admno, 'stationary' col, stationary value
    from tbl_stdfees
    union all
    select admno, 'transport' col, transport value
    from tbl_stdfees
    union all
    select admno, 'hostelfee' col, hostelfee value
    from tbl_stdfees
    union all
    select admno, 'lab' col, lab value
    from tbl_stdfees
    union all
    select admno, 'library' col, library value
    from tbl_stdfees
    union all
    select admno, 'miscell' col, miscell value
    from tbl_stdfees
  ) sf
  group by admno
) tf 
  on tsf.admno = tf.admno
left join
(
  select sadmno, sum(value) feepaid
  from
  (
    select sadmno, 'admissionfee' col, admissionfee value
    from tbl_feecollection
    union all
    select sadmno, 'splfee' col, splfee value
    from tbl_feecollection
    union all
    select sadmno, 'tutionfee' col, tutionfee value
    from tbl_feecollection
    union all
    select sadmno, 'computerfee' col, computerfee value
    from tbl_feecollection
    union all
    select sadmno, 'stationary' col, stationary value
    from tbl_feecollection
    union all
    select sadmno, 'transport' col, transport value
    from tbl_feecollection
    union all
    select sadmno, 'hostelfee' col, hostelfee value
    from tbl_feecollection
    union all
    select sadmno, 'lab' col, lab value
    from tbl_feecollection
    union all
    select sadmno, 'library' col, library value
    from tbl_feecollection
    union all
    select sadmno, 'miscell' col, miscell value
    from tbl_feecollection
  ) fe
  group by sadmno
) fc
  on ta.admno = fc.sadmno;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo

正如我所说,我建议您规范化数据库中的表。我建议对您的数据库进行以下更改。

创建一个payment表:

CREATE TABLE `tbl_payment` (
  `auto_id` int(11) NOT NULL auto_increment,
  `GrupNm` varchar(40) NOT NULL,
  `class` varchar(30) NOT NULL,
  `rollno` int(2) NOT NULL,
  `sadmno` int(11) NOT NULL,
  `PaM` varchar(30) NOT NULL,
  `DDCN` int(25) NOT NULL,
  `bank` varchar(30) NOT NULL,
  `cheqd` date NOT NULL,
  `remarks` varchar(150) NOT NULL,
  `pdate` date NOT NULL,
  PRIMARY KEY  (`auto_id`)
);
Run Code Online (Sandbox Code Playgroud)

创建一个表以将标准费用加入每个admno

create table tbl_stdfee_admin
(
  `id` int(11),
  `Admno` varchar(50) default NULL,
  `fee_id` int(11) NOT NULL,
  `std_fee` float(10,2) default '0.00'
);
Run Code Online (Sandbox Code Playgroud)

然后创建一个表来加入admno每笔支付和收取的费用:

create table tbl_collfee_admin
(
  `id` int(11),
  `payment_id` int(11),
  `Admno` varchar(50) default NULL,
  `fee_id` int(11) NOT NULL,
  `coll_fee` float(10,2) default '0.00'
);
Run Code Online (Sandbox Code Playgroud)

如果对表进行规范化,则将使用类似于以下内容的查询:

select tsf.admno, 
  max(case when f.feename = 'Admission Fee' then tsf.std_fee else 0 end) AdmissionFee,
  max(case when f.feename = 'Spl Fee' then tsf.std_fee else 0  end) SplFee,
  max(case when f.feename = 'Tution Fee' then tsf.std_fee else 0  end) TutionFee,
  max(case when f.feename = 'Computer Fee' then tsf.std_fee else 0  end) ComputerFee,
  max(case when f.feename = 'Stationary' then tsf.std_fee else 0  end) Stationary,
  max(case when f.feename = 'Transport' then tsf.std_fee else 0  end) Transport,
  max(case when f.feename = 'Hostel Fee' then tsf.std_fee else 0  end) HostelFee,
  max(case when f.feename = 'Lab' then tsf.std_fee else 0  end) Lab,
  max(case when f.feename = 'Library' then tsf.std_fee else 0  end) Library,
  max(case when f.feename = 'Miscell' then tsf.std_fee else 0  end) Miscell,
  fc.FeesPaid,
  tf.TotalFees - fc.FeesPaid OustandingFees
from tbl_feevars f
left join tbl_stdfee_admin tsf
  on f.id = tsf.fee_id
left join tbl_Admission ta
  on tsf.admno = ta.admno
left join
(
  select admno, sum(std_fee) TotalFees
  from tbl_stdfee_admin
  group by admno
) tf
  on tsf.admno = tf.admno
left join
(
  select admno, sum(coll_fee) FeesPaid
  from tbl_collfee_admin
  group by admno
) fc
  on ta.admno = fc.admno
where tsf.admno is not null
group by tsf.admno, fc.FeesPaid, tf.TotalFees;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。然后可以将第二个版本转换为动态 SQL 版本,以获取表格中所有费用的列表tbl_feevars。两个版本都会给出结果:

| ADMNO | ADMISSIONFEE | SPLFEE | TUTIONFEE | COMPUTERFEE | STATIONARY | TRANSPORT | HOSTELFEE | LAB | LIBRARY | MISCELL | FEEPAID | OUSTANDINGFEES |
-----------------------------------------------------------------------------------------------------------------------------------------------------
|     1 |         6000 |      0 |         0 |        2500 |       1500 |         0 |         0 |   0 |       0 |    3000 |    1400 |          11600 |
|     2 |         5000 |      0 |         0 |        2500 |       1500 |         0 |         0 |   0 |       0 |    5000 |    3000 |          11000 |
Run Code Online (Sandbox Code Playgroud)