25. januar 2011 - 15:28Der er
18 kommentarer og 2 løsninger
Udtrykket er for langt
Hvad gør man når udtrykket i en forespørgsel er mere end 1024 tegn? Dele det op i flere felter, ja, jeg kan bare ikke se hvordan.
Mit problem: Jeg skal i en rapport vise projektomkostninger. Timesatserne bliver (kan blive) reguleret hvert halve år. Hvis der ikke er blevet skrevet nye satser ind, dvs. hvis satsen står til 0,00 ved en given dato, skal den bruge tallene fra den seneste opdatering. Så udtrykket i feltet Timesats ser sådan ud:
Timesats: IIf([Dato] Between #01-06-2007# And #31-12-2007#;[Timesats2]; IIf([Dato] Between #01-01-2008# And #31-05-2008#;[Timesats1]; IIf([Dato] Between #01-06-2008# And #31-12-2008# And [Timesats3]<>0;[Timesats3]; IIf([Dato] Between #01-01-2009# And #31-05-2009# And [Timesats4]<>0;[Timesats4]; IIf([Dato] Between #01-06-2009# And #31-12-2009# And [Timesats5]<>0;[Timesats5]; IIf([Dato] Between #01-01-2010# And #31-05-2010# And [Timesats6]<>0;[Timesats6]; IIf([Dato] Between #01-06-2010# And #31-12-2010# And [Timesats7]<>0;[Timesats7]; IIf([Dato] Between #01-01-2011# And #31-05-2011# And [Timesats8]<>0;[Timesats8]; IIf([Dato] Between #01-06-2011# And #31-12-2011# And [Timesats9]<>0;[Timesats9]; IIf([Dato] Between #01-01-2012# And #31-05-2012# And [Timesats10]<>0;[Timesats10]; IIf([Dato] Between #01-06-2012# And #31-12-2012# And [Timesats11]<>0;[Timesats11]; IIf([Dato] Between #01-01-2013# And #31-05-2013# And [Timesats12]<>0;[Timesats12]; IIf([Dato] Between #01-06-2013# And #31-12-2013# And [Timesats13]<>0;[Timesats13]; IIf([SenesteÆndring] Between #01-01-2009# And #31-05-2009#;[Timesats4]; IIf([SenesteÆndring] Between #01-06-2009# And #31-12-2009#;[Timesats5]; IIf([SenesteÆndring] Between #01-01-2010# And #31-05-2010#;[Timesats6]; IIf([SenesteÆndring] Between #01-06-2010# And #31-12-2010#;[Timesats7]; IIf([SenesteÆndring] Between #01-01-2011# And #31-05-2011#;[Timesats8]; IIf([SenesteÆndring] Between #01-06-2011# And #31-12-2011#;[Timesats9]; IIf([SenesteÆndring] Between #01-01-2012# And #31-05-2012#;[Timesats10]; IIf([SenesteÆndring] Between #01-06-2012# And #31-12-2012#;[Timesats11]; IIf([SenesteÆndring] Between #01-01-2013# And #31-05-2013#;[Timesats12]; IIf([SenesteÆndring] Between #01-06-2013# And #31-12-2013#;[Timesats13])))))))))))))))))))))))
Muligvis kan dette laves meget mere elegant, håber nogen kan give mig en løsning.
har været ude for noget lignende når kunder konstruere lange queries ad hoc.
Prøv om du kan lave variabler korter T1, T2 og Datoer fra en bestemt dag.
D1 =#01-06-2007#
D2 = D1+180 osv.
jeg ville også lave et kald ned til en function, som beregnede timeprisen. Det meste syntes at kunne ligge nede i en funktion som du kalder i din query.
En satstabel har jeg, det er der timesatserne bliver skrevet ind, og gyldighedsperiode lige nu er Timesats8.
Man kan godt have flere IIF'er, jeg havde 13 indtil jeg udvidede med flere perioder, og det virkede fint.
Muligvis kan jeg komme ned under 1024 ved korte variabelnavne, men det er jo en stakket frist, da tiden går :-) Jeg kan slette IIF sætningerne med SenesteÆndring indtil nuværende periode, men jeg kan jo ikke slette IIF sætningerne med Dato.
hnteknik: Det lyder spændende med et kald til en funktion, men du bliver nødt til at forklere det lidt nærmere før jeg forstår hvordan man gør det.
jeg har før lavet public funktioner i VBA nede i databasen som kaldes direkte af en query. Her kan du i princippet lave hele timesatsberegningen evt. med opslag i en tabel.
Har det ikke lige her - er på vej ud af fabrikken.
timesats: calcaftimesats(dato)
Skal se senere om jeg lige kan grave et eks. frem.
Jeg vil ikke påstå at dette er en af de mest elegante løsninger jeg har set i dag
Gør som f.eks. hnteknik foreslår og lav en funktion der tager Dato og SenesteÆndring som parametre og beregn din time sats der.
Det er meget mere fleksibelt og det er også muligt for andre at vedligeholde satser m.m. Fordelen ved din metode er at du gør dig selv uundværlig fordi du er den eneste der kan overskue det :-)
Jeg har lavet en funktion i et lønprogram hvor det så bare er personens aktuelle fradrag m.m. jeg kan finde. Jeg kalder med den dato hvor lønnen skal beregnes for og så findes de relevante tal.
Du synes at have en unoedvendig kompliceret datastruktur og nu diskuterer traaden maader at faa denne datastruktur til at virke i endnu mere komplicerede udtryk med nestede iif's. Ville det maaske vaere mere frugtbart at proeve at forenkle datastrukturen saa du kan faa timesatserne ved hjaelp af simple queries? (Maaske er det hvad du taenkte paa da du sagde "Muligvis kan dette laves meget mere elegang..").
Det kunne se ud til at du for hver dato kun har en timesats. For eksempel 1000kr fra 1/6/2007 31/12/2007, saa 1010kr indtil 31/12/2008 (man sprang en satsregulering over) og saa 1020 indtil 30/5/2009.
I saafald kunne jeg forestille mig en satstabel med (mindst) tre kolonner, startdato, slutdato, timesats. Ovennaevnte eksempel ville give denne tabel:
Og saa faar du timesatsen for en given dato saaledes (syntaktisk ukorrekt, jeg har glemt hvordan man bruger variable i en Access query)
SELECT timesats FROM satstabel WHERE $dato BETWEEN startdato AND slutdato
Du har ogsaa timesatser for fremtidige perioder, i dit eksempel op til 2013, som du saa maa skoenne dig til. Saa disse indfoerer du i din satstabel, og saa opdaterer du din satstabel naar og hvis de aktuelle satser er anderledes end de skoennede satser, for eksempel fordi man springer en satsregulering over.
Er saadanne tanker relevante? Eller er der yderligere forhold der skal tages hensyn til?
lav en public function og inde i funkionen lave en select case for hver af dato intevallerne. Det er lav proaktisk. Optimalt ville være et opslag i en tabel med dato1, dato2, timesats. Det kunne lynhurtigt læses ind i en array som funktionen søger i. Men hold dig i første omgang til en lavpraktisk funktioin med select cases som ovenfor.
hnteknik: Jeg behøver mere hjælp til at lave en funktion, da eksemplet i dit link er noget anderledes end mit (og fordi jeg altid har bøvlet rundt når jeg prøver at lave funktioner).
Jeg har mere end 1 sats pr. periode, 1 for hver medarbejder.
Timesatsen skal følge perioden, men hvis der ikke er skrevet satser ind i den periode, skal den tage den seneste indtastede sats (SenesteÆndring).
hugopedersen: Ja, det er dejligt at føle sig uundværlig :-) men jeg har dog lavet en formular hvor min direktør skriver satserne ind. Men perioderne er jo nødt til at blive forlænget efterhånden som tiden går. Så hvis jeg kunne lave en funktion hvor der ikke er begrænsning i antallet af tegn, kan jeg fremtidssikre den mange år ud i fremtiden.
Dorit - det er jo nærmest et projekt som nemt kommer op på et par timers arbejde. Det er udover hvad jeg kan tilbyde her.
Jeg har lavet noget lignende med maskinprisindekset, hvor de, hvis de ikke opdateret indekset regelmæssigt, falder tilbage til det sidst kendte indeks.
det kører alt sammen via læsning i indeks tabel, så det har været fremtidssikret de sidste 12 år.
Du vil ikke ind paa at snakke datastruktur som muligvis kan forenkles saaledes at funktionerne kan blive saa simple som muligt, nu og i fremtiden? Du kunne jo forklare lidt mere om dine data, hvor og hvordan du nu gemmer dem, og hvordan du bruger dem. Du siger at du har en sats per medarbejder per periode. Hvordan ser din satstabel ud? En pudsighed er at de to aarlige perioder synes at vaere paa henholdsvis 5 maaneder (1/1 - 31/5) og 7 maaneder (1/6-31/12). Er det individuelt per medarbejder om en sats bliver aendret i en ny periode?
jeg fandt lige mit +15 år gamle indeksering program stump. Princippet er, at finde det sidste indeks før en vis dato og så anvende det til videre beregninger. indekserne ligger i en tabel - dato og tal. Prøv om du kan adapte det til din case:
Function Index2Array() 'Denne routine lægger data op i en array 'for at indexpris2 kan hente den der en hastighedsforøgelse på en faktor 125.
Dim db As Database Dim rst As Recordset Dim qrd As QueryDef Dim StrCriteria As String ' Hvilke dato kriterier Dim IDXDa As Integer, IDXNu As Integer ' Indeksværdier Nu og Da Dim Crst As Integer, i As Integer
Index2Array = True On Error GoTo Index2error Set db = CodeDb()
Set qrd = db.QueryDefs("Qry vis indexer") Set rst = qrd.OpenRecordset() rst.MoveLast Crst = rst.RecordCount rst.MoveFirst
ReDim AIndexer(Crst - 1) For i = 0 To Crst - 1 AIndexer(i).VarDato = rst![Indexdato]
AIndexer(i).VarIDX = rst![Indekstype] AIndexer(i).VarValue = rst![Indeksværdi] rst.MoveNext Next Index2Exit: rst.Close Exit Function
Index2error: MsgBox Error$ Index2Array = False Resume Index2Exit End Function
Den kører en gang og ligger i hukommelsen, så kan man bruge querien med en funktion i.
Function IndexPris2(ByVal FV As Currency, ByVal VD As Variant, ByVal IDX As Integer, ByVal ID As Variant) As Currency ' Ind : Forsikringsværdi i kr FV - 1.000.000 kr ' Vurderingsdato VD - 21-04-1995 ' Indextype IDX- 10 (for Maskinindex) ' Indekseringsdato ID - 08-08-1997 ' Henter: Dagsdato Alternativt - 08-08-1997 ' Index for kvartalet før vurdering: 338 (01-04-1995) ' Index for kvartalet før dagsdato : 351 (01-07-1997) ' Beregner IndexPris: 1.000.000 x 351 / 338 = 1.186.380 ' Ud : IndexPris - 1.038.462 kr Dim i As Integer, I2 As Integer Dim IDXDa As Integer, IDXNu As Integer ' Indeksværdier Nu og Da
i = 0 I2 = 0 On Error GoTo IPError1 Do Until AIndexer(i).VarIDX = IDX Or i = UBound(AIndexer) i = i + 1 Loop
'fandt vi et indeks med indhold ??? If i < UBound(AIndexer) Then I2 = i 'hvor kom vi til Do Until AIndexer(i).VarDato <= VD Or i = UBound(AIndexer) i = i + 1 Loop If i < UBound(AIndexer) Then IDXDa = AIndexer(i).VarValue Else IDXDa = 100 End If i = I2 'hvor kom vi Fra Do Until AIndexer(i).VarDato <= ID Or i = UBound(AIndexer) i = i + 1 Loop If i < UBound(AIndexer) Then IDXNu = AIndexer(i).VarValue Else IDXNu = 100 End If IndexPris2 = Nz(FV) * IDXNu / IDXDa Else MsgBox "Der er ingen indexer" IndexPris2 = 0
End If Exit_Index2: Exit Function
IPError1: 'Der er intet index MsgBox "Der er noget galt med indexeringen! - Indexpris2" MsgBox Error$ IndexPris2 = 0
Altsaa enten bliver alle timesatser opdaterede eller ogsaa ingen. I saa fald kan jeg se to maader at forenkle sagen paa: (1) Opdater timesatserne i hver periode, nogle gange til det samme som de var (hvis satserne ikke blev opdaterede 1/6/2010 saa var, for eksempel, for medarbejder Hansen timesatsen 100kr fra 1/1/2010 til 31/5/2010 og 100 kr fra 1/6/2010 til 31/12/2010). (2) Lav perioderne ulige lange. Hvis der blev opdatetet 1/6/2009, 1/1/2010, ikke 1/6/2010, men 1/1/2011, saa gaar, for eksempel, periode 4 fra 1/6/2009 til 31/12/2009, periode 5 fra 1/1/2010 til 31/12/2010, og periode 6 fra 1/1/2011 til 31/5/2011 med mindre det til den tid besluttes ikke at opdatere i hvilket tilfaelde periode 6 forlaenges til 31/12/2011.
Fordelen jeg ser i begge forslag er at hver tidsperiode kan gives en id og har en sats saa man ikke skal boevle med at finde sidste opdate. Jeg ville saa lave en saerskilt tabel 'Periode' med tre kolonner, periodeid, periodestart, periodeslut. For eksempel:
Saa kan satstabellen ogsaa forenkles til en tabel med tre kolonner, medarbejderid, periodeid, og sats. For medarbejder nummer 25 faar satstabellen saa, for eksempel, disse raekker:
25 4 100 25 5 110 25 6 120
Saa kan man finde timesatsen for enhver medarbejder paa enhver dato ved at saette $medarbejder lig medarbejderens id og $dato lig datoen og saa anvende denne query:
SELECT sats FROM Satstabel WHERE medarbejder = '$medarbejder' AND periodeid = (SELECT periodeid FROM Periode WHERE '$dato' BETWEEN periodestart AND periodeslut)
Det vil vaere fremtidssikret og vil spare funktioner (langhaarede eller ej) og indviklede udtryk med over 1000 tegn og nestede iifs.
Igen skal dette tilpasses Access syntaksen - jeg har brugt mysql syntaks som jeg kender bedre.
dla, saa du mit indlaeg i forgaars? Jeg er spaendt paa din reaktion, om du kunne foelge min tankegang og om det var til nytte eller om jeg har misforstaaet det hele.
Christian_Belgien: Jeg prøvede at følge din tankegang, men det lykkedes mig ikkke at lave noget brugbart.
I stedet kastede jeg mig over en funktion med parametrede Dato, T1, T2 osv, og det lykkedes!
Jeg lavede en select-case for hver periode, som typisk ser sådan ud:
Case #6/1/2010# To #12/31/2010# Select Case T7 Case Is <> 0 Sats = T7 Case Else Select Case T6 Case Is <> 0 Sats = T6 Case Else Select Case T5 Case Is <> 0 Sats = T5 Case Else Sats = T4 End Select End Select End Select
Det kan godt være denne løsning heller ikke er elegant, men det fungerer, så jeg er meget glad!
hnteknik og Christian_Belgien læg et svar begge to, så deler I pointene fordi I har brugt tid på det.
Det er vigtigt for dig at det er til at gå til og det er fremtidsikret.
Synes godt om
Ny brugerNybegynder
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.