Maximalwert aus mehreren Tabellen

7

Stellen Sie sich folgende Situation vor: Wir haben T1-, T2- und T3-Tabellen, in denen gespeicherte idund priceProdukte gespeichert sind. Jetzt müssen wir die idProdukte finden, die maximal pricealle 3 Tabellen haben. Ich habe diese Lösung:

select id
from T1
where price in(
   select max(price)
   from( 
      select max(price) as price
      from T1

      union

      select max(price) as price
      from T2

      union

      select max(price) as price
      from T3
   ) temp
)   

union 

select id
from T2
where price in(
   select max(price)
   from( 
      select max(price) as price
      from T1

      union

      select max(price) as price
      from T2

      union

      select max(price) as price
      from T3
   ) temp
)   

union

....    same for T3 table.

Ist es möglich, diese Abfrage zu optimieren?


quelle
Die Frage ist mehrdeutig - ungefähr ist sie zwischen den beiden Interpretationen in den beiden Antworten A und B nicht eindeutig . Suchen Sie nach der einzelnen ID des Artikels mit dem globalen Höchstpreis oder nach den (bis zu) drei IDs der Artikel, die gemäß jeder der drei Tabellen am teuersten sind?
Jonathan Leffler
Dies erscheint mir als logisches Problem mit Ihrem DBMS-Schema. Sie möchten wahrscheinlich entweder alle 3 Tabellen zu einer zusammenführen oder (wenn Sie viel Code haben, der von der aktuellen Anordnung abhängt) eine Ansicht erstellen, die alle 3 Tabellen zu einer zusammenführt.
Diese Frage ist reine SQL-Programmierung und gehört zu StackOverflow, wo sie ursprünglich gestellt wurde. Durch die Migration auf DBA wird lediglich die Anzahl der Personen reduziert, die jemals davon profitieren werden.
Jonathan Leffler
@ JonathanLeffler: stimme in diesem Fall zu, aber dba.se ist nicht nur für das Sichern / Wiederherstellen von Affen gedacht. Siehe meta.dba.stackexchange.com/questions/564/… und meta.dba.stackexchange.com/questions/495/…
gbn
3
@ JonathanLeffler: "DBA" ist für DB-Administrator, BI, Advanced SQL, Datenbankdesign usw. meta.dba.stackexchange.com/questions/503/… Hier erhalten Sie mehr Augäpfel von Datenbanktypen als auf SO. Siehe auch meta.dba.stackexchange.com/questions/535/…
gbn

Antworten:

3

Ich gehe davon aus, dass Sie meinen, Sie möchten den idArtikel, der aufgrund der Daten aus allen drei Tabellen am teuersten ist. Für jede Tabelle benötigen Sie den idund den Preis der Artikel mit dem Höchstpreis in dieser Tabelle. Für eine Tabelle ist das:

SELECT id, price FROM Tn WHERE price = (SELECT MAX(price) FROM Tn)

Es gibt also eine Unterabfrage:

SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
UNION
SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
UNION
SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3)

Und Sie müssen einen der idWerte mit dem Maximalpreis aus dieser Unterabfrage finden:

SELECT id
  FROM (SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
        UNION
        SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
        UNION
        SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3)
       ) AS M
 ORDER BY price DESC
 LIMIT 1

Beachten Sie, dass die UNION-Abfrage mehr als 3 Zeilen zurückgibt, wenn in einer der Tabellen zwei gleich teure Elemente aufgeführt sind. Wenn zwei oder mehr gleich teure Elemente vorhanden sind, wählt die Abfrage mit dem LIMIT eines aus, und Sie können nicht vorhersagen, welches. Wenn Sie alle idWerte von gleich teuren Gegenständen sehen möchten, die am meisten kosten, müssen Sie ein ähnliches Spiel mit verschachtelten Abfragen spielen. Das macht mich wahnsinnig, sollte aber funktionieren:

SELECT id, price
  FROM (SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
        UNION
        SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
        UNION
        SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3)
       ) AS M
 WHERE price =
       (SELECT MAX(Price)
          FROM (SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
                UNION
                SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
                UNION
                SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3)
               ) AS M2
       );

Dies wäre mit einer WITH-Klausel einfacher:

WITH MaxPrices AS
     (SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
      UNION
      SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
      UNION
      SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3)
     )
SELECT id, price
  FROM M
 WHERE price = (SELECT MAX(Price) FROM M);

Nicht jedes DBMS unterstützt die WITH-Klausel wie diese, und ich glaube, MySQL ist eines der DBMS in der Kategorie, die diese Unterstützung nicht enthält.

Jonathan Leffler
quelle
Aber wenn es mehr als ein Produkt gibt, das den Höchstpreis hat
@Ashot: Ich habe das mit meinem Update behoben, das geschrieben wurde, als Sie Ihre Zusatzfrage gestellt haben. Wenn Sie mit einer komplexen Abfrage arbeiten, bauen Sie sie Stück für Stück auf, wie ich gezeigt habe (und wie ich in vielen anderen Antworten auf SQL-Fragen gezeigt habe). Wenn Sie versuchen, alles auf einmal zu tun, ist der Verstand nur verwirrt und die Abfrage ist kaputt. Dann müssen Sie sie zerreißen und die Komponenten testen, um zu sehen, wo es schief geht. Es wäre also einfacher gewesen, sie aufzubauen getestete Komponenten als zu tun, rückgängig zu machen und zu wiederholen.
Jonathan Leffler
1
create temporary table allthree as
select * from t1
union
select * from t2
union
select * from t3;

select id, max(price) from allthree;

Wenn Sie im Falle eines Unentschieden alle IDs erhalten möchten, die den gleichen Preis haben (oder aus anderen Gründen strenger sein müssen), verwenden Sie Folgendes

select id from allthree where price=(select max(price) from allthree);

Unter bestimmten Umständen kann es hilfreich sein, pricenach dem Erstellen der temporären Tabelle einen Index für die Spalte zu erstellen .

Ken Bloom
quelle
+1 für die temporäre Tabelle, wahrscheinlich nicht wert, alle Spalten neu zu erstellen:create temporary table allthree as SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1) ...
@ Bruno: gute Idee. Poste das als Antwort und ich werde dich positiv bewerten. (Sobald Sie so weit gegangen sind, eine temporäre Tabelle mit so wenig Daten zu erstellen, ist es wahrscheinlich besser, die Abfrage auf der Clientseite abzuschließen.)
@ KenBloom Hat meine Antwort gepostet.
1

Optimierte Version:

create temporary table allthree as
SELECT id, price FROM T1 WHERE price = (SELECT MAX(price) FROM T1)
UNION ALL
SELECT id, price FROM T2 WHERE price = (SELECT MAX(price) FROM T2)
UNION ALL
SELECT id, price FROM T3 WHERE price = (SELECT MAX(price) FROM T3);

select id from allthree where price=(select max(price) from allthree);
Bruno Silva
quelle
0

Ich bin mir nicht sicher, ob ich Ihre Frage verstanden habe, aber sollte das nicht genug sein:

select id, max(price) as price
from T1
union all
select id, max(price) as price
from T2
union all
select id, max(price) as price
from T3

Dadurch werden die Maximalwerte für jede Tabelle ermittelt.

Bruno Silva
quelle
Die ID des Produkts mit dem Höchstpreis finden Sie in seiner Tabelle. Ich brauche IDs von Produkten, deren Preis von allen 3 Tabellen maximal ist
2
Genau genommen select by id, max(price)ist dies in SQL nicht zulässig, MySQL erlaubt es jedoch als Erweiterung - es wählt die ID aus, die für Sie am besten geeignet ist.
0

Ich hoffe, dass meine Syntax funktioniert, da ich normalerweise keine MySql-Person bin.

SELECT *
FROM (
   SELECT 'T1' AS TableName, * FROM Table1
   UNION ALL SELECT 'T2', * FROM Table2
   UNION ALL SELECT 'T3', * FROM Table3
) AS X
WHERE
   price = (
      SELECT Max(price)
      FROM (
         SELECT price FROM Table1
         UNION ALL SELECT price FROM Table2
         UNION ALL SELECT price FROM Table3
      ) AS X
   )

Wenn MySQL über Fensterfunktionen oder CTEs oder CROSS APPLY oder andere Techniken verfügt, die mir nicht bekannt sind, um ganze Zeilen auszuwählen, die eine aggregierte Bedingung erfüllen, kann meine Abfrage verbessert oder vereinfacht werden.

ErikE
quelle
0

CTE ist zur Rettung!

Diese Abfrage ruft auch eine Angabe der Tabelle ab, in der das Maximum gefunden wurde.

WITH three AS (
        SELECT 1::INTEGER AS num, id, zdate FROM table1
        UNION ALL
        SELECT 2::INTEGER AS num, id, zdate FROM table2
        UNION ALL
        SELECT 3::INTEGER AS num, id, zdate FROM table3
        )
SELECT * FROM three tt
WHERE NOT EXISTS (
        SELECT * FROM three nx
        WHERE nx.zdate > tt.zdate
        );

Die schlechte Nachricht ist: Der Abfrageoptimierer von Postgres "sieht" die Indizes (auf zdate) in den Gewerkschaften nicht und zieht sie nicht in die Hauptabfrage hoch. Der Abfrageplan besteht aus verschachtelten sequentiellen Scans.

 Nested Loop Anti Join  (cost=297.82..315505.01 rows=3055 width=16) (actual time=633.593..633.594 rows=1 loops=1)
   Join Filter: (nx.zdate > tt.zdate)
   CTE three
     ->  Result  (cost=0.00..297.82 rows=4582 width=16) (actual time=0.013..2.623 rows=4582 loops=1)
           ->  Append  (cost=0.00..297.82 rows=4582 width=16) (actual time=0.011..2.143 rows=4582 loops=1)
                 ->  Seq Scan on table1  (cost=0.00..99.19 rows=1519 width=16) (actual time=0.011..0.884 rows=1519 loops=1)
                 ->  Seq Scan on table2  (cost=0.00..99.12 rows=1512 width=16) (actual time=0.006..0.449 rows=1512 loops=1)
                 ->  Seq Scan on table3  (cost=0.00..99.51 rows=1551 width=16) (actual time=0.003..0.450 rows=1551 loops=1)
   ->  CTE Scan on three tt  (cost=0.00..91.64 rows=4582 width=16) (actual time=0.021..0.630 rows=4582 loops=1)
   ->  CTE Scan on three nx  (cost=0.00..91.64 rows=4582 width=8) (actual time=0.000..0.069 rows=757 loops=4582)
 Total runtime: 633.820 ms
Wildplasser
quelle