4 Stimmen

Sie müssen einen Ausdruck in einer äußeren Verknüpfung erstellen, der nur eine Zeile zurückgibt

Ich erstelle eine wirklich komplexe dynamische Sql, es muss eine Zeile pro Benutzer zurückgeben, aber jetzt muss ich gegen eine eins zu viele Tabelle verbinden. Ich mache eine äußere Verknüpfung, um sicherzustellen, dass ich mindestens eine Zeile zurückbekomme (und kann auf Null prüfen, um zu sehen, ob es Daten in dieser Tabelle gibt), aber ich muss sicherstellen, dass ich nur eine Zeile von diesem äußeren Verknüpfungsteil zurückbekomme, wenn es mehrere Zeilen in dieser zweiten Tabelle für diesen Benutzer gibt. Bis jetzt habe ich folgendes gefunden: (sybase)

SELECT a.user_id
FROM table1 a
    ,table2 b
WHERE a.user_id = b.user_id
AND a.sub_id = (
    SELECT min(c.sub_id)
    FROM table2 c
    WHERE b.sub_id = c.sub_id
    )

Die Unterabfrage findet den Mindestwert in der One-to-Many-Tabelle für diesen bestimmten Benutzer.

Dies funktioniert, aber ich fürchte, dass es unangenehm ist, korrelierte Unterabfragen zu machen, wenn Tabelle 1 und 2 sehr groß werden. Gibt es einen besseren Weg? Ich versuche, mir einen Weg auszudenken, um Joins dazu zu bringen, es zu tun, aber ich sehe es nicht. Auch "where rowcount=1" oder "top 1" hilft mir nicht weiter, denn ich versuche nicht, die obige Abfrage zu reparieren, sondern ich füge sie zu einer bereits komplexen Abfrage hinzu.

1voto

Dónal Punkte 180956

In MySql können Sie sicherstellen, dass jede Abfrage höchstens X Zeilen zurückgibt, indem Sie

select *
from foo
where bar = 1
limit X;

Leider bin ich mir ziemlich sicher, dass dies eine MySQL-spezifische Erweiterung von SQL ist. Eine Google-Suche nach etwas wie "mysql sybase limit" könnte jedoch ein Äquivalent für Sybase ergeben.

1voto

Tom H Punkte 45699

Ein paar kurze Hinweise:

  1. Sie brauchen eindeutige Geschäftsregeln. Wenn die Abfrage mehr als eine Zeile zurückgibt, müssen Sie darüber nachdenken, warum (nicht nur "es ist eine 1:Viele-Beziehung - WARUM ist es eine 1:Viele-Beziehung?) Sie sollten sich eine betriebswirtschaftliche Lösung einfallen lassen und nicht einfach "min" verwenden, weil Sie damit eine Zeile erhalten. Die betriebswirtschaftliche Lösung könnte einfach lauten: "Nimm die erste". In diesem Fall könnte "min" die Antwort sein, aber Sie müssen sicherstellen, dass dies eine bewusste Entscheidung ist.
  2. Sie sollten wirklich versuchen, die ANSI-Syntax für Joins zu verwenden. Nicht nur, weil es der Standard ist, sondern weil die Syntax, die Sie haben, nicht wirklich das tut, was Sie denken, dass sie tut (es ist keine äußere Verknüpfung) und einige Dinge sind einfach unmöglich, mit der Syntax zu tun, die Sie haben.

Angenommen, Sie verwenden am Ende die MIN-Lösung, hier ist eine mögliche Lösung ohne die Unterabfrage. Sie sollten sie mit verschiedenen anderen Lösungen testen, um sicherzustellen, dass sie im Ergebnis gleichwertig sind, und um zu sehen, welche die beste Leistung erbringt.

SELECT
     a.user_id, b.*
FROM
     dbo.Table_1 a
LEFT OUTER JOIN dbo.Table_2 b ON b.user_id = a.user_id AND b.sub_id = a.sub_id
LEFT OUTER JOIN dbo.Table_2 c ON c.user_id = a.user_id AND c.sub_id < b.sub_id
WHERE
     c.user_id IS NULL

Sie müssen dies testen, um zu sehen, ob es wirklich das gibt, was Sie wollen, und Sie müssen es möglicherweise optimieren, aber die Grundidee ist, den zweiten LEFT OUTER JOIN zu verwenden, um sicherzustellen, dass es keine Zeilen gibt, die eine niedrigere sub_id haben als die im ersten LEFT OUTER JOIN gefundene (falls eine gefunden wird). Sie können die Kriterien im zweiten LEFT OUTER JOIN je nach den endgültigen Geschäftsregeln anpassen.

0voto

Ady Punkte 4716

Vielleicht ist Ihr Beispiel zu vereinfacht, aber ich würde eine Gruppierung nach verwenden:

SELECT
  a.user\_id 
FROM 
  table1 a
    LEFT OUTER JOIN table2 b ON (a.user\_id = b.user\_id)
GROUP BY
  a.user\_id

Ich fürchte, die einzige andere Möglichkeit wäre die Verwendung verschachtelter Abfragen:

Der Unterschied zwischen dieser Abfrage und Ihrem Beispiel besteht darin, dass eine "Untertabelle" nur einmal generiert wird, während Sie in Ihrem Beispiel für jede Zeile in Tabelle1 eine "Untertabelle" generieren (dies kann jedoch vom Compiler abhängen, so dass Sie vielleicht den Abfrageanalysator verwenden möchten, um die Leistung zu überprüfen).

SELECT
  a.user\_id,
  b.sub\_id
FROM 
  table1 a
    LEFT OUTER JOIN (
      SELECT
        user\_id,
        min(sub\_id) as sub\_id,
      FROM
        table2
      GROUP BY
        user\_id
    ) b ON (a.user\_id = b.user\_id)

Wenn Ihre Abfrage recht komplex wird, würde ich außerdem temporäre Tabellen verwenden, um den Code zu vereinfachen. Das kostet zwar etwas mehr Verarbeitungszeit, macht Ihre Abfragen aber viel einfacher zu pflegen.

Ein Beispiel für eine Temp-Tabelle wäre:

SELECT
  user\_id
INTO
  #table1
FROM 
  table1
WHERE
  .....

SELECT
  a.user\_id,
  min(b.sub\_id) as sub\_id,
INTO
  #table2
FROM
  #table1 a
    INNER JOIN table2 b ON (a.user\_id = b.user\_id)
GROUP BY
  a.user\_id

SELECT
  a.\*,
  b.sub\_id
from
  #table1 a
    LEFT OUTER JOIN #table2 b ON (a.user\_id = b.user\_id)

0voto

Tony Andrews Punkte 125904

Wie wäre es damit:

select a.user_id 
from table1 a
where exists (select null from table2 b 
              where a.user_id = b.user_id 
             )

0voto

James Curran Punkte 98228

Zunächst einmal glaube ich, dass die Abfrage, die Sie in Ihrem Beispiel zu schreiben versuchen, lautet:

select a.user_id 
from table1 a, table2 b 
where a.user_id = b.user_id 
and b.sub_id = (select min(c.sub_id) 
                from table2 c 
                where b.user_id = c.user_id)

Es sei denn, Sie wollten eine äußere Verknüpfung (ich glaube, jemand hat die Oracle-Syntax überarbeitet).

select a.user_id 
from table1 a
left outer join table2 b on a.user_id = b.user_id 
where b.sub_id = (select min(c.sub_id) 
                from table2 c 
                where b.user_id = c.user_id)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X