Lange SELECT Anweisung



  • Visual C++ 2008
    Win32 MDI Anwendung
    ODBC - Abfrage über SQLExecDirect

    Hallo,

    ich möchte eine bzw. zwei Tabellen über ODBC auslesen.

    ...
    SQLCHAR		SQLStmt[255] = {0};
    //std::string SQLStmt ="";
    ...
    strcpy_s((char *) SQLStmt,sizeof(SQLStmt), "SELECT * From ZahlV ");
    strcat_s((char *) SQLStmt,sizeof(SQLStmt), "ORDER BY Faelligkeitsdatum ");
    
    rc = SQLExecDirect(Test.StmtHandle, SQLStmt, SQL_NTS);
    

    dies funktioniert soweit.

    Da die Anzahl der Zeichen auf 255 begrenzt ist und die Abfrage die ich neu einbinden möchte, durch Inner Join, Where und From, ca.730 Zeichen lang ist - dachte ich auf std::string umzustellen 🙂

    Da SQLExecDirect als 2ten Parameter ein SQLCHAR erwartet geht dies nicht.

    Wie kann ich vorgehen?

    Vielen Dank


  • Mod

    Nimm CString, gehört zur ATL, oder lege den Buffer mit new an.



  • Du bekommst zwar mit c_str() einen char* Zeiger, der jedoch konstant ist. Am Besten du forderst per new einen ausreichend großen Platz im Freispeicher an, führst die Abfrage durch und kopierst den Inhalt des Speichers in den string. Dieser Speicher kann anschließend freigegeben und mit dem string komfortabel weitergearbeitet werden.



  • Vielen Dank für die Unterstützung.
    Ich versuche es umzusetzen.

    Danke


  • Administrator

    Vicious Falcon schrieb:

    Du bekommst zwar mit c_str() einen char* Zeiger, der jedoch konstant ist. Am Besten du forderst per new einen ausreichend großen Platz im Freispeicher an, führst die Abfrage durch und kopierst den Inhalt des Speichers in den string. Dieser Speicher kann anschließend freigegeben und mit dem string komfortabel weitergearbeitet werden.

    Wieso über new und nicht gleich std::vector<char> ? Dann muss man sich auch keine Gedanken mehr über das Freigeben machen.

    Grüssli



  • [quote="Dravere"]

    Vicious Falcon schrieb:

    Dann muss man sich auch keine Gedanken mehr über das Freigeben machen.

    Klar, geht auch, eigentlich noch besser. Aber bei Objekten mit dermaßen kurzer Lebensdauer kann man sich imho kaum _keine_ Gedanken über die Freigabe machen.


  • Administrator

    Vicious Falcon schrieb:

    Dravere schrieb:

    Dann muss man sich auch keine Gedanken mehr über das Freigeben machen.

    Klar, geht auch, eigentlich noch besser. Aber bei Objekten mit dermaßen kurzer Lebensdauer kann man sich imho kaum _keine_ Gedanken über die Freigabe machen.

    Naja, RAII ist immer gut. Du kannst später plötzlich den Code mal abändern. Plötzlich rufst du eine zusätzliche Funktion auf, welche eine Exception werfen kann. Es gibt tausend Möglichkeiten, dass man es plötzlich mal vergisst. Mit std::vector hast du dieses Problem erst gar nicht. Und einen Nachteil hast du dadurch auch nicht.

    Grüssli



  • Vicious Falcon schrieb:

    Dravere schrieb:

    Dann muss man sich auch keine Gedanken mehr über das Freigeben machen.

    Klar, geht auch, eigentlich noch besser. Aber bei Objekten mit dermaßen kurzer Lebensdauer kann man sich imho kaum _keine_ Gedanken über die Freigabe machen.

    die frage ist ob man robuste software basteln möchte, oder ob es einem mehr spass macht dauernd über trivialen fliegenschiss nachzudenken (wie z.b. ob jetzt statement X eine exception werfen könnte oder doch nicht)

    soll heissen: in so klaren fällen wie new+delete, lock+unlock etc. mache ich es lieber gleich "richtig". und wenn ich dazu nichtmal eine eigene guard-klasse schreiben muss, weil eine passende schon existiert, dann verstehe ich auch wirklich nicht wieso um alles in der welt es jemand anders machen wollen würde.



  • Aber gerade als Anfänger sollte man sich (auch) Gedanken über das grundlegende Speichermanagement machen. Die Aussage nimm std::vector und alles ist gut, gilt jedenfalls nicht.

    // es wird suggeriert, es so zu machen
    std::vector<int> v(-1); // crasht auf jedem System
    // so sollte es eigentlich sein
    std::vector<int> v2;
    try {
    	v2.resize(-1);
    }
    catch(std::exception&) {
    	// oh
    }
    int* p = new int[-1]; // wird gar nicht erst kompiliert (VC)
    

    Natürlich sind die Container der Standardbibiothek bequem, die Grundlagen sollten dennoch verstanden sein, bevor diese Klassen zum Einsatz kommen.

    Edit: Bevor noch einmal auf mögliche exceptions eingegangen wird: Eine Datenbankabfrage sollte niemals eine exception werfen und sollte so gekapselt werden, dass bspw. der string "leer" ist, aber nie nie nie eine exception geworfen wird.
    Edit 2 : d!= f



  • Vicious Falcon schrieb:

    Eine Datenbankabfrage sollte niemals eine exception werfen und sollte so gekapselt werden, dass bspw. der string "leer" ist, aber nie nie nie eine exception geworfen wird.

    Kannst du das begründen? Ich halte das nämlich für einigermassen gefährlichen Unsinn.

    Man sollte definitiv damit rechnen dass Datenbankabfragen schief gehen, denn das tun sie einfach hin und wieder - genügend Last vorausgesetzt. Man sollte diese Fehler allerdings auf keinen Fall zu früh behandeln, indem man z.B. einfach ein leeres Ergebnis im Fehlerfall zurückliefert oder eine Zeile mit lauter leeren Strings/Nullen/NULL-Werten.

    Wenn man sowas nämlich in einer Wrapper-Schicht macht, dann werden darauf aufbauende Programmteile mit falschen Daten "rechnen" (arbeiten), und anfangen Unsinn zu produzieren. Solche Fehler sind oft schwer zu debuggen und auch oft folgeschwer. Zumindest bei den Anwendungen die ich bisher programmiert habe war ein falsches Ergebnis definitiv schlimmer, als gar kein Ergebnis mit der Info dass irgendwo ein Fehler aufgetreten ist.



  • Du wirst das schon richtig verstanden haben, dass der Returnwert Aufschluss darüber gibt, ob die Anfrage erfolgreich war (am Besten ein enum: ok, timeout, connectionfailure etc.).
    PS: Lebst du in einer anderen Zeitzone? Nein, musst nicht antworten...



  • Vicious Falcon schrieb:

    Du wirst das schon richtig verstanden haben, dass der Returnwert Aufschluss darüber gibt, ob die Anfrage erfolgreich war (am Besten ein enum: ok, timeout, connectionfailure etc.).

    Und was soll das für einen Sinn haben?

    p.S.: ich halte das immer noch für gefährlichen Unsinn.



  • Hallo,

    vielleicht habe ich mich nicht richtig ausgedrückt:
    Es sollte (in diesem Fall) nicht das Ergebnis der Abfrage gespeichert werden sonder es ging mir um die Abfrage selbst.

    SQLCHAR        SQLStmt[255] = {0};
    

    Hier habe ich SQLStmt auf die 'Größe' 255 festgelegt - ich könnte hier vermutl. auch 1280 festlegen.

    strcpy_s((char *) SQLStmt,sizeof(SQLStmt), "SELECT * From ZahlV "); 
    strcat_s((char *) SQLStmt,sizeof(SQLStmt), "ORDER BY Faelligkeitsdatum ");
    

    Hier wurde 'umständlich' der Querytext "Select usw. ..." in die Variable SQLCHAR SQLStmt kopiert bzw. angehängt.

    rc = SQLExecDirect(Test.StmtHandle, SQLStmt, SQL_NTS);
    

    Und hier wird die Abfrage KORREKT ausgeführt mit der Select-Anweisung.
    In der ODBC Standardlibrary ist SQLCHAR ein unsigned char.

    Wenn ich jetzt die Select Anweisung ändere auf "SELECT... InnerJoin ...FROM ...Where..." ca. 700 oder 800 Zeichen lang, ist die strcpy_s bzw. strcat_s sehr umständlich, da ich die aufgeführte Select-Anweisung in mehreren Zeilen in meinem Editor aufteilen muss - und das Problem ist dass ich nicht genau weiß wie lang die Select Anweisung ist. Änder ich manuell die SQLStmt-Größe ab - funktioniert die Abfrage wieder ansonsten crash.

    Ich habe nun versucht mit CString zu arbeiten:

    string SQLText;
                SQLText = "Select ...";
                SQLText +=" Innerjoin ...";
                ...
    strcpy_s((char *) SQLStmt,sizeof(SQLStmt), SQLText.c_str());
    

    Nun ging ich davon aus das der SQLText.c_str() einfach in die strcpy_s-Funktion geschrieben werden kann - gibt jedoch einen Fehler string kann nicht in char konvertiert werden. Ich habe dann sehr viel rumprobiert und glaube dass es vermutlich eine ganz einfache Lösung gibt.

    Was ist z.B. mit vector<char> bezogen auf mein(dies) Problem genau gemeint?

    Dravere schrieb:

    Wieso über new und nicht gleich std::vector<char>? Dann muss man sich auch keine Gedanken mehr über das Freigeben machen.
    Grüssli

    Vielen Dank für euere Geduld



  • Hallo,
    darf ich noch einmal hierzu nerven?

    Um meine Fehler besser erkennen zu können habe ich mit cout an mehreren Stellen die Inhalte der String-Variablen anzeigen lassen, da die Kompilierung zeitweise fehlerfrei durchlief.

    Hier meine String-Funktion:

    void LangerText(string *strText)
    {
         string strTmp ="";
         strTxt="SELECT OP.Datum, OP.Konto, ... ";
         strTxt+="Debitor.Name ...";
    
         *strText = (const char) strTmp.c_str();
         cout << strTxt.c_str() << endl;// Text wird korrekt angezeigt
    }
    

    Der geänderte Aufruf:

    string strTxt="";
     string strTmp = LangerText( &strTxt);
    
     strcpy_s((char *) SQLStmt ,sizeof(SQLStmt),(char *)strTmp.c_str());
    

    Fehlermeldung;
    error C2440: 'Initialisierung': 'void' kann nicht in 'std::basic_string<_Elem,_Traits,_Ax>' konvertiert werden

    Ich hatte auch dies versucht:

    string strTxt="";
    LangerText( &strTxt);
    
    strcpy_s((char *) SQLStmt ,sizeof(SQLStmt),(char *)strTxt.c_str());
    cout << strTxt.c_str() << endl;//Hier wurde nur eine 8 angezeigt
    

    Hierbei war keine Compiler-Fehlermeldung aber im strTxt-Text , über cout, war nur der Wert "8"?? Ich ging hier davon aus der der Referenzparameter verändert wird??

    Vielen Dank



  • *strText = (const char) strTmp.c_str();
    

    Das ist Quatsch, das kann nicht gehen. Du machst aus einem Zeiger ein char.

    Wenn die "direkte" Zuweisung nicht geht, dann liegt das vermutlich daran dass SQLCHAR kein char sondern ein wchar_t ist. D.h. du musst entweder einen "narrow" String nach Unicode konvertieren, oder aber gleich mit Unicode arbeiten.
    Also L"text" statt "text" und überall wchar_t statt char.


Anmelden zum Antworten