Avatar billede itognet Nybegynder
25. april 2004 - 19:15 Der er 7 kommentarer

Den umulige delphi/database opgave

Hej alle,

jeg er faldet over den "umulige" opgave.

Det går simpelt ud på at vi har et delphi program der snakker i en database.
Jeg ville gerne have delphi til at tjekke for om tabellerne / tabel stukturen er korrekt i forhold til hvad delphi forventer - og hvis den ikke er, så opret det der mangler.

f.eks.
Tabel:
Fornavn, Efternavn, Telefon

Delphi forventer:
Fornavn, Efternavn, Adresse, Telefon.
-Og derfor skal delphi selv oprette Adresse feltet i databasen, hvorefter at database og delphi programmet er "synkrone" tabel struktur mæssigt.

Er der nogen der er faldet over en komponenter der f.eks. kan dette - eller har et godt forslag til at få det til at virke.
Avatar billede martinlind Nybegynder
25. april 2004 - 19:59 #1
Hårdt arb.
Avatar billede doc404 Novice
25. april 2004 - 20:05 #2
Du skriver i HAR et Delphi program, der taler med en database. Hvilken database?

Og hvorfor skulle opgaven være umulig?
Avatar billede itognet Nybegynder
26. april 2004 - 08:54 #3
Den er heller ikke umulig, men er ret svær at koncentrerere. Da man laver rutinen i dag, men at det først er om et år eller to, at den kommer til rigtig virkning, ved at man på dét tidspunkt udvider databasen/programmet med noget som ikke er kendt i dag.
Databasen/programmet forestilles at ligge på fx 20 enkeltstående maskiner, hvilket jo vil lave en del benarbejde, hvis man manuelt skal upgrade databaserne.

Vi benytter en interbase 7 database, hvilket godt kan anbefales. vi har et par triggere, samt et par compute by felter + alm. felter.
Avatar billede nop Nybegynder
26. april 2004 - 13:16 #4
cursorWait;
    tnList:=TstringList.Create;
    db.getTableNames(tnList);
    exists:=(tnList.IndexOf('tab1')<>-1) and
            (tnList.IndexOf('tab2')<>-1) and
            (tnList.IndexOf('tab3')<>-1);
//og viderer
    if exists then begin
        db.getFieldNames('contact',ds.prefix,tnList);
        exists:=(tnList.IndexOf('id')<>-1) and
                (tnList.IndexOf('name')<>-1) and
                (tnList.IndexOf('address')<>-1);
    end;
    tnList.Free;
    cursorNormal;
//exists holder om rigtig db

Det her er noget kode som jeg bruger til at teste om der peges til en bestemt database. (Hvis du virkelig analyserer kode vil du se at den ikke tester på alle tabellernes felter, men det er ok til mit brug).

Det kunne bygges op til at osse lave "alter table" osv. Men du ville osse have brug for at læse felternes properties selvfølgelig, ikke noget lille projekt, for hvis du skal lave en alter, og vil beholde de felter der allerede er i en tabel skal de jo med i "alter" scriptet. Eller hvad?
Avatar billede pigbear Nybegynder
03. juni 2004 - 09:48 #5
Hej

Måske det er en ide lave en select sætning omgivet af en try sætning, så
kan man fx gøre dette:

try
query1.sql.clear;
query1.sql.add('Select Fornavn from databasetabell');
query1.open;
except
Showmessage('Feltnavnet findes ikke');
end;

Hvis programmet ikke falder i except så findes den angivne kolonne.
Så kan du lave et check for hvert feltnavn i dine tabeller, og hvis du
falder i except sætningen så er det fordi du mangler en kolonne.(Eller at
der er andre fejl(fx hvis en tabel mangler eller forbindelse til databasen mangler) men dem kan man fange først )

Mvh

PigBear

NB:
Et spørgsmål: Er der nogen der kan sige mig hvordan man fanger hvilken type
et felt i en tabell er, fx. telefon kolonnen i ovenstående eksempel. Kan man checke om det er af typen integer, varchar/string ellar float etc. ?
Avatar billede nop Nybegynder
03. juni 2004 - 10:43 #6
pigbear (og andre)>>

Det her vil æde en tabel for en allerede åben connection.


type

    TmyDb = class
        ...
        public
            connection:    TadoConnection;
            prefix:        string;
        .....
       

    TnumberInfo = record
        neverSet:          boolean;
        valFrom,valTo:      extended;
        notZero:            boolean;
        blankWhenZero:      boolean;
        sep1000:            boolean;
        dynLength:          boolean;
        signAllowed:        boolean;
        digInt,digFrac:    integer;
        designFmt:          string;
    end;

    TstringInfo = record
        neverSet:          boolean;
        maxLength:          integer;
        minLength:          integer;
        upperCase:          boolean;
        lowerCase:          boolean;
        noBlanks:          boolean;
        padBlanks:          boolean;
        trimTailingBlanks:  boolean;
        trimLeadingBlanks:  boolean;
        trimMultiBlanks:    boolean;
        designFmt:          string; //desinger kan vise fmt for et felt
    end;

    TdateTimeInfo = record
        neverSet:          boolean;
        format:            string;
        designFmt:          string;
        parts:              string;
        timeFlag:          boolean;
        dateFlag:          boolean;
        isSearch:          boolean;
    end;

    TfieldInfoRecord = record
        name:      string;
        typ:        fieldType;
        numInfo:    TnumberInfo;
        strInfo:    TstringInfo;
        dtInfo:    TdateTimeInfo;
    end;

var
    fieldInfo:  record
        count:  integer;
        list:  array of TfieldInfoRecord;
    end;


procedure TmyDb.getFieldTypes(const tableName,prefix: string);
var
    fields:    TADODataSet;
    fname:      string;
    ftype:      word;
    curIx:      integer;

    procedure finNum(var numInfo: TnumberInfo);
    //finalize number
    // find min,max udfra data
    var
        vi,vf:  extended;
        i:      integer;
    begin
        with numInfo do begin
            if not dynLength then
                if digInt+digFrac > 16 then
                    digInt:=16-digFrac;
            if digInt > 3 then
                sep1000:=true
            else
                sep1000:=false;
            blankWhenZero:=false;
            notZero:=false;
            vi:=0; vf:=0;
            for i:=1 to digFrac do
                vf:=vf / 10 + 0.9;
            for i:=1 to digInt do
                vi:=vi*10+9;
            if dynLength then
                valTo:=vi+1
            else
                valTo:=vi+vf;
            if signAllowed then
                valFrom:=-valTo
            else
                valFrom:=0;
        end;
    end; //fin num

    procedure nullType;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeNull;
        end;
    end; //null type

    procedure unsupported;
    begin
        nullType;
    end; //unsupported

    procedure numFixed;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeNumber;
            with numInfo do begin
                digInt:=fields.FieldByName('numeric_precision').Value;
                digFrac:=fields.FieldByName('numeric_scale').Value;
                digInt:=digInt-digFrac;
                dynLength:=false;
                signAllowed:=true;
                //info(name+': number fixed '+toStr(digInt)+','+toStr(digFrac));
            end;
            finNum(numInfo);
        end;
    end; //num fiexed

    procedure numInt(signed: boolean);
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeNumber;
            with numInfo do begin
                digInt:=fields.FieldByName('numeric_precision').Value;
                digFrac:=0;
                dynLength:=false;
                signAllowed:=signed;
                //if signAllowed then s:='signed' else s:='unsigned';
                //info(name+': integer '+toStr(numInfo.digInt)+' '+s);
            end;
            finNum(numInfo);
        end;
    end; //num int

    procedure numDyn;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeNumber;
            with numInfo do begin
                digInt:=fields.FieldByName('numeric_precision').Value;
                digFrac:=digInt;
                dynLength:=true;
                signAllowed:=true;
                //info(name+': number dynamic '+toStr(digInt));
            end;
            finNum(numInfo);
        end;
    end; //num dyn

    procedure numMoney;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeNumber;
            with numInfo do begin
                digInt:=fields.FieldByName('numeric_precision').Value-4;
                digFrac:=4;
                signAllowed:=true;
                dynLength:=false;
                //info(name+': number money '+toStr(digInt)+',4');
            end;
            finNum(numInfo);
        end;
    end; //num money

    procedure date;
    var
        precision:  integer;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeDateTime;
            with dtInfo do begin
                //format:='(DK) dd-mm-yyyy hh:nn:ss.zzz';
                if pos('microsoft access driver',lowercase(dsInfo.odbcInfo.driverName))<>0
                then
                    precision:=0
                else
                    precision:=fields.FieldByName('dateTime_precision').Value;
                //info(name+': date '+toStr(precision));
            end;
        end;
    end; //date

    procedure chr;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeString;
            with strInfo do begin
                maxLength:=fields.FieldByName('character_maximum_length').Value;
                if fields.FieldByName('is_nullable').Value then
                    minLength:=0
                else
                    minlength:=1;
                //info(name+': char, min: '+toStr(minlength));
            end;
        end;
    end; //chr

    procedure str;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeString;
            with strInfo do begin
                if fields.FieldByName('is_nullable').Value then
                    minLength:=0
                else
                    minlength:=1;
                maxLength:=fields.FieldByName('character_maximum_length').Value;
                if maxLength=0 then maxLength:=maximumStringLength;
                //info(name+': string, min: '+toStr(minlength)+' max: '+toStr(maxLength));
            end;
        end;
    end; //str

    procedure bit;
    begin
        with fieldInfo.list[curIx] do begin
            name:=fname;
            typ:=typeBool;
            //info(name+': boolean');
        end;
    end; //bit

    procedure sortFieldInfo;
    var
        i,j,ix:    integer;
        t:          TfieldInfoRecord;
    begin
        for i:=0 to fieldInfo.count-1-1 do begin
            ix:=i;
            for j:=i+1 to fieldInfo.count-1 do
                if lowercase(fieldInfo.list[ix].name) > lowercase(fieldInfo.list[j].name)
                then ix:=j;
            if ix<>i then begin
                t:=fieldInfo.list[i];
                fieldInfo.list[i]:=fieldInfo.list[ix];
                fieldInfo.list[ix]:=t;
            end;
        end;
    end; //sort field info

begin //get field types
    fields:= TADODataSet.Create(nil);
    curIx:=0;
    if prefix='' then
        connection.OpenSchema(TSchemaInfo(adSchemaColumns),
                              VarArrayOf([Null,null,tableName,null]),
                              EmptyParam,
                              fields)
    else
        connection.OpenSchema(TSchemaInfo(adSchemaColumns),
                              VarArrayOf([Null,prefix,tableName,null]),
                              EmptyParam,
                              fields);
    fieldInfo.count:=fields.RecordCount;
    setLength(fieldInfo.list,fieldInfo.count);
    curIx:=0;
    while not fields.EOF do begin
        fname:=fields.FieldByName('column_name').Value;
        ftype:=fields.FieldByName('data_type').Value;
        case ftype of
            0,1:    nullType;
            2,3:    numInt(true);
            4,5:    numDyn;
            6:      numMoney;
            7:      date;
            8:      str;
            9,10:  unsupported;
            11:    bit;
            12,13:  unsupported;
            14:    numFixed;
            15:    unsupported;
            16:    numInt(true);
            17:    numInt(false);
            18,19:  numInt(false);
            20:    numInt(true);
            21:    numInt(false);
            128:    unsupported;
            129:    chr;
            130:    str;
            131:    numFixed;
            132:    unsupported;
            133:    unsupported;
            134:    unsupported;
            135:    date;
            136,137,138,139:    unsupported;
        else
            unsupported;
        end;
        fields.Next;
        inc(curIx);
    end;
    fields.Free;
    sortFieldInfo;
end; //TmyDb get field types

Det er godt nok revet ud af sammenhæng men det kan nok bruges, og så er det gratis :)

--nop
Avatar billede nop Nybegynder
03. juni 2004 - 10:45 #7
man kan sagtens lave den uden TmyDb classen, der skal så bare åbnes connection et andet sted, evt i selve getFieldTypes, eller connection som parameter.
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
Kurser inden for grundlæggende programmering

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