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のあるホテルには、私の質問で説明したように、テーブルに入力された1日あたりの宿泊料金があります。ここで、ホテルは、入力されたすべての日付をリストするのではなく、開始日と終了日を使用して価格カレンダーをより短い方法で表示するレポートを取得する必要があります。たとえば、2010/07/01から2010/07/02までのAの価格は200、Bの価格は300です。この価格は3日から4日に変更され、5日はB室があるその日のみ異なる価格になります。価格が350に変更されます。したがって、これは1日の差と見なされます。そのため、開始日と終了日は同じです。

これが問題のシナリオを説明したことを願っています。また、このホテルは特定の期間休業する可能性があることに注意してください。これは私の最初の質問に対する追加の問題であるとしましょう。問題は、特定の日付に料金が入力されていない場合、たとえば日曜日にホテルがこれら2つの部屋を販売しないため、価格が入力されていない場合、つまり行がテーブルに存在しない場合です。

4

3 に答える 3

6

関連するテーブルを作成すると、関連する情報をクエリして抽出するための自由度が大幅に高まります。役立つと思われるリンクをいくつか示します。

次のチュートリアルから始めることができます:http:
//dev.mysql.com/tech-resources/articles/intro-to-normalization.html
http://net.tutsplus.com/tutorials/databases/sql-for-beginners/

スタックオーバーフローに関して、役立つかもしれないいくつかの質問もあります。
平易な英語での
正規化データベースの正規化は正確に何をしますか?

とにかく、可能な解決策に。次の例では、ホテルの部屋の例えを使用しています。

まず、ホテルの部屋に関する情報を保持するテーブルを作成します。このテーブルには部屋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;

2つの部屋を作成し、各部屋に関する1日の料金情報を追加します。

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);

その情報が保存されている状態で、を使用した簡単なSELECTクエリによりJOIN、すべての1日の宿泊料金が表示されます。

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 に答える