Warum liefern zwei SQL-Suchen unterschiedliche Ergebnisse, obwohl das gleiche rauskommen sollte? [Workaround gefunden]

  • SQL

Es gibt 33 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    Warum liefern zwei SQL-Suchen unterschiedliche Ergebnisse, obwohl das gleiche rauskommen sollte? [Workaround gefunden]

    Hallo zusammen.

    Ich habe zwei SQL-Abfragen:

    SQL-Abfrage

    1. SELECT ArtNr1,ArtNr2 FROM Tabelle WHERE ArtNr1='123' OR ArtNr1='456'

    SQL-Abfrage

    1. SELECT ArtNr1,ArtNr2 FROM Tabelle WHERE ArtNr2='111222333'

    Wenn ich die durchlaufen lasse, erhalte ich für die erste Abfrage {"123", "111222333"} und {"456", "111222333"}, aber für die zweite Abfrage nur {"456", "111222333"}. Das erste Ergebnis fehlt. Was übersehe ich oder was mache ich falsch?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Leider unverändert. Ich habe mir zuerst Strings geben lassen und dann für die 2. Abfrage eben diese als Argument verwendet. Jetzt mit Objects, mit allem auswählen und LIKE, aber unverändert:
    Habe ArtNr1 und ArtNr2 hier im Code getauscht, da die Reihenfolge in der DB-Tabelle anders ist.

    VB.NET-Quellcode

    1. Dim SQL = $"SELECT ArtNr1,ArtNr2 FROM Tabelle WHERE ArtNr2='11035088' OR ArtNr2='12894020'"
    2. Dim ResultList As New List(Of (Object, Object))
    3. Using Reader = MyDataReader.ExecuteReader(SQL)
    4. While Reader.Read()
    5. ResultList.Add((Reader.GetValue(0), Reader.GetValue(1)))
    6. End While
    7. End Using
    8. SQL = $"SELECT * FROM Tabelle WHERE ArtNr1 LIKE '%{ResultList(0).Item1}%'"
    9. Using Reader = MyDataReader.ExecuteReader(SQL)
    10. While Reader.Read()
    11. ResultList.Add((Reader.GetValue(0), Reader.GetValue(1)))
    12. End While
    13. End Using


    Ergebnis im Anhang. über der Linie die Ergebnisse der ersten Abfrage, darunter die der 2.
    Ich kann die Datenbank nicht einsehen - Fremdanbieter.
    Bilder
    • Results.png

      6,04 kB, 324×94, 92 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    vielleicht verändert sich ja der Zustand von MyDataReader durch die Execution.
    Was Ist MyDataReader ühaupt für ein Dingens?

    Testweise könnte man auch mal mit einem DataAdapter eine DataTable (in beiden Varianten) befüllen:

    VB.NET-Quellcode

    1. dim adp=new SqlDataAdapter(selectCommandText, connectionString)
    2. adp.SelectCommand.Connection.Open()
    3. dim tb=adp.GetTable()
    die Tabelle kann man ja direkt im Debugger angucken.
    Der Datentyp ist laut ILSpy (in der verwendeten DLL sind die Tabellentypen drin) jeweils String. Das mit LIKE '%{ResultList(1).Item1}%' habe ich probiert - unverändert 3 Ergebnisse. Ich habe die ArtNr1-Werte so »vergleichen« lassen:

    VB.NET-Quellcode

    1. Dim GroupedList ResultList.GroupBy(Function(x) x.Item1)
    Es ergibt sich eine Gruppe mit 3 Einträgen, daher sind die Langnummern identisch.



    Der DataReader ist ein vom Anbieter bereitgestelltes DLL-Shared-Object, welches eben für die SQL-Abfrage erschaffen wurde. Ich habe keinen Einfluss darauf, aber es stellt eben eine entsprechende Abfrage-Sende-Funktion zur Verfügung (ExecuteReader), die mir ein Objekt zurückgibt, welches IDataReader implementiert, womit ich dann eben die SQL-Abfrage-Resultate erhalte. Wenn ich die Anfragen tausche, ändert sich nur die Resultatreihenfolge. Die Abfrage ändert also nicht die Einzelresultate. Eine tDS-Abfrage kann ich nicht stellen, weil ich keinen direkten Zugriff auf die DB habe. Ich habe nur indirekt über die DLL Zugriff auf die DB.

    Allerdings bietet das Reader-Objekt eine Methode, um das Tabellenschema zu bekommen (es gibt eine DataTable wieder). Die Infos sind also alle da. Datentypen sind jeweils String. Das wusste ich aber auch schon vorher. Alles auszulesen kann ich machen: 376000 Resultate.
    Aber jetzt wird es noch verrückter. Ich dachte, dass es ggf. an den Wertetupels liegt, daher habe ich eine einfache Datenklasse erstellt und die Resultlist als List(Of Datenklasse):

    VB.NET-Quellcode

    1. Dim Result1 = ResultList.Where(Function(x) x.ArtNr1 = "4084500601888") 'ergibt 1 Ergebnis
    2. Dim Result2 = ResultList.Where(Function(x) x.ArtNr2 = "11035088") 'ergibt 1 Ergebnis
    3. Dim Result3 = ResultList.Where(Function(x) x.ArtNr2 = "12894020") 'ergibt KEIN Ergebnis

    aber wenn ich jetzt mit dem hier abfrage: SQL = $"SELECT * FROM Tabelle WHERE ArtNr2='12894020'", dann bekomm ich wieder ein Ergebnis ?(

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „VaporiZed“ ()

    Hallo VaporiZed

    Ich denke deine Abfragen sind ja perfekt.

    376'000 Datensätze sind schon eine Menge. Trotztem: Könntest Du nicht mal alle in eine Datatable laden und mit einem Dataview filtern, oder auch Linq. Wie lange dauert den sowas? Und funktioniert da vb.net noch?

    Bei einer Access Datenbank würde ich noch die Datenbank komprimieren und reparieren, vielleicht gibt es ja auch bei dieser Datenbank ein Wartungsprogramm. Das Feld könnte ja indexiert sein, vielleicht könnte man dieser Index neu aufbauen.

    Lg

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Panter“ ()

    @Amro: Ja, das ist gewollt. Es gibt in dieser Tabelle immer Paare. Langnummer (ArtNr1) und Kurznummer (ArtNr2). Die Kurznummern treten jeweils nur einmal auf, eine Langnummer kann aber bei mehreren Kurznummern auftauchen. Ich will mit meiner Abfrage eben alle Kurznummern finden, die eine gemeinsame Langnummer haben.
    @Panter: Am Zielcomputer dauert die Abfrage inkl. Laden ins / Anzeige im DGV ca. 2 Sekunden. Vertretbar. An der DB kann und werd ich nix machen. Wie geschrieben: Fremdanbieter. Wenn ich da was manipuliere, kann's für mich teuer werden.

    Ich lade alle Datensätze direkt in mein DGV, DataTable kann ich auch noch probieren.
    Ich habe mein Testprogramm etwas erweitert und eine SuFu eingebaut. Diese funktioniert. Wenn ich mir erstmal nur z.B. 100 Einträge anzeigen lasse und dann nach einer definitiv im DGV vorhandenen ArtNr1 oder ArtNr2 suchen lasse, wird die Zeile markiert, egal, ob sie über über oder unter der aktuell markierten Zeile ist. Wenn ich eine Nummer suche, die nicht im DGV ist, wird eine SQL-Abfrage gestartet und so nochmal gesucht. Das funktioniert bei definitiv vorhandenen und bei definitiv nicht vorhandenen Artikelnummern.

    VB.NET-Quellcode

    1. Private CurrentRowIndex As Integer = 0
    2. '…
    3. Private Sub BtnStartSearch_Click(sender As Object, e As EventArgs) Handles BtnStartSearch.Click
    4. Dim ActualItemIdToFind As String
    5. Dim TargetIndex As Integer
    6. If TxtArtNr1ToFind.Text.IsNotEmpty Then
    7. ActualItemIdToFind = TxtArtNr1ToFind.Text
    8. TargetIndex = 0
    9. Else
    10. ActualItemIdToFind = TxtArtNr2ToFind.Text
    11. TargetIndex = 1
    12. End If
    13. Dim OldStartRowIndex = CurrentRowIndex
    14. Do
    15. CurrentRowIndex = (CurrentRowIndex + 1) Mod DataGridView1.Rows.Count
    16. If DataGridView1.Item(TargetIndex, CurrentRowIndex).Value.ToString = ActualItemIdToFind Then DataGridView1.Rows(CurrentRowIndex).Selected = True : DataGridView1.FirstDisplayedScrollingRowIndex = CurrentRowIndex : Return
    17. Loop Until CurrentRowIndex = OldStartRowIndex
    18. MessageBox.Show($"{ActualItemIdToFind} konnte nicht gefunden werden. Starte SQL-Abfrage.")
    19. Dim WhereClause As String
    20. If TargetIndex = 0 Then
    21. WhereClause = $"ArtNr1='{ActualItemIdToFind}'"
    22. Else
    23. WhereClause = $"ArtNr2='{ActualItemIdToFind}'"
    24. End If
    25. Dim SQL = $"SELECT * FROM Tabelle WHERE {WhereClause}"
    26. Using Reader = MyDataReader.ExecuteReader(SQL)
    27. While Reader.Read()
    28. MessageBox.Show($"{Reader.GetString(0)} und {Reader.GetString(1)} sind doch da?!?")
    29. Return
    30. End While
    31. End Using
    32. MessageBox.Show($"{ActualItemIdToFind} gibt es wirklich nicht.")
    33. End Sub


    Das Ergebnis ist für mich nicht nachvollziehbar, siehe Anhang.

    ##########

    Ist es möglich, dass der Anbieter den DataReader so eingestellt hat, dass er nur das erste Ergebnis zurückgibt, unabhängig davon, was der SQL-Abfragetext sagt? Denn ich habe jetzt nochmal das so durchlaufen lassen und es bleibt beim Ergebnis:

    VB.NET-Quellcode

    1. Dim SQL As String
    2. Dim Foo As New List(Of String)
    3. SQL = $"SELECT ArtNr1,ArtNr2 FROM Tabelle WHERE ArtNr2='11035088' OR ArtNr2='12894020'"
    4. Using Reader = MyDataReader.ExecuteReader(SQL)
    5. While Reader.Read()
    6. Foo.Add(Reader.GetString(0) & " " & Reader.GetString(1)) 'ergibt die zwei erwarteten Ergebnisse
    7. End While
    8. End Using
    9. SQL = $"SELECT ArtNr1,ArtNr2 FROM Tabelle"
    10. Using Reader = MyDataReader.ExecuteReader(SQL)
    11. While Reader.Read()
    12. If Reader.GetString(0) = "4084500601888" Then Foo.Add(Reader.GetString(0) & " " & Reader.GetString(1)) 'ergibt nur ein Ergebnis: 4084500601888 + 12894020
    13. End While
    14. End Using

    Bilder
    • Result1.png

      1,67 kB, 364×133, 78 mal angesehen
    • Result2.png

      1,37 kB, 280×133, 587 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“ ()

    Die DB nennt sich Caché von Intersystems. Den Typ versuch ich zu ermitteln …
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Ist es möglich, dass der Anbieter den DataReader so eingestellt hat, dass er nur das erste Ergebnis zurückgibt, unabhängig davon, was der SQL-Abfragetext sagt?

    Daran habe ich auch gedacht. Gibt es Dokumentation zu der dll? Kannst du uns da etwas zur Verfügung stellen? Evtl. schadet auch ein Blick per ILSpy in die dll nicht.
    Weder gibt es mir zugängliche Doku noch darf ich die DLL hochladen - da sind tatsächlich Zugangsdaten/Passwörter drin - OMG! Per IlSpy kann ich nachschauen, weiß nur auf Anhieb nicht, wonach ich suchen muss.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    da sind tatsächlich Zugangsdaten/Passwörter drin - OMG!
    :thumbsup:
    Dann wirds natürlich schwer, weiter zu unterstützen. Ich würde halt bei der Funktion ExecuteReader("") einsteigen und mich von da durchhangeln. Mal sehen, ob du fündig wirst, was das Verhalten erklären würde.
    Ich kenne Caché nicht, könnte ich mir aber durchaus vorstellen, dass das Ergebnis über eine Art Tabellentrigger über dieses Feld in der DB direkt reduziert/begrenzt wird.

    VaporiZed schrieb:

    Weder gibt es mir zugängliche Doku

    Hier würde ich mal bei dem Hersteller/Kunden anfragen. Man kann ja nicht erwarten, dass man blind irgendwas entwickelt. "Ist ja nur eine Abfrage"...

    ISliceUrPanties schrieb:

    Hier würde ich mal bei dem Hersteller/Kunden anfragen. Man kann ja nicht erwarten, dass man blind irgendwas entwickelt.
    Naja. Ich bin der Kunde. Der Anbieter unseres Betriebs-Warenwirtschaftssystem nutzt die Caché-DB, um eben seine Programme zu betreiben. Ich bin offiziell nur der Nutzer der WaWi-Software. Dass ich da direkt die DB auslese, ist von unserem WaWi-Anbieter nicht vorgesehen. Er nutzt sie ja für seine Programme. Dass ich da jetzt komme und die DB richtig auslesen will, das wird unser WaWi-Anbieter nicht supporten. Aber die von ihm angebotenen Programme sind an einigen Stellen ziemlich … ausbaufähig. Daher bastel ich seit Jahren Ergänzungsprogramme. Bisher musste ich immer den Umweg über das WaWi-GUI nehmen, später fand ich die Möglichkeit, Funktionen aus den DLLs der WaWi-Software zu nutzen. Nun bin ich eben bei den DB-Abfragen als vielleicht ultimative Datenquelle angekommen.
    Klar, ich kann Anfragen bei unserem Anbieter für Verbesserungen stellen. Aber da wir nicht die einzigen Konsumenten der WaWi-Software sind, sind meine/unsere Belange nur eine von hunderten. Und werden daher ziemlich weit hinten abgestellt. Ein Wechsel zu einem anderen WaWi-Anbieter ist aber ausgeschlossen. 1. Kosten, 2. Mitarbeiterumstellung, 3. ich nix Chef, 4. kommen wir dann nur vom Degen in die Schlaufe und 5.: meine Entwicklungen von über 10 Jahren wären für den Ar…meepapierkorb.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hallo VaporiZed

    Ich verstehe noch was nicht:

    Was meinst du mit
    Ich lade alle Datensätze direkt in mein DGV


    also so?

    SQL-Abfrage

    1. SELECT ArtNr1,ArtNr2 FROM Tabelle
    also ohne irgendwelche Bedingungen.



    Ich habe es so verstanden:
    • Where ArtNr1 ... - hier werden alle Datensätze übergeben
    • Where ArtNr2 ... - hier werden zuwenig Datensätze übergeben
    Daher meine Idee, nichts zu filtern.

    und dann eben mit dem Datagridview

    VB.NET-Quellcode

    1. .Filter = "ArtNr2 = '111222333"


    LG Panter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Panter“ ()

    Ja, ich lade ohne WHERE alles ins DGV. Aber es taucht nur eines von zwei erwarteten Ergebnissen auf. Auch dieses hier führt zu unterschiedlichen Ergebnissen:

    SQL-Abfrage

    1. SQL = $"SELECT * FROM Tabelle WHERE ArtNr2='11035088' OR ArtNr2='12894020'"
    - zwei Ergebnisse

    SQL-Abfrage

    1. SQL = $"SELECT * FROM Tabelle WHERE SUBSTRING(ArtNr2,1,8)='11035088' OR SUBSTRING(ArtNr2,1,8)='12894020'"
    - nur ein Ergebnis

    WTF?!?

    Where ArtNr1 = filtern nach Langnummer -> nur eines von zwei erwarteten Ergebnissen kommt
    Where ArtNr2 = filtern nach Kurznummer -> beide erwarteten Ergebnissen kommen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    versuch mal statt OR ein !
    docs.intersystems.com/latest/c…ngelements_ops_relational


    hier gibt es auch eine Community.
    Den Tabellenname vielleicht auch angeben (Tabellennamen.ArtNr1)

    schaumal in den Link die Comments


    Comments
    Caché SQL supports both single-line comments and
    multi-line comments. Comment text can contain any characters or strings,
    except, of course, the character(s) that indicate the end of the
    comment.

    Note:

    Using Embedded SQL marker syntax (&sql<marker>(...)<reversemarker>)
    imposes a restriction on the contents of SQL comments. If you are using
    marker syntax, the comments within the SQL code may not contain the
    character sequence “)<reversemarker>”. For further details, refer to The &sql Directive in the “Using Embedded SQL” chapter of this manual

    Nicht das es an den Ausführungzeichen im string liegt

    Having ist auch interessant
    https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_having

    ----------------------------------------------------------------

    If you use a simple statement for a query that can return multiple rows, then only the first row is returned:

    &sql(SELECT Name INTO :name
    FROM Patient
    WHERE Age = 43)

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Amro“ ()