1

我的目标是在列中返回具有相同值的开始日期和结束日期。这是我的桌子。(*) 已标记为让您了解我希望如何为 A 和 B 列的每个相似序列值获取“EndDate”

ID | DayDate   |  A  |  B
-----------------------------------------------
1  | 2010/07/1 | 200 |  300
2  | 2010/07/2 | 200 |  300 *
3  | 2010/07/3 | 150 |  250
4  | 2010/07/4 | 150 |  250 *
8  | 2010/07/5 | 150 |  350 *
9  | 2010/07/6 | 200 |  300
10 | 2010/07/7 | 200 |  300 *
11 | 2010/07/8 | 100 |  200
12 | 2010/07/9 | 100 |  200 *

我想从上表中得到以下结果表

| DayDate   |EndDate   |  A  |  B
-----------------------------------------------
| 2010/07/1 |2010/07/2 | 200 |  300
| 2010/07/3 |2010/07/4 | 150 |  250
| 2010/07/5 |2010/07/5 | 150 |  350
| 2010/07/6 |2010/07/7 | 200 |  300
| 2010/07/8 |2010/07/9 | 100 |  200

更新:

谢谢迈克,从您将下一行视为错误的角度来看,您的方法似乎有效。

8  | 2010/07/5 | 150 |  350 * 

然而,这不是一个错误。我面对这类数据的挑战就像记录市场价格随日期变化的情况。mycase 中的真正问题是,如果 A 和 B 在所有这些行中都匹配,则选择具有开始日期和结束日期的所有行。还要选择与先前选择的行相邻的行,依此类推,这样表中就不会遗漏任何数据。

我可以解释一个真实世界的场景。如我的问题中所述,具有 A 室和 B 室的酒店将每天的房价输入到表格中。现在酒店需要获取一份报告,以使用开始和结束日期以更短的方式显示价格日历,而不是列出所有输入的日期。例如,在 2010/07/01 到 2010/07/02,A 的价格是 200,B 的价格是 300。这个价格从 3 日变为 4 日,而 5 日只有房间 B 的那一天有不同的价格。是价格更改为 350。所以这被认为是一天的差异,这就是开始日期和结束日期相同的原因。

我希望这解释了问题的场景。另请注意,这家酒店可能会在特定时间段内关闭,可以说这是我第一个问题的另一个问题。问题是如果没有在特定日期输入价格,例如在周日,酒店不出售这两个房间,所以他们没有输入价格,这意味着该行将不存在于表中。

4

3 回答 3

6

创建相关表可以让您更自由地查询和提取相关信息。以下是一些您可能会觉得有用的链接:

您可以从这些教程开始: http:
//dev.mysql.com/tech-resources/articles/intro-to-normalization.html
http://net.tutsplus.com/tutorials/databases/sql-for-beginners/

这里还有几个关于 stackoverflow 的问题可能有用:
Normalization in plain English
数据库规范化到底是做什么的?

无论如何,找到一个可能的解决方案。以下示例使用您的酒店房间类比。

首先,创建一个表来保存有关酒店房间的信息。此表仅包含房间 ID 及其名称,但您可以在此处存储其他信息,例如房间类型(单人房、双人房、双床房)、其景观(海景、海景、城市景观、泳池景观)和很快:

CREATE TABLE `room` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `name_UNIQUE` (`name` ASC) )
ENGINE = InnoDB;

现在创建一个表格来保存不断变化的房价。此表room通过room_id列链接到表。外键约束防止将记录插入到rate引用不存在的房间的表中:

CREATE TABLE `rate` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `room_id` INT UNSIGNED NOT NULL,
  `date` DATE NOT NULL,
  `rate` DECIMAL(6,2) UNSIGNED NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_room_rate` (`room_id` ASC),
  CONSTRAINT `fk_room_rate`
    FOREIGN KEY (`room_id` )
    REFERENCES `room` (`id` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

创建两个房间,并添加一些关于每个房间的每日房价信息:

INSERT INTO `room` (`id`, `name`) VALUES (1, 'A'), (2, 'B');

INSERT INTO `rate` (`id`, `room_id`, `date`, `rate`) VALUES
( 1, 1, '2010-07-01', 200),
( 2, 1, '2010-07-02', 200),
( 3, 1, '2010-07-03', 150),
( 4, 1, '2010-07-04', 150),
( 5, 1, '2010-07-05', 150),
( 6, 1, '2010-07-06', 200),
( 7, 1, '2010-07-07', 200),
( 8, 1, '2010-07-08', 100),
( 9, 1, '2010-07-09', 100),
(10, 2, '2010-07-01', 300),
(11, 2, '2010-07-02', 300),
(12, 2, '2010-07-03', 250),
(13, 2, '2010-07-04', 250),
(14, 2, '2010-07-05', 350),
(15, 2, '2010-07-06', 300),
(16, 2, '2010-07-07', 300),
(17, 2, '2010-07-08', 200),
(18, 2, '2010-07-09', 200);

存储了这些信息后,一个带有 a 的简单SELECT查询JOIN将显示所有每日房价:

SELECT
    room.name,
    rate.date,
    rate.rate
FROM room
JOIN rate
ON rate.room_id = room.id;

+------+------------+--------+
| A    | 2010-07-01 | 200.00 |
| A    | 2010-07-02 | 200.00 |
| A    | 2010-07-03 | 150.00 |
| A    | 2010-07-04 | 150.00 |
| A    | 2010-07-05 | 150.00 |
| A    | 2010-07-06 | 200.00 |
| A    | 2010-07-07 | 200.00 |
| A    | 2010-07-08 | 100.00 |
| A    | 2010-07-09 | 100.00 |
| B    | 2010-07-01 | 300.00 |
| B    | 2010-07-02 | 300.00 |
| B    | 2010-07-03 | 250.00 |
| B    | 2010-07-04 | 250.00 |
| B    | 2010-07-05 | 350.00 |
| B    | 2010-07-06 | 300.00 |
| B    | 2010-07-07 | 300.00 |
| B    | 2010-07-08 | 200.00 |
| B    | 2010-07-09 | 200.00 |
+------+------------+--------+

要查找每个房价的开始日期和结束日期,您需要一个更复杂的查询:

SELECT 
    id,
    room_id,
    MIN(date) AS start_date,
    MAX(date) AS end_date,
    COUNT(*) AS days,
    rate
FROM (
    SELECT
        id,
        room_id,
        date,
        rate, 
        (
            SELECT COUNT(*)
            FROM rate AS b
            WHERE b.rate <> a.rate
            AND b.date <= a.date
            AND b.room_id = a.room_id
        ) AS grouping
    FROM rate AS a
    ORDER BY a.room_id, a.date
) c
GROUP BY rate, grouping
ORDER BY room_id, MIN(date);

+----+---------+------------+------------+------+--------+
| id | room_id | start_date | end_date   | days | rate   |
+----+---------+------------+------------+------+--------+
|  1 |       1 | 2010-07-01 | 2010-07-02 |    2 | 200.00 |
|  3 |       1 | 2010-07-03 | 2010-07-05 |    3 | 150.00 |
|  6 |       1 | 2010-07-06 | 2010-07-07 |    2 | 200.00 |
|  8 |       1 | 2010-07-08 | 2010-07-09 |    2 | 100.00 |
| 10 |       2 | 2010-07-01 | 2010-07-02 |    2 | 300.00 |
| 12 |       2 | 2010-07-03 | 2010-07-04 |    2 | 250.00 |
| 14 |       2 | 2010-07-05 | 2010-07-05 |    1 | 350.00 |
| 15 |       2 | 2010-07-06 | 2010-07-07 |    2 | 300.00 |
| 17 |       2 | 2010-07-08 | 2010-07-09 |    2 | 200.00 |
+----+---------+------------+------------+------+--------+

您可以在这里找到上述查询中使用的技术的一个很好的解释:
http ://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data

于 2010-06-28T17:11:58.940 回答
1
  • 我的一般方法是根据 DayDate = DayDate+1 将表格连接到自身上,并且 A 或 B 值不相等
  • 这将找到每个时期的结束日期(第二天的值会有所不同)
  • 唯一的问题是,这不会找到最后一个时期的结束日期。为了解决这个问题,我从表中选择最大日期并将其合并到我的结束日期列表中
  • 定义结束日期列表后,您可以根据结束日期大于或等于原始日期将它们加入原始表
  • 从此最终列表中,选择按其他字段分组的最短日期

    select
    min(DayDate) as DayDate,EndDate,A,B from
    (SELECT DayDate, A, B, min(ends.EndDate) as EndDate
    FROM yourtable
    LEFT JOIN
    (SELECT max(DayDate) as EndDate FROM yourtable UNION
    SELECT t1.DayDate as EndDate 
    FROM yourtable t1
    JOIN yourtable t2
    ON date_add(t1.DayDate, INTERVAL 1 DAY) = t2.DayDate 
    AND (t1.A<>t2.A OR t1.B<>t2.B)) ends
    ON ends.EndDate>=DayDate
    GROUP BY DayDate, A, B) x
    GROUP BY EndDate,A,B
    
于 2010-06-27T10:50:31.283 回答
0

我想我已经找到了一个可以生成所需表格的解决方案。

SELECT  
  a.DayDate AS StartDate,  

  ( SELECT b.DayDate  
    FROM Dates AS b  
    WHERE b.DayDate > a.DayDate AND (b.B = a.B OR b.B IS NULL)  
    ORDER BY b.DayDate ASC LIMIT 1 
  ) AS StopDate,
a.A as A,
    a.B AS B

FROM Dates AS a 
WHERE Coalesce( 
               (SELECT c.B  
                FROM Dates AS c  
                WHERE c.DayDate <= a.DayDate  
                ORDER BY c.DayDate DESC LIMIT 1,1  
               ), -99999  
              ) <> a.B 
  AND a.B IS NOT NULL 
ORDER BY a.DayDate ASC; 

能够生成下表结果

StartDate   StopDate    A   B
2010-07-01  2010-07-02  200 300
2010-07-03  2010-07-04  150 250
2010-07-05  NULL        150 350
2010-07-06  2010-07-07  200 300
2010-07-08  2010-07-09  100 200

但我需要一种方法将 NULL 替换为与开始日期相同的日期。

于 2010-06-28T16:00:23.477 回答