如果没有数据,MySQL返回前一行

时间:2014-02-13 10:55:17

标签: mysql join

我无法正确选择语法。请帮助
我有两个表:第一个包含工作数据

  CREATE TABLE ProdStatus (
  Id    INT(6) UNSIGNED ZEROFILL,
  ProdId INT,
  StatusId INT,
  Date  DATE,
  Total  INT
);

此表中的数据:

INSERT INTO ProdStatus
VALUES
  (000001, 1, 1, '2014-02-01', 5),
  (000002, 1, 2, '2014-02-01', 3),
  (000003, 1, 2, '2014-02-05', 4),
  (000004, 2, 1, '2014-02-07', 2),
  (000005, 2, 1, '2014-02-08', 7),
  (000006, 2, 2, '2014-02-08', 9);

第二个是日历表

    CREATE TABLE calendar (
      id int(11),
      Date date
);

日历表的数据:

INSERT INTO calendar (id, Date)
VALUES
(1, '2014-02-01'),
(2, '2014-02-02'),
(3, '2014-02-03'),
(4, '2014-02-04'),
(5, '2014-02-05'),
(6, '2014-02-06'),
(7, '2014-02-07'),
(8, '2014-02-08'),
(9, '2014-02-09'),
(10, '2014-02-10');

我需要能够从ProdStatus表中选择所有值的总和,按ProdId和Date分组(总和,因为我可以有多个状态)但是与日历表一起使用,因为我需要每天的值选定的范围。 如果我在ProdStatus表中没有一天的信息,那么应该返回前一天的结果。

以下是我所做的查询:

SELECT p.ProdId, c.Date, sum(p.Total) as Result
FROM ProdStatus p
right outer join calendar c on c.Date = p.Date
WHERE p.ProdId in (1, 2)
and c.date between '2014-02-01' and '2014-02-10'
group by c.date, p.ProdId

此查询返回此结果:

ProdId    Date    Result
1    2014-02-01    8
1    2014-02-05    4
2    2014-02-07    2
2    2014-02-08    16

预期结果:

ProdId    Date    Result
1    2014-02-01    8
2    2014-02-01    0
1    2014-02-02    8
2    2014-02-02    0
1    2014-02-03    8
2    2014-02-03    0
1    2014-02-04    8
2    2014-02-04    0
1    2014-02-05    4
2    2014-02-05    0
1    2014-02-06    4
2    2014-02-06    0
1    2014-02-07    4
2    2014-02-07    2
1    2014-02-08    4
2    2014-02-08    16
1    2014-02-09    4
2    2014-02-09    16
1    2014-02-10    4
2    2014-02-10    16

有什么建议吗? 谢谢

3 个答案:

答案 0 :(得分:0)

这似乎是编程语言的任务。你是如何消费结果的?你能用这种语言实现这个目标吗?

如果您需要这是一个100%的mysql解决方案,您可能需要编写一个存储过程来执行此操作,而不仅仅是一个简单的查询。

答案 1 :(得分:0)

这是我的sqlfiddle。有一个派生表,表示列表产品ID,以确保每个日历日期的每个产品都有一个条目。

SELECT foo.p AS ProdId, calendar.Date, SUM(COALESCE(ProdStatus.Total,0)) as Result
FROM calendar
JOIN (SELECT 1 AS p UNION SELECT 2) AS foo
LEFT JOIN ProdStatus
  ON calendar.Date >= ProdStatus.Date
    AND foo.p = ProdStatus.ProdId
LEFT JOIN ProdStatus AS t1
  ON calendar.Date >= t1.Date
    AND ProdStatus.Date < t1.Date
    AND foo.p = t1.ProdId
WHERE calendar.date BETWEEN '2014-02-01' AND '2014-02-10'
  AND t1.Id IS NULL
GROUP BY calendar.date, foo.p
ORDER BY calendar.date, foo.p;

以下是使用您的Product表(假设密钥称为ID):

SELECT product.id AS ProdId, calendar.Date, SUM(COALESCE(ProdStatus.Total,0)) as Result
FROM calendar
JOIN Product
LEFT JOIN ProdStatus
  ON calendar.Date >= ProdStatus.Date
    AND product.id = ProdStatus.ProdId
LEFT JOIN ProdStatus AS t1
  ON calendar.Date >= t1.Date
    AND ProdStatus.Date < t1.Date
    AND product.id = t1.ProdId
WHERE calendar.date BETWEEN '2014-02-01' AND '2014-02-10'
  AND t1.Id IS NULL
  AND product.id IN (1, 2, 253, 54, 78)
GROUP BY calendar.date, product.id
ORDER BY calendar.date, product.id;

答案 2 :(得分:0)

我将您的查询切换为left outer join,只是因为我更好地按照这种方式查询查询(“在进行连接时保留第一个表中的所有行”)。

您只需将prodid上的条件从where子句移到on子句:

SELECT p.ProdId, c.Date, sum(p.Total) as Result
FROM calendar c left outer join
     ProdStatus p
     on c.Date = p.Date and p.ProdId in (1, 2)
WHERE c.date between '2014-02-01' and '2014-02-10'
group by c.date, p.ProdId
order by c.date, p.ProdId;

如果没有匹配项,则prodiIdNULL,因此where子句中的条件失败。

编辑:

我明白了。要解决此类查询,您需要生成两个prodids和日期的所有可能组合,然后加入prod状态:

SELECT prods.ProdId, c.date, coalesce(sum(p.Total), 0) as Result
FROM (select 1 as ProdId union all select 2) as prods cross join
     calendar c left outer join
     ProdStatus p
     on c.Date = p.Date and prods.ProdId = p.ProdId
WHERE c.date between '2014-02-01' and '2014-02-10'
GROUP BY prods.ProdId, c.date
OPRDER BY prods.ProdId, c.date;