none
Recordcount liefert -1 obwohl 1 Treffer vorhanden sein muss RRS feed

  • Frage

  • Hallo,

    ich benutze folgende Abfrage, um aus 2 Spalten "Länge" und "Breite" alle diejenigen zurück geliefert zu bekommen, die in den beiden Spalten keine Leereinträge haben und auch nicht doppelt vorkommen:

    SELECT

    [Quelle], [A Breite], [A Länge], '', a.Anzahl, [Netzbetreiber]

    FROM (SELECT DISTINCT [A Breite], [A Länge], [Quelle], [Netzbetreiber], COUNT(*) as Anzahl

                       

    FROM [Fx__Test_D2]


                       

    WHERE ([A Breite]<>N'' and [A Länge]<>N'') 

                       

    GROUP BY [A Breite],[A Länge],[Quelle], [Netzbetreiber]) as a

                

    ORDER By a.Anzahl desc

    Die Abfrage in Access liefert bei objRecordset.Recordcount -1, obwohl tatsächlich ein Treffer da sein müsste!!!

    Was ist da los???

    Gruß Jürgen

    Montag, 9. März 2015 12:12

Antworten

  • Hallo Jürgen,

    ich meine mich dunkel aus meinen VB6 Zeiten mit ADO zu erinnern das dies an   .CursorLocation = adUseServer liegt. Du musst damit RecordCount funktioniert   .CursorLocation = adUseClient nutzen.

    Gruß


    Jens Gerber

    • Als Antwort vorgeschlagen Olaf HelperMVP Montag, 9. März 2015 13:53
    • Als Antwort markiert Jürgen Sch Montag, 9. März 2015 13:57
    Montag, 9. März 2015 13:29

Alle Antworten

  • Hallo Jürgen,

    mit dem Ergebnis -1 will Dir MS Access (genauer DAO) mitteilen, das es derzeit nicht weiß, wieviele Datensätze vorliegen. Siehe MSDN Recordset.RecordCount-Eigenschaft (DAO) => Hinweis: Die RecordCount-Eigenschaft gibt erst an, wie viele Datensätze in einem Recordset-Objekt vom "dynaset"-, "snapshot"- oder "forward-only"-Typ enthalten sind, wenn auf alle Datensätze zugegriffen wurde


    Olaf Helper

    [ Blog] [ Xing] [ MVP]


    Montag, 9. März 2015 12:16
  • Hallo Olaf,

    danke für die schnelle Antwort.

    Ich habe die Abfrage einmal in der SQL-Management-Konsole direkt gemacht und da kam auch tatsächlich der eine Treffer.

    Starte ich aber über VBA in Access die selbe Abfrage kommt dieser nicht definierte Hinweis.

    Habe ich über VBA etwas vergessen, denn bei objRecordset.Fields.Count kommt die richtige Spaltenanzahl 6 raus???

    Montag, 9. März 2015 12:38
  • Hallo Jürgen,

    objRecordset.Fields.Count liefert die Anzahl der Spalten. Du willst ja aber die Anzahl der Zeilen. Daher solltest Du das Recordset mit

    objRecordset.Open <SqlStatement>, <Connection>, 3, 1

    öffnen (ggfs. auch , 3, 3 anstelle von , 3, 1). Siehe dazu auch:

      http://www.aspfaq.de/index.asp?FID=4&ELE=354

    für den Fall, dass Du mit Set objRecordset = <Connection>.Execute <SqlStatement> arbeiten solltest.



    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Montag, 9. März 2015 12:46
    Moderator
  • Hallo Jürgen,

    warum willst Du unbedingt die Anzahl haben? Normalerweise prüft man mit Eof, ob noch Datensätze vorliegen und arbeitet die ab.


    Olaf Helper

    [ Blog] [ Xing] [ MVP]

    Montag, 9. März 2015 12:47
  • Danke Stefan,

    aber ich kann hinter dem SqlStatement nicht mehr angeben. Meine Abweisungen in VBA sehen wie folgt aus:

    '***SQL-Abfrage an Server senden
        Set ObjRecordset = CreateObject("ADODB.Recordset")
        With ObjRecordset
            .ActiveConnection = objConnSQL
            .CursorLocation = adUseServer
            .CursorType = adOpenKeyset
            .LockType = adLockOptimistic
            .Open = strSQL                          's. obige Abfrage
        End With

        a = ObjRecordset.Fields.Count
        b = ObjRecordset.RecordCount

    Montag, 9. März 2015 12:55
  • Ich brauche die Anzahl der Ergebnisdatensätze, da diese in eine Excelmappe geschrieben werden sollen, um sie von dort aus nach MapPoint zu übergeben.

    Bei einer Menge von Datensätzen, 1-3 Millionen kann das dann doch schon mal länger dauern und dann kann ich mit der Gesamtzahl eine Statusanzeige generieren.

    Montag, 9. März 2015 13:02
  • Hallo Jürgen,

    ich meine mich dunkel aus meinen VB6 Zeiten mit ADO zu erinnern das dies an   .CursorLocation = adUseServer liegt. Du musst damit RecordCount funktioniert   .CursorLocation = adUseClient nutzen.

    Gruß


    Jens Gerber

    • Als Antwort vorgeschlagen Olaf HelperMVP Montag, 9. März 2015 13:53
    • Als Antwort markiert Jürgen Sch Montag, 9. März 2015 13:57
    Montag, 9. März 2015 13:29
  • Hallo Jens,

    jau, das wars (.CursorLocation = adUseClient). Jetzt wird mir der richtige Wert angezeigt.

    Danke Danke.

    Das hatte ich auch schon mal da benutzt, nur sagte mir son anderer Schlaumeyer, dass auf großen Datenbeständen das mit adUseServer schneller laufen würde. Dass aber dann das RecordCount nicht mehr funzt, davon sagte er nix.

    Naja, geht ja jetzt wieder. :-)

    Danke an  Alle

    Montag, 9. März 2015 13:42
  • Hallo Jürgen,

    so ganz unrecht hatte der Schlaumeyer nicht. adUseServer ist wirklich schneller. Allerdings kann eben kein RecordCount abgerufen werden weil die Datensätze nicht komplett in den Client geladen werden. Du könntest aber wenn es schneller laufen soll adUseServer nutzen und den benötigten Count der Datensätze vorher in einem separaten SQL Statement (Select count(*) as count from ....) abfragen und in einer Variable speichern.

    Gruß


    Jens Gerber

    Montag, 9. März 2015 14:07
  • Hallo Jürgen,

    die Vorliebe mancher Entwickler unbedingt die Anzahl des Ergebnisses vor dem Ergebnis selbst wissen zu wollen, macht sehr wohl die Geschichte langsamer. Denn der RecordCount ist immer nur ein Nebeneffekt der Verarbeitung.

    adUseClient bedeutet ebenso, dass erst alle Zeilen gelesen werden müssen und sich im Speicher breit machen. Das geschieht zwar mit einem optimierten Cursor, aber der Speicher ist trotzdem eher voll.

    adUseServer kann wiederum die Daten ohne Overhead liefern und sobald man die Zeile verarbeitet hat wird sie (im Idealfall[1]) wieder vergessen. Siehe DECLARE CURSOR mit FAST_FORWARD für den "schnellsten" Zugriff - entspricht adOpenForwardOnly mit adLockReadOnly.

    Insofern solltest Du Dich erneut fragen, wer der Schlaumeier ist?

    Gruß Elmar

    [1] In Deinem Falle hängt es insgesamt mehr von der Effizienz des GROUP BY ab.

    Montag, 9. März 2015 14:18
  • Ok Jens,

    wenn ich also adUseServer nutzen will, wie muss dann genau die Select Count(*) as Count from ... insgesamt heißen, damit ich die Anzahl in eine Variable speichern kann???

    Gruß Jürgen

    Montag, 9. März 2015 15:46
  • Hallo Jürgen,

    SELECT COUNT(*) ist genauso wenig schlau... Denn das Ergebnis kann falsch sein, wenn sich die Datenbasis zwischenzeitlich ändert, und selbst wenn nicht, wird die Abfrage zweimal ausgeführt, was wieder Zeit kostet (schlimmstenfalls das doppelte).

    Das optimale Vorgehen lautet: Durchlaufe die Datenzeilen - ein While Not rs.Eof reicht dafür - und ganz am Ende darfst Du auf RecordCount zugreifen, soweit Du die Zahl überhaupt noch benötigst.

    Um es noch einmal klar zustellen: Ein SQL Server versucht, wenn möglich die Daten erst zu lesen, wenn sie gebraucht werden und sobald sie ausgeliefert wurden, werden sie wieder vergessen, um den Speicher für anderes frei zu haben.

    Das kann man in etwa mit dem Durchsuchen eines Karteikastens vergleichen, aus denen man die nur Karteikarten entnimmt, die den gesuchten Kriterien entsprechen, sie anschaut und wieder wegsteckt. Erst wenn der Kasten vollständig durchsucht ist, steht fest, wie viele Treffer (RecordCount) es gibt.

    Gruß Elmar

    Montag, 9. März 2015 15:56
  • Hallo Elmar,
    das ist alles zweifelsohne richtig was du schreibst. Und bei allem Respekt vor dir und deinem 
    extrem umfangreichem und detailliertem Wissen. (Keinerlei Ironie !!! ich habe schon zu den MS Newsgroup Zeiten sehr viele hilfreiche Beiträge von dir verfolgt vor vielen Jahren)

    Natürlich wären das 2 Abfragen. Aber (die erste) ein Select Count(auf ein indiziertes Feld) kann man 
    doch nicht vergleichen mit dem Abruf aller kompletten Daten des Statements was die eigentlichen Daten 
    liefert. Das geht i.d.R. doch rasend schnell weil es nur ein Datensatz ist (hängt natürlich
    von seiner Datenbank Struktur ab). Wie sonst soll er bei einer für den User sichtbaren Aktion
    einen Fortschrittsbalken realisieren ohne annähernd die Anzahl der Datensätze zu kennen ?
    Und selbst wenn sich die Daten wärend der laufenden Aktion verändern, was bei der Anzahl der
    Datensätze vernachlässigbar sein sollte... (wobei das natürlich auch stark von der Dauer der Ausführung der eigentlichen Abfrage abhängt) hat der User zumindest eine Sicht auf den Fortschritt
    der Aktion. Sollte es ein best practice dazu geben, würde mich das auch interessieren.

    VG


    Jens Gerber

    Dienstag, 10. März 2015 00:24
  • Guten Morgen zusammen,

    Vielleicht noch mal der Hintergrund, warum ich die Gesamtzahl der Treffer in einem Rutsch (RecordCount) und nicht sequentiell (While Not rs.EOF) brauche:

    Ich habe in einer SQL-Tabelle ca. 1,5 Millionen Datensätze mit 46 Datenspalten. In 2 Spalten stehen Geokoordinaten, die aber nicht bei jedem Datensatz vorhanden sein müssen. Ich möchte nun diese Datensätze mit vorhandenen Geokoordinaten herausfiltern, in eine Excelmappe überführen, um sie von dort nach MapPoint übergeben zu können, damit sie in MapPoint dargestellt werden können.

    Nach der Select ... Abfrage schiebe ich also das komplette Recordset mit

    .Range(.Cells(2, 1), .Cells(b + 1, a)).CopyFromRecordset ObjRecordset, b, a '(b = Recordcount)

    in eine Excelmappe. Ohne den Recordcount, habe ich keine Range-Begrenzung.

    Oder gibt es dafür eine andere Möglichkeit, z.B. die Zeilen des Recordset sequentiell auf das Excel-Tabellenblatt zu schreiben???

    Gruß Jürgen

    Dienstag, 10. März 2015 06:00
  • Hallo Jürgen,

    für den Fall braucht man das nicht. Füge Deiner Abfrage eine TOP-Klausel hinzu (und wirf das überflüssige DISTINCT raus). CopyFromRecordset ist so konzipiert, das es ohne auskommt - wie die Dokumentation mit dem Hinweis verrät, am Ende steht das RecordSet auf EOF.

    Mehr Gedanken solltest Du Dir bei der Tabellengröße über weitere Kriterien / Filter machen. Denn die derzeitige WHERE Klausel ist kein (selektives) Ausschluss-Kriterium. Nicht vorhandene Koordinaten sollten z. B. IS NOT NULL (oder äquivalent) gleich eliminiert werden. Optionale Begrenzer auf Quelle / Netzbetreiber könnten das Ganze beschleunigen, wenn man einen Index darauf hat.

    Gruß Elmar

    Dienstag, 10. März 2015 08:29
  • Hallo Jens,

    eine kürzere Antwort, nach dem mir der Browser beim ersten Senden abstürzte und sich lapidar für die Zeitverschwendung "entschuldigte".

    Ein Fortschrittsbalken und ein "rasend schnelles"  SELECT COUNT(*) sind IMO ein Widerspruch an sich. Möchte Programmierer einen Fortschrittsbalken, so wird er allgemein erwarten, das es länger dauern könnte.

    Ein SELECT COUNT(*) ist keine Schätzung, sondern wird buchhalterisch genau ausgeführt. Ist die zugrundeliegende Abfrage mehr als trivial - das GROUP BY der Ausgangsfrage fällt schon darunter - wird das Zählen nicht wesentlich  schneller sein, als die Rückgabe des vollständigen Ergebnisses. Einzig die Netzwerkbelastung ist reduziert, doch der SQL Server muss die Abfrage zweimal ausführen, und benötigt dafür fast die gleichen Ressourcen.

    Mit den "Best Practices" ist das so eine Sache. Ein adUseServer / Firehose Cursor ist nicht automatisch die bessere Wahl. Will man in einem Ergebnis auf- und abblättern, wahlfrei zugreifen, so reduziert ein Client Cursor sehr wohl den Ressourcen-Bedarf. Wie man es auch bei ADO.NET mit einer Auflistung tut (oder es von einem ORM tun lässt).

    Andersrum ist eine Übersichtseite (ala Google Suche) mit einem serverseitigen Cursor besser bedient (und Googles Zahlen sind "Schätzungen").

    Für den Fortschrittsbalken sollte es i. a. ein "Ladekringel" tun... Informiert man über jede gelesene Zeile, so begeht man schnell den nächsten Fehler und blockiert die Daten auf Serverseite (sichtbar an den Wait Statistics, insbes. ASYNC_NETWORK_IO), anstatt die Daten so schnell wie möglich abzurufen.

    Am Ende sollte sich Programmierer darüber schlau machen, was hinter den Kulissen stattfindet, auch selbst mal messen (mit realen Datenvolumina und nicht einer Handvoll Testdaten). Blind den heute allerorten zu findenden Tutorials zu folgen reicht nicht - deren frei verfügbare Video-Inkarnationen mich in ihrer Unbedarftheit oft erschrecken.

    Gruß Elmar


    • Bearbeitet Elmar Boye Dienstag, 10. März 2015 08:55
    Dienstag, 10. März 2015 08:54