Avatar billede eksperten1 Nybegynder
13. august 2007 - 13:19 Der er 10 kommentarer og
1 løsning

Opgøre antallet af aktive dage, per måned

Puuha, jeg har rodet rundt med denne her i 2 dage nu - har virkelig brug for hjælp.

Jeg har en tabel [Sales Datawarehouse], den indeholder data for solgte annoncer. Hver annonce har [Starting Date] samt [Ending Date].
En annonce kan så starte 2007-01-10 og slutte 2007-02-10. Jeg skal lave en rapport som kan vise hvor mange dage, per måned, en given annonce er aktiv (for at udregne hvor stor del at det fakturerede beløb der tilskrives en given periode).
Så for eksemplet skulle det være 21 dage i januar samt 10 dage i februar.

Jeg har oprettet en tabel med en kalender i, en række per dato [Date View] og så havde jeg forestillet mig at kunne joine start og slutdato og lave noget count, men jeg kan simpelthen ikke få det til at fungere, enten får jeg ingen data, eller forkerte (mange) rækker.

Det kører på en SQL Server 2000 - jeg har ikke dbo rettigheder, så det skal køre som t-sql, noget jeg kan rulle igennem query analyzer.

Kode eksempler, links o.l. modtages med kyshånd!
Avatar billede eksperten1 Nybegynder
13. august 2007 - 13:21 #1
Her er det sql jeg roder i lige nu (har ryddet det for overflødige ting)
select a.[Advertisement No_], a.[Starting Date], a.[Ending Date], b.[Date] from [Sales Datawarehouse] a inner join [Date view] b ON (a.[Starting Date] >= b.[Date] AND a.[Ending Date] <= b.[Date]) WHERE (a.[Starting Date] >= '2006-01-01' AND a.[Ending Date] >= '2006-01-01' AND a.[Ending Date] <= '2006-01-20' )
Avatar billede kjulius Novice
13. august 2007 - 18:43 #2
Tja, egentlig har jeg vel bare omskrevet din forespørgsel lidt, men da det ikke ser ud til, at der er andre der vil give deres besyv med, er her mit forslag:

SELECT d.[Date], s.[Advertisement No_], s.[Starting Date], s.[Ending Date]
FROM [Date view] d, [Sales Datawarehouse] s
WHERE s.[Ending Date] >= s.[Starting Date]
  AND d.[Date] BETWEEN '2006-01-01' and '2006-01-20'
  AND d.[Date] BETWEEN s.[Starting Date] AND s.[Ending Date]
Avatar billede kjulius Novice
13. august 2007 - 18:54 #3
Hmmm... måske har jeg misforstået spørgsmålet, men man burde herefter kunne foretage en GROUP BY på År/Måned, som giver antallet af dage. Forudsætter naturligvis, at Sales Datawarehouse kun indeholder én række pr. s.[Advertisement No_], s.[Starting Date], s.[Ending Date] - ellers går der kuk i det)...


SELECT YEAR(d.[Date]) AS [Årstal], MONTH(d.[Date]) AS [Måned], s.[Advertisement No_], s.[Starting Date], s.[Ending Date], COUNT(*) AS [Antal dage]
FROM [Date view] d, [Sales Datawarehouse] s
WHERE s.[Ending Date] >= s.[Starting Date]
  AND d.[Date] BETWEEN '2006-01-01' and '2006-01-20'
  AND d.[Date] BETWEEN s.[Starting Date] AND s.[Ending Date]
GROUP BY YEAR(d.[Date], MONTH(d.[Date]), s.[Advertisement No_], s.[Starting Date], s.[Ending Date]
Avatar billede lorentsnv Nybegynder
14. august 2007 - 13:37 #4
Kan du ikke bruge DateDiff funktionen?
Avatar billede lorentsnv Nybegynder
14. august 2007 - 13:42 #5
Unskyld, jeg skulle egentlig ikke have sendt ovenstående. Udfordringen bliver at generere en linie pr. måned, så kan du eventuelt bruge datediff, til at regne dage for første og sidste måned.

Hvis du laver en månedstabel, som angiver først og sidste dag i måneden. Derefter linker du din [Sales Datawarehouse] til din månedstabel, hvor første dag i annonceperioden er mindre end sidste dag i måenden, og sidste dag i annonceperioden er større en første dag i afsluttende måned.

Når du har et output som giver dig en række for hver måned i annonceperioden, kan du bruge datediff til at regne på antal dage i 'ikke hele' måneder, og eventuelt hente en værdi med antal dage i måeneden for hele måneder.
Avatar billede lorentsnv Nybegynder
14. august 2007 - 14:06 #6
Jeg lavet en lille test, hvor jeg oprættet to tabeller, Annonce og Måneder.
Annoncetabellen indeholder et AnnonceID, samt start og slutdato. Dessuden lavet jeg en 'Måneder' tabel, som indeholder alle måneder , med FraDato, TilDato, og AntalDage. AntalDage er forhåndsberegnet, og angiver antal dage i hele måneden,

Derefter lavet jeg følgende forespørgsel, som ser ud til at fungere som jeg håbet:

select a.AnnonceID, m.Måned, a.StartDato, a.SLutDato,
    AntalDage = CASE
                    --Hele måneder
                    WHEN a.StartDato < m.FraDato and a.SlutDato >= m.TilDato
                            Then m.AntalDage
                    -- Start og slutdato indenfor samme måned
                    WHEN a.StartDato > m.FraDato and a.SlutDato <= m.TilDato
                            Then DateDiff(Day, a.StartDato, a.SlutDato)
                    -- Beregner antal dage i første måned
                    WHEN a.StartDato > m.FraDato and a.SlutDato > m.TilDato
                            Then DateDiff(Day, a.StartDato, dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,a.StartDato)+1, 0)))
                    -- Beregner antal dage i sidste måned
                    WHEN a.StartDato < m.FraDato and a.SlutDato <= m.TilDato
                            Then DateDiff(Day, DATEADD(mm, DATEDIFF(mm,0,a.SlutDato), 0), a.SlutDato) + 1
                    ELSE 1
                    END
from dbo.Annonce a ,dbo.MÅneder m
where a.StartDato <= m.TilDato
and    a.SlutDato >= m.FraDato
Avatar billede lorentsnv Nybegynder
14. august 2007 - 14:12 #7
En lille rettelse med > ændret til >= for 'Start og slutdato indenfor samme måned':

select a.AnnonceID, m.Måned, a.StartDato, a.SLutDato,
    AntalDage = CASE
                    --Hele måneder
                    WHEN a.StartDato < m.FraDato and a.SlutDato >= m.TilDato
                            Then m.AntalDage
                    -- Start og slutdato indenfor samme måned
                    WHEN a.StartDato >= m.FraDato and a.SlutDato <= m.TilDato
                            Then DateDiff(Day, a.StartDato, a.SlutDato)
                    -- Beregner antal dage i første måned
                    WHEN a.StartDato > m.FraDato and a.SlutDato > m.TilDato
                            Then DateDiff(Day, a.StartDato, dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,a.StartDato)+1, 0)))
                    -- Beregner antal dage i sidste måned
                    WHEN a.StartDato < m.FraDato and a.SlutDato <= m.TilDato
                            Then DateDiff(Day, DATEADD(mm, DATEDIFF(mm,0,a.SlutDato), 0), a.SlutDato) + 1
                    ELSE 1
                    END
from dbo.Annonce a ,dbo.MÅneder m
where a.StartDato <= m.TilDato
and    a.SlutDato >= m.FraDato

Hvis dine datoer også indeholder klokkeslet, skal du lave noget SQL for at fjærne klokkeslet. Dette kan f.eks. gøres med:
select DATEADD(day, DATEDIFF(day, '20000101', dinDato), '20000101')
Avatar billede eksperten1 Nybegynder
14. august 2007 - 15:04 #8
lorentsv: Tak for din kode! Nu har jeg ikke lige mulighed for at lave dine tabeller så jeg har ikke testet det, men det _virker_ umiddelbart som en stor omvej - jeg ved det ikke. Jeg fik det løst ved at joine på en tabel med datoer - så kunne jeg få det nøjagtige antal dage per måned.

kjulius: det var lige din BETWEEN ting jeg manglede - ved ikke hvorfor jeg ikke kunne huske den - det spiller nu, tak for hjælpen :-) (smider du et svar?)

Her er den sql jeg endte med at bruge:
Select a.[Advertisement No_], a.[Number], a.[Salesperson Code], CAST(a.[Net Amount] as int), a.[Advertiser Name], a.[Starting Date], a.[Ending Date],
COUNT(DISTINCT b.[Date]) as numdays, DATENAME(month, b.[Date]), YEAR(b.[Date])
FROM [Sales Datawarehouse] a
INNER JOIN [Date view] b ON (b.[Date] BETWEEN a.[Starting Date] AND a.[Ending Date] )
WHERE a.[Selling Publication] = 'INTDK' AND LEN(a.[Salesperson Code]) > 0 AND
(a.[Starting Date] <= '2006-01-01' AND a.[Ending Date] >= '2006-01-01' AND a.[Ending Date] <= '2006-12-31') AND a.[Net Amount] > 0 AND YEAR(b.[Date]) = '2006'
GROUP BY a.[Advertisement No_], a.[Number], a.[Salesperson Code], a.[Net Amount], a.[Advertiser Name], a.[Starting Date], a.[Ending Date],YEAR(b.[Date]) ,DATENAME(month, b.[Date]) ;
Avatar billede lorentsnv Nybegynder
14. august 2007 - 16:21 #9
Fint at du har fået det til at virke.

Umiddelbart er jeg ikke enig at den måde jeg viser, er nogen omvej, da jeg vil tro at den vil være hurtigst, ved større mængde data.

Grunden er at Den vil behandle væsentlig færre rækker data, da joinen kun vil returnere antal annoncer * antal måneder. Din sql vil behandle et rækkeantal som antal annoncer * antal dage, og lave en efterfølgende GROUP BY.

Men hvis det ikke er tale om et stort antal annoncer, betyder performance måske ikke så meget.
Avatar billede kjulius Novice
14. august 2007 - 17:31 #10
Tja, der er som bekendt mange veje til Rom. :-)
Avatar billede eksperten1 Nybegynder
14. august 2007 - 20:36 #11
jeg kan ikke se nogen performance problemer umiddelbart - der er lige over 300.000 rækker og den returnerer ca. 3200 rækker på 2 sekunder så det er fint performance. Jeg ville bare tro at en CASE er langsommere end en join/count men jeg ved det ikke. Jeg værdsætter dit svar, det er ikke det, men egen kode er oftest nemmest at arbejde videre med :-)

Tak for hjælpen begge 2.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester