MySQL LEFT JOIN with SUM before a date and SUM before and at that date

Posted

技术标签:

【中文标题】MySQL LEFT JOIN with SUM before a date and SUM before and at that date【英文标题】: 【发布时间】:2017-12-07 17:04:41 【问题描述】:

我有三张桌子(pagu、realisasi 和 perencanaan)。我正在使用左连接。我想在某个日期之前对一个字段求​​和,而在该日期之前和那个日期对另一个字段求​​和。

我的帕古桌:

idpagu  kode    komponen    pagu    target_kinerja  tahun   keterangan
1   2217.057.056    Pengembangan Sistem Manajemen Mutu UPTP     52740000    1   2017    tes123
2   2220.051.051    Pelatihan Peningkatan Produktivitas     4732755000  120     2017    tes123
3   2220.052.051    Calon Wirausaha Baru yang Dilatih (RM)  12464938000     240     2017    TES123

我的现实表:

idrealisasi     idpagu  kode    komponen    realisasi   target_kinerja  tgl     keterangan
5   1   2217.057.056    Pengembangan Sistem Manajemen Mutu UPTP     52740000    1   1/14/2017   tes
6   2   2220.051.051    Pelatihan Peningkatan Produktivitas     10000000    10  1/14/2017   tes
7   2   2220.051.051    Pelatihan Peningkatan Produktivitas     20000000    20  2/14/2017   tes2
8   2   2220.051.051    Pelatihan Peningkatan Produktivitas     10000000    10  3/14/2017   tes3
9   2   2220.051.051    Pelatihan Peningkatan Produktivitas     10000000    10  6/14/2017   tes4
10  2   2220.051.051    Pelatihan Peningkatan Produktivitas     10000000    10  1/15/2017   tes

我的 perencanaan 表(无记录):

idperencanaan   idpagu  kode    komponen    penarikan   target_kinerja  tgl     keterangan

我的查询:

select p.idpagu,p.kode,p.komponen,p.pagu,p.target_kinerja,
if(pr.penarikan is null,0,sum(pr.penarikan)) as perenuang,
if(pr.target_kinerja is null,0,sum(pr.target_kinerja)) as perenfis,
if(r.realisasi is null,0,sum(r.realisasi)) as realuang,
if(r.target_kinerja is null,0,sum(r.target_kinerja)) as realfis,
p.pagu-if(r.realisasi is null,0,sum(r.realisasi)) as sisauang,
p.target_kinerja-if(r.target_kinerja is null,0,sum(r.target_kinerja)) as sisafis,
if(sum(rr.realisasi) is null,0,sum(rr.realisasi)) as tes
from pagu p 
left join realisasi r on p.idpagu=r.idpagu and r.tgl BETWEEN "2017-01-01" and "2017-01-15"
left join perencanaan pr on p.idpagu=pr.idpagu and r.tgl BETWEEN "2017-01-01" and "2017-01-15"
left join realisasi rr on p.idpagu=rr.idpagu and rr.tgl < "2017-01-15"
group by p.idpagu,r.idpagu,rr.idpagu

我的结果:

idpagu  kode    komponen    pagu    target_kinerja  perenuang   perenfis    realuang    realfis     sisauang    sisafis     tes
1   2217.057.056    Pengembangan Sistem Manajemen Mutu UPTP     52740000    1   0   0   52740000    1   0   0   52740000
2   2220.051.051    Pelatihan Peningkatan Produktivitas     4732755000  120     0   0   20000000    20  4712755000  100     20000000
3   2220.052.051    Calon Wirausaha Baru yang Dilatih (RM)  12464938000     240     0   0   0   0   12464938000     240     0

我的问题:

tes 列在当前日期“2017-01-15”之前是 pagu - realuang,第二行应该是 10.000.000,但我的查询给出了 20.000.000。 如何编写查询,以便在第二条记录中 tes 列是 10.000.000?

我的架构:

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `pagu`;
CREATE TABLE `pagu` (
  `idpagu` int(11) NOT NULL AUTO_INCREMENT,
  `kode` varchar(45) NOT NULL,
  `komponen` varchar(250) NOT NULL,
  `pagu` bigint(20) NOT NULL,
  `target_kinerja` int(11) NOT NULL,
  `tahun` varchar(4) NOT NULL,
  `keterangan` varchar(250) DEFAULT NULL,
  PRIMARY KEY (`idpagu`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

INSERT INTO `pagu` VALUES ('1', '2217.057.056', 'Pengembangan Sistem Manajemen Mutu UPTP', '52740000', '1', '2017', 'tes123');
INSERT INTO `pagu` VALUES ('2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '4732755000', '120', '2017', 'tes123');
INSERT INTO `pagu` VALUES ('3', '2220.052.051', 'Calon Wirausaha Baru yang Dilatih (RM)', '12464938000', '240', '2017', 'TES123');

DROP TABLE IF EXISTS `perencanaan`;
CREATE TABLE `perencanaan` (
  `idperencanaan` int(11) NOT NULL AUTO_INCREMENT,
  `idpagu` int(11) NOT NULL,
  `kode` varchar(45) NOT NULL,
  `komponen` varchar(250) NOT NULL,
  `penarikan` bigint(20) NOT NULL,
  `target_kinerja` int(11) NOT NULL,
  `tgl` date NOT NULL,
  `keterangan` varchar(250) DEFAULT NULL,
  PRIMARY KEY (`idperencanaan`),
  KEY `fk_perencanaan_pagu1_idx` (`idpagu`),
  CONSTRAINT `fk_perencanaan_pagu1` FOREIGN KEY (`idpagu`) REFERENCES `pagu` (`idpagu`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `realisasi`;
CREATE TABLE `realisasi` (
  `idrealisasi` int(11) NOT NULL AUTO_INCREMENT,
  `idpagu` int(11) NOT NULL,
  `kode` varchar(45) NOT NULL,
  `komponen` varchar(250) NOT NULL,
  `realisasi` bigint(20) NOT NULL,
  `target_kinerja` int(11) NOT NULL,
  `tgl` date NOT NULL,
  `keterangan` varchar(250) DEFAULT NULL,
  PRIMARY KEY (`idrealisasi`),
  KEY `fk_perencanaan_pagu1_idx` (`idpagu`),
  CONSTRAINT `fk_perencanaan_pagu10` FOREIGN KEY (`idpagu`) REFERENCES `pagu` (`idpagu`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

INSERT INTO `realisasi` VALUES ('5', '1', '2217.057.056', 'Pengembangan Sistem Manajemen Mutu UPTP', '52740000', '1', '2017-01-14', 'tes');
INSERT INTO `realisasi` VALUES ('6', '2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '10000000', '10', '2017-01-14', 'tes');
INSERT INTO `realisasi` VALUES ('7', '2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '20000000', '20', '2017-02-14', 'tes2');
INSERT INTO `realisasi` VALUES ('8', '2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '10000000', '10', '2017-03-14', 'tes3');
INSERT INTO `realisasi` VALUES ('9', '2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '10000000', '10', '2017-06-14', 'tes4');
INSERT INTO `realisasi` VALUES ('10', '2', '2220.051.051', 'Pelatihan Peningkatan Produktivitas', '10000000', '10', '2017-01-15', 'tes');

【问题讨论】:

感谢您的快速回复@philipxy。您之前的总和是正确的,并且在某个日期总和。我添加了架构和代码片段(我的表很长),所以用户可以复制粘贴它。 感谢您的编辑。请:不要使用链接。不要使用图像。即使是 ER 图,也要以文本形式提供信息。如果您的问题不是 php/html/javascript,请不要使用 sn-ps。将您的代码编辑为“代码块”。 (缩进 4 个空格或单击“”。)转到 sqlfiddle.com。 (没有其他语言的固定站点。)查询以显示您的表值。运行您的查询。将 sqlfiddle 链接编辑到您的问题中。您还可以在代码块中包含使用对齐列重新格式化的 sqlfiddle 输出;或者您可以将代码的 INSERT VALUES 格式化为对齐列。 (对齐 = 人类可读。) 请不要保留旧的不清楚的文字;编辑您的问题以明确。你所有的原文都不清楚。在我上一条评论中,我猜想两个部分说的是同一件事;但我不知道那是什么。我也无法理解你的新描述。用小步骤说事情。此外,您仍然只是通过示例描述您想要的内容。但举个什么例子?试着告诉我们一行在表格中时的含义。再举例:请阅读minimal reproducible example。你的 desired 输出表是什么?也许使用一个更简单/经过编辑的示例来显示您的问题/需求。 我猜你的意思是 tes = pagu - ("sisauang before the current date") = pagu - (pagu - ("realuang before the current date")),即 tes = "realuang before the current日期”?不幸的是,这仍然只是单词沙拉。您的意思是给定 pagu 和(当前)日期的 tes 是该日期前几天该 pagu 的 realisasi 值的总和。如果你强迫自己去写/写你真正的意思,你就会开始清楚地思考/写。 PS您提供的原始文本查询与您提供的链接中的文本不同。我已将其从链接复制到您的问题中。 【参考方案1】:

根据您的查询和表名和列名(pagu = 上限、realisasi = 实际、perencanaan = 计划)猜测,对于每个上限,您将在特定范围内的天数的实际值、那些天的计划值、其那些日子的实际值,以及一些相关的目标值,将 NULL 值视为 0。

我假设如果上限在某个日期具有实际值,那么它在该日期具有计划值。否则,您的查询将涉及 FULL JOIN,而 LEFT JOIN 有时会错误地形成具有不同日期的行等。所以我怀疑您缺少 FK(外键)。

您的第一个 LEFT JOIN 获取某些日期每个上限的实际值。然后,您需要 上限和日期 的任何计划值。所以你错过了and r.tgl = pr.tgl。然后,您需要其中一些日期的 ceiling & date 的任何实际值。所以你的第三个 LEFT JOIN 丢失了and r.tgl = rr.tgl

您的分组和求和不正确。如果 SELECT 使用聚合调用之外的列,那么对于 GROUPed 列的每个子行,它应该是单值的。 (GROUP BY 为 GROUPed 列的每个子行返回一行。)(SET sql_mode = 'ONLY_FULL_GROUP_BY' 不允许 mysql 的非标准行为。)

select p.idpagu, count(*), p.pagu, p.target_kinerja,
    sum(coalesce(pr.penarikan,0)) as pr_penarikan,
    sum(coalesce(pr.target_kinerja,0)) as pr_target_kinerja,
    sum(coalesce(r.realisasi,0)) as r_realisasi,
    sum(coalesce(r.target_kinerja,0)) as r_target_kinerja,
    p.pagu-sum(coalesce(r.realisasi,0)) as d_realisasi,
    p.target_kinerja-sum(coalesce(r.target_kinerja,0)) as d_target_kinerja,
    sum(coalesce(rr.realisasi,0)) as rr_realisasi
from pagu p
left join realisasi r on p.idpagu=r.idpagu and r.tgl BETWEEN "2017-01-01" and "2017-01-15"
left join perencanaan pr on p.idpagu=pr.idpagu and r.tgl = pr.tgl and r.tgl BETWEEN "2017-01-01" and "2017-01-15"
left join realisasi rr on p.idpagu=rr.idpagu and r.tgl = rr.tgl and r.tgl < "2017-01-15"
group by p.idpagu, p.pagu, p.target_kinerja;

idpagu  pagu                    pr_penarikan    r_realisasi     d_realisasi             rr_target_kinerja 
    count(*)        target_kinerja  pr_target_kinerja       r_target_kinerja    d_target_kinerja    
1   1   52740000    1           0   0           52740000    1   0               0       52740000
2   2   4732755000  120         0   0           20000000    20  4712755000      100     10000000
3   1   12464938000 240         0   0           0           0   12464938000     240     0

【讨论】:

以上是关于MySQL LEFT JOIN with SUM before a date and SUM before and at that date的主要内容,如果未能解决你的问题,请参考以下文章

MySQL Left Join Subquery with *

Mysql LEFT JOIN with count 返回未知列

MySQL left join with 'b's limit

Mysql sum distinct 基于包含多个 LEFT JOIN 的其他列

Mysql left join with nested select慢,如何优化

mysql left join to table with multiple rows on min id 以确保返回单行