Avatar billede dla Praktikant
25. januar 2011 - 15:28 Der 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.

Mvh Dorit
Avatar billede hnteknik Novice
25. januar 2011 - 15:48 #1
HMmmm..

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.
Avatar billede jensen363 Forsker
25. januar 2011 - 15:50 #2
Umiddelbart tror jeg du får problemer med antallet af IIF i din forespørgsel ... mener kun at der accepteres 7 ...

Når det så er sagt, ville jeg anbefale at du opretter en satstabel med gyldighedsperiode fra/til og relaterer til dine data
Avatar billede mugs Novice
25. januar 2011 - 15:58 #3
Det er korrekt, at dr kun accepteres 7 IIF i en forespørgsel, men dete kan omgås, ved at sætte en IFF mere ind i den eksisterende IIF.
Avatar billede dla Praktikant
25. januar 2011 - 16:05 #4
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.
Avatar billede hnteknik Novice
25. januar 2011 - 16:29 #5
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.
Avatar billede hugopedersen Nybegynder
25. januar 2011 - 17:15 #6
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.
25. januar 2011 - 17:26 #7
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:

startdato slutdato timesats
1/6/2007    31/12/2007  1000
1/1/2008    31/12/2008  1010
1/1/2009    30/5/2009  1020

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?
25. januar 2011 - 17:27 #8
Jeg saa ikke hugopedersens indlaeg foer jeg skoed dette af.  Jeg er ikke uenig i hvad han siger.
Avatar billede hnteknik Novice
25. januar 2011 - 20:10 #9
Hej - jeg har ikke et eks. at hand, men selve brugen af custom functions in queries ses i et afsnit her:
http://www.fmsinc.com/tpapers/queries/index.html

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.
Avatar billede dla Praktikant
26. januar 2011 - 10:49 #10
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.

Please, hjælp med funktionen!
Avatar billede hnteknik Novice
26. januar 2011 - 11:10 #11
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.

Men det er som sagt en lidt større sag.
Avatar billede dla Praktikant
26. januar 2011 - 11:21 #12
Helt i orden - jeg prøver selv.

Men kan jeg ikke lave funktionen med If - Elseif, i princippet ligesom i udtrykket? Det kan jeg bedre styre end Select Case.
26. januar 2011 - 14:34 #13
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?
Avatar billede hnteknik Novice
26. januar 2011 - 15:06 #14
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
   
Resume Exit_Index2

End Function
Avatar billede dla Praktikant
26. januar 2011 - 15:34 #15
hnteknik: Din kode er lige lovlig langhåret til mit niveau, men jeg lover dig at kigge på det og prøve at få hoved og hale på det.

Christian: Jo, jeg vil meget gerne have det forenklet hvis jeg vidste hvordan jeg skulle gøre!

Min satstabel består af medarbejdernavn, timesats1, timesats2, osv. (det er ikke en fejl at perioden er 5 mdr. og 7 mdr.).

Bliver satserne opdateret er det for alle medarbejdere.

SenesteÆndring hentes fra en tabel som bliver opdatet med docmd.sql "update...osv. når satserne bliver opdateret i formularen.

Derudover er der selvfølgelig en tabel hvor medarbejderne skriver deres timer ind.
26. januar 2011 - 16:56 #16
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:

4 1/6/2009 31/12/2009
5 1/1/2010 31/12/2010
6 1/1/2011 31/5/2011

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.
28. januar 2011 - 17:37 #17
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.
Avatar billede dla Praktikant
31. januar 2011 - 10:16 #18
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.

Mvh og tak
Dorit
31. januar 2011 - 10:22 #19
svar fra mig.
Avatar billede hnteknik Novice
31. januar 2011 - 15:15 #20
Det er vigtigt for dig at det er til at gå til og det er fremtidsikret.
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
Dyk ned i databasernes verden på et af vores praksisnære Access-kurser

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