2009-11-16 12 views
2

Im ayant un peu d'un bloc mental sur celui-ci.Regroupement des dates par mois dans une requête SQL Server (procédure stockée)

Je suis système de réservation pour une chambre d'hôtel et il contient une table comme si

BookingRoomLink
BookingId (FK)
RoomID (FK)
start_date
date_fin

I » d aiment interroger les données pour extraire les niveaux d'occupation pour chaque mois. Je pourrais faire ceci manuellement (c.-à-d. Pour le mois passé faire quelque chose comme ceci).

SELECT BookingRoomLink.Start_Date, 
     BookingRoomLink.End_Date, 
     DATEDIFF("d", BookingRoomLink.Start_Date, BookingRoomLink.End_Date) as RoomNights 
    FROM BookingRoomLink 
    WHERE BookingRoomLink.Start_Date >= dateadd(m, -1, getdate()) 
    AND BookingRoomLink.End_Date <= GETDATE() 

Ensuite, je peux faire un compte sur les résultats ou similaire qui me donnerait les nuits des chambres « utilisées » et soustrayez contre les nuits de chambres disponibles dans un mois.

Par exemple. 10 chambres x 30 jours dans le mois = 300 nuits possibles disponibles. 150 utilisés (résultat de la requête) = 50% d'occupation.

Le problème

Id aiment automatiser ce dans une procédure stockée.

Est-il possible de regrouper ceci en mois pour une année donnée?

Comment est-ce que je m'assurerais que les réservations qui chevauchent une limite de mois sont convenablement manipulées?

+0

Les données d'échantillon et les résultats attendus aident toujours! – JonH

Répondre

3
WITH (
     SELECT 0 
     UNION ALL 
     SELECT m + 1 
     FROM mon 
     WHERE m < 11 
     ), 
     yr (y) AS 
     (
     SELECT CAST('1990-01-01' AS DATETIME) 
     UNION ALL 
     SELECT DATEADD(year, 1, y) 
     FROM yr 
     WHERE y <= GETDATE() 
     ), 
     dates (smy, emy) AS 
     (
     SELECT DATEADD(month, m, y), DATEADD(month, m + 1, y) 
     FROM yr 
     CROSS JOIN 
       mon 
     ), 
     diffs (smy, emy, days) AS 
     (
     SELECT smy, emy, DATEDIFF(day, smy, emy) 
     FROM dates 
     ) 
SELECT smy, 
     roomId, 
     CAST(SUM(DATEDIFF(day, 
     CASE WHEN start_date < smy THEN smy ELSE start_date END, 
     CASE WHEN end_date > emy THEN emy ELSE end_date END 
     )) AS FLOAT)/days 
FROM diffs 
JOIN bookings 
ON  start_date < emy 
     AND end_date >= smy 
GROUP BY 
     roomId, smy, emy, days 
+0

+1 pour l'utilisation d'une solution simple et prenant également en compte les séjours qui commencent avant le 1er ou se terminent après le dernier jour du mois –

+0

sql 2008 me propose ce Msg 155, Niveau 15, État 1, ligne 19 'mon' n'est pas une option dateadd reconnue. Msg 174, niveau 15, état 1, ligne 30 La fonction datediff requiert 3 argument (s). – Matt

+0

'@ jossy': voir mise à jour. Je l'ai testé sur des échantillons de données. – Quassnoi

0

Vous pouvez utiliser la fonction DATEPART pour obtenir le numéro du mois et le groupe correspondant à ce numéro.

0

Si vous pouvez ajouter une colonne calculée à votre table, indiquez sa valeur le mois que vous souhaitez utiliser. Je choisirais certainement "stocker avec rangée" vs calculer chaque fois. En ce qui concerne les mois qui s'étendent sur plusieurs mois, vous devrez décider comment modéliser cela. Que faire si vous avez une réservation qui s'étend sur 3 mois? 4? 12? Plus?

Comme pour le mois, vous pouvez essayer une valeur à 6 chiffres comme 200911 afin que vous puissiez facilement les trier mais les garder dans un champ entier. Si la valeur est calculée, personne ne pourra en faire partie.

3

Si vous devez le faire souvent, vous pouvez ajouter les parties mois et l'année comme persisté colonnes calculées à votre table, et de mettre un index sur eux

ALTER TABLE dbo.BookingRoomLink 
    ADD StartMonth AS MONTH(Start_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD StartYear AS Year(Start_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD EndMonth AS MONTH(End_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD EndYear AS Year(End_Date) PERSISTED 

Vous pouvez maintenant sélectionner ces nouvelles colonnes calculées , utilisez-les dans une clause WHERE, GROUP par ces colonnes - et ils seront toujours à jour en fonction de Start_Date et End_Date - ils ne sont pas calculés chaque fois que vous y accédez -> beaucoup plus rapidement qu'en utilisant DATEPART dans toutes vos requêtes !

0

Essayez:

Select DateName(month, start_Date) + 
     DateName(Year, Start_Date) as MonthName, 
    Sum(DateDiff(Day, Start_Date, 
     Case DateDiff(Month, Start_Date, End_Date) 
      When 0 Then End_Date 
      Else DateAdd(day, -day(Start_Date), 
        DateAdd(day, DateDiff(day, 0, Start_Date), 0)) Bookings 
From BookingRoomLink 
Group By DateName(month, start_Date) + DateName(Year, Start_Date) 
2

Vous pourriez "Round" la date au 1er du mois, puis GROUP BY sur ce point.Semblable à l'utilisation de DatePart, mais vous avez toujours une date valide, de sorte que vous pouvez utiliser une plage de dates dans la clause WHERE avant ou après avoir effectué le regroupement.

 
SELECT [Date] = DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0), -- 1st of the month 
     [Bookings] = COUNT(*) 
FROM BookingRoomLink 
GROUP BY DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0) 
ORDER BY [Date] 
+0

J'ai adoré cette solution. Simple! J'aime taper moins. Je vous remercie. –