Disons que j'ai le tableau A: BookingsPerPerson
Person_Id ArrivalDate DepartureDate
123456 2012-01-01 2012-01-04
213415 2012-01-02 2012-01-07
Ce que je dois réaliser avec une vue est le suivant:
Person_Id ArrivalDate DepartureDate Jan-01 Jan-02 Jan-03 Jan-04 Jan-05 Jan-06 Jan-07
123456 2012-01-01 2012-01-04 1 1 1 1
213415 2012-01-02 2012-01-07 1 1 1 1 1 1
Le système est pour les événements, donc chaque réservation d'hôtel peut prendre entre 1 et 15 jours, mais pas plus. Toutes les idées seraient très appréciées.
Vous pouvez utiliser la fonction PIVOT
pour effectuer cette requête. Ma réponse inclura à la fois une version statique et une version dynamique car il est parfois plus facile de la comprendre en utilisant une version statique.
Un pivot statique est lorsque vous codez en dur toutes les valeurs que vous souhaitez transformer en colonnes.
-- first into into a #temp table the list of dates that you want to turn to columns
;with cte (datelist, maxdate) as
(
select min(arrivaldate) datelist, max(departuredate) maxdate
from BookingsPerPerson
union all
select dateadd(dd, 1, datelist), maxdate
from cte
where datelist < maxdate
)
select c.datelist
into #tempDates
from cte c
select *
from
(
select b.person_id, b.arrivaldate, b.departuredate,
d.datelist,
convert(CHAR(10), datelist, 120) PivotDate
from #tempDates d
left join BookingsPerPerson b
on d.datelist between b.arrivaldate and b.departuredate
) x
pivot
(
count(datelist)
for PivotDate in ([2012-01-01], [2012-01-02], [2012-01-03],
[2012-01-04], [2012-01-05], [2012-01-06] , [2012-01-07])
) p;
Résultats (Voir SQL Fiddle With Demo ):
PERSON_ID | ARRIVALDATE | DEPARTUREDATE | 2012-01-01 | 2012-01-02 | 2012-01-03 | 2012-01-04 | 2012-01-05 | 2012-01-06 | 2012-01-07
=====================================================================================================================================
123456 | 2012-01-01 | 2012-01-04 | 1 | 1 | 1 | 1 | 0 | 0 | 0
213415 | 2012-01-02 | 2012-01-07 | 0 | 1 | 1 | 1 | 1 | 1 | 1
La version dynamique générera la liste des valeurs à transformer en colonnes:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
;with cte (datelist, maxdate) as
(
select min(arrivaldate) datelist, max(departuredate) maxdate
from BookingsPerPerson
union all
select dateadd(dd, 1, datelist), maxdate
from cte
where datelist < maxdate
)
select c.datelist
into #tempDates
from cte c
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10), datelist, 120))
from #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT person_id, arrivaldate, departuredate, ' + @cols + ' from
(
select b.person_id, b.arrivaldate, b.departuredate,
d.datelist,
convert(CHAR(10), datelist, 120) PivotDate
from #tempDates d
left join BookingsPerPerson b
on d.datelist between b.arrivaldate and b.departuredate
) x
pivot
(
count(datelist)
for PivotDate in (' + @cols + ')
) p '
execute(@query)
Les résultats sont les mêmes (voir SQL Fiddle With Demo ):
PERSON_ID | ARRIVALDATE | DEPARTUREDATE | 2012-01-01 | 2012-01-02 | 2012-01-03 | 2012-01-04 | 2012-01-05 | 2012-01-06 | 2012-01-07
=====================================================================================================================================
123456 | 2012-01-01 | 2012-01-04 | 1 | 1 | 1 | 1 | 0 | 0 | 0
213415 | 2012-01-02 | 2012-01-07 | 0 | 1 | 1 | 1 | 1 | 1 | 1
Je suis de la vieille école et je trouve CASE
plus facile à travailler dans ma tête que PIVOT
. Je suis sûr que Bluefeet apparaîtra sous peu et me fera honte, mais en attendant, vous pouvez jouer avec cette requête SQL dynamique. En supposant que votre table stocke DATE
et non DATETIME
(ou pire encore, VARCHAR
):
USE tempdb;
GO
CREATE TABLE dbo.a
(
Person_Id INT,
ArrivalDate DATE,
DepartureDate DATE
);
INSERT dbo.a SELECT 123456, '2012-01-01', '2012-01-04'
UNION ALL SELECT 213415, '2012-01-02', '2012-01-07';
DECLARE @sql NVARCHAR(MAX) = N'SELECT Person_Id';
;WITH dr AS
(
SELECT MinDate = MIN(ArrivalDate),
MaxDate = MAX(DepartureDate)
FROM dbo.a
),
n AS
(
SELECT TOP (DATEDIFF(DAY, (SELECT MinDate FROM dr), (SELECT MaxDate FROM dr)) + 1)
d = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY [object_id])-1,
(SELECT MinDate FROM dr))
FROM sys.all_objects
)
SELECT @sql += ',
' + QUOTENAME(d) + ' = CASE WHEN ''' + CONVERT(CHAR(10), d, 120)
+ ''' BETWEEN ArrivalDate AND DepartureDate THEN ''1'' ELSE '''' END' FROM n;
SELECT @sql += ' FROM dbo.a;'
EXEC sp_executesql @sql;
GO
DROP TABLE dbo.a;
Un des très rares cas, BTW, où je pouvais justifier l'utilisation de BETWEEN
pour les requêtes de plage de dates.
Que diriez-vous de cela pour générer une liste de dates
declare @Date01 as smalldatetime
declare @Date02 as smalldatetime
select @Date01 = min(periodstart), @Date02 = max(periodend)
from BookingTable
declare @DateDiff as int
select @DateDiff = (select DATEDIFF(DAY, @Date01, @Date02))
;
WITH Tally (N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.all_columns a CROSS JOIN sys.all_columns b
)
SELECT DATEADD(day, N, @Date01)
FROM Tally
where N <= @DateDiff