2010-06-26 16 views
1

Mon objectif est de renvoyer une date de début et de fin ayant la même valeur dans une colonne. Voici ma table. Le (*) ont été marqués pour vous donner l'idée de la façon dont je veux « EndDate » pour chaque valeur de séquence similaire de A & B colonnesComment créer une plage de dates de période à partir d'une table mysql regroupant chaque séquence commune de valeurs dans une colonne

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 * 

et je veux obtenir le tableau des résultats ci-dessous de ce qui précède Table

| 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 

MISE à JOUR:

Merci Mike, l'approche de la vôtre semble fonctionner dans votre perspective de considérer la ligne suivante comme une erreur.

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

Cependant, ce n'est pas une erreur. Le défi auquel je suis confronté avec ce type de données est comme un scénario d'enregistrement d'un changement de prix du marché avec la date. Le vrai problème dans mycase est de sélectionner toutes les lignes avec la date de début et de fin si les deux A & B correspondent dans toutes ces lignes. Aussi pour sélectionner les lignes qui sont à côté de précédemment sélectionné, et ainsi de suite, aucune donnée n'est laissée de côté dans la table.

Je peux expliquer un scénario du monde réel. Un hôtel avec la salle A et B a des taux de pièce pour chaque jour entré dans la table comme expliqué dans ma question. Maintenant, l'hôtel doit obtenir un rapport pour afficher le calendrier des prix plus rapidement en utilisant les dates de début et de fin, au lieu de répertorier toutes les dates saisies. Par exemple, le 2010/07/01 à 2010/07/02 le prix de A est 200 et B est 300. Ce prix est changé de 3ème à 4ème et le 5ème il y a un prix différent seulement pour ce jour où la Salle B est le prix est changé à 350. Donc, cela est considéré comme une seule différence de jour, c'est pourquoi les dates de début et de fin sont les mêmes. J'espère que cela a expliqué le scénario du problème. Notez également que cet hôtel peut être fermé pour une période de temps spécifique, disons que c'est un problème supplémentaire à ma première question. Le problème est que si le taux n'est pas entré à des dates précises, par exemple le dimanche, l'hôtel ne vend pas ces deux chambres, donc ils ne sont pas entrés dans le prix, ce qui signifie que la ligne n'existe pas dans le tableau.

+0

I J'ai fourni une réponse, mais je ne suis pas clair sur vos critères de sélection. La troisième ligne de votre exemple de sortie ne me semble pas correcte (celle datée 2010/07/5). Pourquoi cela a-t-il les mêmes DayDate et EndDate, et quels critères ont été utilisés pour le sélectionner? – Mike

Répondre

4

La création de tables associées vous permet une plus grande liberté d'interrogation et d'extraction d'informations pertinentes. Voici quelques liens qui pourraient vous être utiles:

Vous pouvez commencer par ces tutoriels:
http://dev.mysql.com/tech-resources/articles/intro-to-normalization.html
http://net.tutsplus.com/tutorials/databases/sql-for-beginners/

Il y a aussi quelques questions ici sur stackoverflow qui pourraient être utiles:
Normalization in plain English
What exactly does database normalization do?

Quoi qu'il en soit, à une solution possible. Les exemples suivants utilisent l'analogie de votre chambre d'hôtel. Commencez par créer une table contenant des informations sur les chambres d'hôtel. Cette table contient seulement l'ID de la chambre et son nom, mais vous pouvez stocker d'autres informations ici, telles que le type de chambre (simple, double, twin), sa vue (océan, vue sur l'océan, vue sur la ville, vue sur la piscine) ainsi de suite:

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; 

Créez maintenant une table pour contenir les taux de change. Cette table est liée à la table room via la colonne room_id. La contrainte de clé étrangère empêche les enregistrements étant insérés dans la table rate qui se réfèrent à des chambres qui n'existent pas:

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; 

Créer deux chambres, et ajouter des informations de tarif journalier sur chaque chambre:

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

Avec que les informations stockées, une requête simple SELECT avec un JOIN vous montrera les tous les prix des chambres:

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 | 
+------+------------+--------+ 

Pour trouver les dates de début et de fin pour chaque tarif de la chambre, vous avez besoin d'une requête plus complexe:

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 | 
+----+---------+------------+------------+------+--------+ 

Vous pouvez trouver une bonne explication de la technique utilisée dans la requête ci-dessus ici:
http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data

+0

Mike, j'apprécie vraiment votre grande aide sur la lutte contre le problème. Je peux comprendre les conceptions de bases de données relationnelles, mais la question a été simplifiée afin d'éviter les complications.J'utilise des tables similaires à celles que vous avez expliquées, mais il existe trois types de taux différents pour chaque ID de pièce dans ma table de base de données réelle. Ils sont un taux unique, taux double, et le taux de tRIPPLE. C'est pourquoi j'utilisais le scénario A & B. Je vais tester votre solution à ma vraie base de données aujourd'hui et je vous ferai savoir si cela fonctionne pour tous ces 3 tarifs. – Raftalks

+0

et merci pour les superbes liens, ils sont très utiles. – Raftalks

+0

Je serais intéressé de savoir pourquoi cela a été downvoted. – Mike

1
  • Mon approche générale est de se joindre à la table sur elle-même basée sur DayDate = DayDate + 1 et les valeurs A ou B ne pas être égale
  • Ce trouvera les dates de fin pour chaque période (où la valeur va pour être différent le jour suivant)
  • Le seul problème est, qui ne trouvera pas une date de fin pour la période finale. Pour contourner cela, je sélectionne la date maximale de la table et l'union dans ma liste de dates de fin
  • Une fois que vous avez la liste des dates de fin définies, vous pouvez les joindre à la table d'origine en fonction de la date de fin étant plus grande ou égale à la date originale
  • a partir de cette liste finale, sélectionnez le DayDate minimum regroupés par les autres champs

    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 
    
+0

J'ai testé cela en MySQL ce matin et j'ai corrigé quelques petites choses. Cela produit maintenant le résultat exact comme l'exemple du problème original. –

0

Je pense avoir trouvé une solution qui ne produit la table souhaitée.

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; 

est en mesure de générer le résultat tableau suivant

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 

Mais je besoin d'un moyen de remplacer la valeur NULL à la même date de la date de début.

+1

vous pouvez envelopper le dernier champ de date d'arrêt dans une vérification IF, quelque chose comme: IF (date de fin est nul, startdate, stopdate) comme date d'arrêt –