SQL: Duplikate vermeiden

Diskutiere das Thema SQL: Duplikate vermeiden im Forum Web-Programmierung.

  1. Saugkraft

    Saugkraft Thread Starter Moderator

    Beiträge:
    9.333
    Medien:
    1
    Zustimmungen:
    3.099
    Mitglied seit:
    20.02.2005
    Hallo SQL Experten,

    ich sitze grad an einem relativ kniffligen Problem und ich weiß nicht, ob es dazu eine reine SQL Lösung gibt. Ich habe eine Abfrage bei der datensatz_id, status1, status2 und datum abgefragt werden sollen.

    datensatz_id und status1 kommen aus Tabelle 1.
    datensatz_id ist Fremdschlüssel in Tabelle 2. Aus Tabelle 2 lese ich status2 und datum aus.

    status1 kann true sein, status2 kann true sein oder alle beide.

    In Tabelle 2 sind jeder datensatz_id aus Tabelle 1 mehrere Datumswerte zugeordnet.

    Ich will abfragen, an welchen Tagen (Datum) status1, status2 oder alle beide true sind. Dabei soll nach Datumswerten sortiert werden.

    Soweit so gut. Problem: Ich will jede datensatz_id nur einmal aufgeführt bekommen.

    Mit
    Code:
    SELECT tabelle1.datensatz_id, tabelle1.status1, tabelle2.status2, tabelle2.datum INNER JOIN tabelle1 ON tabelle1.datensatz_id = tabelle2.datensatz_id WHERE tabelle1.status1 = true OR tabelle2.status2 = true GROUP BY tabelle1.datensatz_id, tabelle1.status1, tabelle2.status2, tabelle2.datum ORDER BY MIN(tabelle2.datum)
    spuckt er mir die Datensätze aus, führt aber ggf. die datensatz_id doppelt auf. Nämlich dort wo ein status true ist und mehrere Datumseinträge zu einer datensatz_id existieren.

    Zum Beispiel so:
    Code:
    datensatz_id status1 status2 datum
           2           true     false   1.1.2008
           2           true     false   2.1.2008
           3           true     true    5.1.2008
           3           true     true    7.1.2008
           2           false    true    8.1.2008
           1           true     true    9.1.2008
    
    Was ich brauche ist das jeweils erste Datum bei dem eine datensatz_id auftaucht. Bei 2 z.B. der 1.1.2008. Danach ist die datensatz_id "verbraucht" und darf nicht wieder auftauchen. Das Ergebnis sollte also so aussehen:
    Code:
    datensatz_id status1 status2 datum
           2           true     false   1.1.2008
           3           true     true    5.1.2008
           1           true     true    9.1.2008
    
    Warum das so sein muss? Da hängen natürlich noch ein paar andere Daten aus anderen Tabellen dran. Wenn ich daraus eine Webseite generiere, kommen die Daten geclustert zum Einsatz und zu jeder id werden die Datumswerte nochmal gesondert abgefragt (plus ein paar Zusatzinfos):

    Beginn: 1.1.2008
    id: 2
    Termine: 1.1.2008, 2.1.2008, 8.1.2008

    Entscheidend für die Sortierung ist aber der erste Termin. Wenn die id mehrfach (irgendwo weiter hinten in der Liste nochmal) auftaucht, haut er mir dazu natürlich nochmal die 4 Termine raus. Das will ich natürlich nicht.

    Ich kann an der Datenbank Struktur nichts ändern. Dazu hängt zuviel hinten dran. Die Frage ist also: Gibt es dafür eine reine SQL Lösung oder muss ich in ASP, PHP, etc. was programmieren um bereits abgearbeitete ids zu überspringen?

    Ich hoffe, ihr könnt mir helfen.
     
  2. somunium

    somunium Mitglied

    Beiträge:
    523
    Zustimmungen:
    35
    Mitglied seit:
    16.01.2008
    Hm... Das einzige, was mir jetzt einfällt, währe das über mehrere einzelne Querys zu machen.

    Für jede datensatz_id einen eigenen, der sich dann mit LIMIT und ORDER jeweils den ersten Satz holt.

    Wird aber bei vielen Sätzen recht unperfomant...

    Ansonsten, schau Dir mal den GROUP-Befehl an. Den hab ich jetzt nicht so gut drauf, aber mein Instinkt sagt mir, dass, wenn es möglich ist, es damit möglich ist :D

    (Männlicher Instinkt, keine Weibliche Intuition :D)
     
  3. timbajr

    timbajr Mitglied

    Beiträge:
    582
    Zustimmungen:
    26
    Mitglied seit:
    22.04.2005
    Mit GROUP BY und FIRST. Schau dir mal group by und die sog. Aggregatfunktionen an.
     
  4. frikih

    frikih Mitglied

    Beiträge:
    24
    Zustimmungen:
    3
    Mitglied seit:
    10.02.2007
    das sollte per SQL ohne Probleme möglich sein, poste doch mal bitte die genau Struktur der Tabellen mit Primärschlüsseln etc... das macht es einfacher.
     
  5. Internetlady

    Internetlady Mitglied

    Beiträge:
    2.035
    Zustimmungen:
    7
    Mitglied seit:
    06.03.2005
    Hast du es schon mal mit DISTINCT und einem LEFT JOIN Probiert? *

    SELECT DISTINCT tabelle1.datensatz_id…
    FROM (tabelle1 LEFT JOIN tabelle2
    ON tabelle1.datensatz_id = tabelle2.datensatz_id)
    WHERE…. (Datumsanfrage muss hier rein u.a.)



    *ggf. nur DB2 Syntax ...
    aber müsste es für alle Datenbanken ja analoges zu geben
     
  6. wegus

    wegus Mitglied

    Beiträge:
    16.283
    Zustimmungen:
    2.902
    Mitglied seit:
    13.09.2004
    auf die Schnelle fiele mir ein:

    GROUP BY datensatz_id

    und

    ORDER BY tabell2.datum

    das sollte ggf helfen die Zeilen auf eine ( die älteste) zu reduzieren. Da Du ja im Prinzip nur die Datensätze ausschließt bei denen status1 und status2 false sind, bekommst Du auch mit INNER JOINS keine besseren Daten.

    Ggf. kann man die Zeilen auch erstmal alle so in eine temporäre Tabelle schreiben und dort mit einem 2ten Statement nur die jeweils ältesten Einträge je ID herauslesen. Sind zwei Schritte aber ggf. etwas übersichtlicher!
     
  7. wegus

    wegus Mitglied

    Beiträge:
    16.283
    Zustimmungen:
    2.902
    Mitglied seit:
    13.09.2004
    an DISTINCT hab ich auch gedacht, bezieht sich denn DISTINCT nur auf die eine Spalte oder ist DISTINCT je Resultset/Tupel gemeint? Im letzteren Fall dürfte das ja kaum helfen.
     
  8. dms

    dms

    Sowohl, als auch, wenn ich mich nicht irre.

    SELECT DISTINCT spalte FROM tabelle

    vs.

    SELECT DISTINCT(spalte) FROM tabelle
     
  9. Saugkraft

    Saugkraft Thread Starter Moderator

    Beiträge:
    9.333
    Medien:
    1
    Zustimmungen:
    3.099
    Mitglied seit:
    20.02.2005
    FIRST wird vom MS SQL Server 2000 nicht unterstützt. :hum: Wird Zeit, dass ich mal auf 2008 update.

    DISTINCT datensatz_id klappt auch nicht. Weder mit LEFT JOIN noch ohne. Ich bin mir nicht sicher, aber ich vermute, dass sich DISCTINCT auf das Resultset bezieht.

    Das Problem ist ja, dass ich selbst wenn ich die datensatz_id gruppiere und per MIN DISTINCT die Datumswerte eingrenze, zwei oder mehr Daten erhalte, bei denen die Kriterien erfüllt sind. Dadurch unterscheiden sich die Ergebnissätze und für jedes unterschiedliche Datum wird mir die datensatz_id mit ausgespuckt.

    frikih: Die Struktur sieht so aus..
    Tabelle 1: datensatz_id (Primärschlüssel, ID), status1
    Tabelle 2: tbl2_id, datensatz_id (Fremdschlüssel von Tabelle 1), status2, datum

    Sind zwar noch mehr Felder drin aber die kommen nicht zum Einsatz und bilden auch keine Verknüpfung zwischen den Tabellen.

    Das mit der temporäten Tabelle hab ich mir auch schon überlegt, müsste allerdings erstmal rausfinden ob das mit dem SQL 2000 geht. Vielleicht kann ich ja auch eine Abfrage auf eine Abfrage legen.

    Alternativ böte sich noch ASP.NET an, da kann man Abfragen auf das Ergebnis anwenden weil die Daten nicht als Recordset sondern in einer Tabellenstruktur vorliegen. Der Nachteil ist, dass die Datensatznavigation da nicht per Zeiger funktioniert und für mein kleines Hirn offensichtlich zu kompliziert ist.
     
  10. wegus

    wegus Mitglied

    Beiträge:
    16.283
    Zustimmungen:
    2.902
    Mitglied seit:
    13.09.2004
    [QUOTE="Saugkraft]Das mit der temporäten Tabelle hab ich mir auch schon überlegt, müsste allerdings erstmal rausfinden ob das mit dem SQL 2000 geht. Vielleicht kann ich ja auch eine Abfrage auf eine Abfrage legen.[/QUOTE]

    das geht in jedem Fall! Nutze das selbst hier. Auf die Schnelle aus dem Kopf ohne Gewähr: temp. Tabellen beginnen mit einer # glaub ich!
    EDIT: stimmte :) #tabelle ist lokale temporäre Tabelle ( nur für Deinen einen Connect) ##tabelle ist eine globale temporäre Tabelle. Mehr gibt es im Query Analyzer unter Hilfe zu Transact SQL wenn Du nach temporär suchst!

    hast Du das mit GROUP BY probiert? Immerhin ist das das Mittel der Wahl da auf eine Zeile herunterzubrechen.
     
  11. just.do.it

    just.do.it Mitglied

    Beiträge:
    756
    Zustimmungen:
    72
    Mitglied seit:
    13.08.2007
    Hallo,

    also hier sollte dir definitv ein GROUP BY helfen. Etwa in der Art:

    SELECT
    datensatz_id,
    MIN(datum)
    FROM
    ... (Join)
    WHERE
    status1 = true or status2 = true
    GROUP BY
    datensatz_id

    Diese Abfrage liefert erstmal alle Datensätze in denen Status1 oder Status2 auf TRUE stehen.
    Der GROUP BY sorgt dann dafür, dass du jede "datensatz_id" nur einmal bekommst und über den MIN bekommst du das kleinste Datum (also das frühste).

    Gruß
    Dirk
     
  12. Saugkraft

    Saugkraft Thread Starter Moderator

    Beiträge:
    9.333
    Medien:
    1
    Zustimmungen:
    3.099
    Mitglied seit:
    20.02.2005
    Jep. GROUP BY hab ich probiert. Steht ja auch oben drin:

    SELECT tabelle1.datensatz_id, tabelle1.status1, tabelle2.status2, tabelle2.datum
    INNER JOIN tabelle1 ON tabelle1.datensatz_id = tabelle2.datensatz_id
    WHERE tabelle1.status1 = true OR tabelle2.status2 = true
    GROUP BY tabelle1.datensatz_id, tabelle1.status1, tabelle2.status2, tabelle2.datum
    ORDER BY MIN(tabelle2.datum)

    Dann schaue ich mal nach den temporären Tabellen.

    Wenn ich die Abfrage als Sicht abspeichere und darauf eine Abfrage mit MIN DISTINCT durchführe, klappts auch. Das finde ich aber wiederum nicht so elegant. Wer weiß, ob ich in einem Anfall von "Was ist denn UnterAbfrageSichtAufDingensBumens) für ne Abfrage, die braucht doch kein Mensch mehr" die Abfrage lösche.

    Ich experimentiere auch grad mit einer Unterabfrage im Stil:
    Code:
    SELECT DISTINCT tabelle1.datensatz_id, status1, status2, 
    min(datum) FROM tabelle1, tabelle2  
    WHERE EXISTS 
    (SELECT datum from tabelle2 WHERE tabelle1.datensatz_id = tabelle2.datensatz_id) 
    AND (status1 = true) OR (status2 = true) 
    GROUP BY tabelle1.datensatz_id, status1, status2 
    ORDER BY datum
    So richtig will das aber auch noch nicht.
     
  13. wegus

    wegus Mitglied

    Beiträge:
    16.283
    Zustimmungen:
    2.902
    Mitglied seit:
    13.09.2004
    soweit ich weiß werden Views auch automatisch indiziert, das könnte also doch eher ein Grund dafür sein! Ich nutze Views sehr gern. Wichtig ist nur das man im Kopf behält: Geänderte Tabellen erfordern aktualisieren der Views !
     
  14. just.do.it

    just.do.it Mitglied

    Beiträge:
    756
    Zustimmungen:
    72
    Mitglied seit:
    13.08.2007
    Du darfst hier aber weder nach status1, status2 oder insbesondere Datum gruppieren, denn dann bekomsmt du ja wieder für jedes Datum einen Datensatz. Du darfst ausschließlich nach datensatz_id gruppieren, damit du nur je eine Zeile pro ID bekommst. Über das MIN(datum) erhälst du dann das jüngste Datum.

    Gruß
    Dirk
     
  15. wegus

    wegus Mitglied

    Beiträge:
    16.283
    Zustimmungen:
    2.902
    Mitglied seit:
    13.09.2004
    Aua ja! Das hatte ich gar nicht gelesen. Vorher ist das Group by nat nutzlos gewesen.
     
  16. Saugkraft

    Saugkraft Thread Starter Moderator

    Beiträge:
    9.333
    Medien:
    1
    Zustimmungen:
    3.099
    Mitglied seit:
    20.02.2005
    :faint:

    Oh Mann. Wie blind kann ein einzelner Mensch sein? Na logo. Die Statusfelder hatte ich zur Kontrolle mit drin, um zu sehen welche Kombination vorliegt.

    Hab's gerade probiert, klappt natürlich. Jetzt muss ich es nur noch mal in der Anwendung testen um zu sehen, ob ich damit alle Infos habe die ich brauche, aber ich denke das sieht gut aus.

    Tausend Dank! Das hat mein Hirn mal wieder in Schwung gebracht. :)
     
Die Seite wird geladen...

MacUser.de weiterempfehlen

  1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Akzeptieren Weitere Informationen...