it-swarm-eu.dev

Ruft mehrere Spalten aus einer ausgewählten Unterabfrage ab

SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
   ( 
       SELECT ps.price 
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS special_price,
   ( 
       SELECT ps.date 
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)

Wie Sie sehen, wiederhole ich dieselbe Unterabfrage, um eine weitere Spalte herauszuholen. Ich frage mich, gibt es einen besseren Weg, dies zu tun?

id ist der Primärschlüssel in beiden Tabellen. Ich habe kein Problem damit, product_special.priority einzigartig zu machen, wenn das helfen kann.

26
Sparctus

Angenommene Kombination product_special.id, product_special.priority ist einzigartig

 SELECT p.*, special_price,special_date
 FROM product p
 LEFT JOIN 
 (
     SELECT ps.id, ps.price as special_price, ps.`date` as special_date
     FROM product_special ps
     INNER JOIN 
     (
       SELECT id, MIN(priority) as min_priority 
       FROM product_special
       GROUP BY id
     ) ps2 
     ON (ps2.id = ps.id)
 )a ON (a.id=p.id)
11
a1ex07

wenn Sie nicht beabsichtigen, die Felder als special_price.price und date.date zurückzugeben, warum nicht die Namen in der Unterabfrage aliasisieren? z.B.

SELECT p.*, p.name AS  name, p.image, p.price, ps.*
FROM product p
LEFT JOIN
   (SELECT
      psi.price as special_price, psi.date as my_date 
    FROM product_special psi
    WHERE 
      p.id = psi.id AND
      psi.date < NOW()
    ORDER BY psi.priority ASC, LIMIT 1
   ) AS ps ON
  p.id = ps.id

Hat Ihre Abfragesprache eine FIRST () - Aggregatfunktion? Ich bin mir nicht sicher, ob Sie die PK von product_special zu einer Mischung aus ID und Priorität (beide ASC-Sortierung) machen und die ORDER-Klausel in GROUP BY id, psi.priority Ändern können.

möglicherweise können Sie die ORDER BY-Klausel vollständig entfernen und HAVING MIN(psi.priority) verwenden

5
mpag

Beachten Sie, dass der "Cross Apply" -Mechanismus von SQL Server dies lösen würde, aber in PostgreSQL nicht verfügbar ist. Grundsätzlich war es ihre Lösung für die Übergabe von Parametern (die in der Regel Verweise auf Spalten außerhalb des aktuellen Tabellenausdrucks sind) an Funktionen, die in der FROM-Klausel als Tabellenausdrücke aufgerufen werden. Es hat sich jedoch als nützlich für alle Arten von Situationen erwiesen, in denen Sie vermeiden möchten, dass eine andere Ebene der Unterabfrage verschachtelt oder von der FROM-Klausel in die SELECT-Klausel verschoben wird. PostgreSQL hat dies durch eine Art Ausnahme ermöglicht - Sie können solche Parameter übergeben, wenn der Ausdruck ein einfacher Funktionsaufruf ist, aber streng genommen kein eingebettetes SELECT. Damit

left join highestPriorityProductSpecial(p.id) on true

ist ok, aber nicht

left join (select * from product_special ps where ps.id = p.id order by priority desc limit 1) on true

obwohl die Definition der Funktion genau das ist.

Das ist in der Tat eine praktische Lösung (mindestens in 9.1): Erstellen Sie eine Funktion, um Ihre Zeile mit der höchsten Priorität zu extrahieren, indem Sie das Limit innerhalb der Funktion ausführen.

Funktionen haben jedoch den Nachteil, dass der Abfrageplan nicht anzeigt, was in ihnen vor sich geht, und ich glaube, dass er immer einen verschachtelten Loop-Join auswählt, auch wenn dies möglicherweise nicht der beste ist.

2
Paul Vaughan

Inspiriert von dezsos Antwort https://dba.stackexchange.com/a/222471/1274 Ich löse das Problem in PostgreSQL mithilfe von Arrays wie folgt:

SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
   ( 
       SELECT ARRAY[ps.price, ps.date]
       FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1
   ) AS special_price_and_date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)

Zugegeben, es ist immer noch nur eine Spalte, aber in meinem Code kann ich leicht auf die beiden Werte zugreifen. Hoffe, es funktioniert auch für Sie.

2
tobi42

Versuchen Sie den folgenden SQL-Befehl:

SELECT p.name,p.image,p.price,pss.price,pss.date
FROM Product p OUTER APPLY(SELECT TOP(1)* 
FROM ProductSpecial ps
WHERE p.Id = ps.Id ORDER BY ps.priority )as pss
2
SANTOSH APPANA

Ich möchte dies nur als letzten Ausweg hier einfügen, für alle, die eine Datenbank-Engine verwenden, die eine oder mehrere der anderen Antworten nicht unterstützt ...

Sie können etwas verwenden wie:

SELECT (col1 || col2) as col3 

(Mit Trennzeichen oder Formatieren von col1 und col2 auf eine bestimmte Länge.) Zeichnen Sie Ihre Daten später mit Unterzeichenfolgen.

Ich hoffe, jemand findet es nützlich.

2
H.A.H.

Verwenden Sie in DB2 für z/OS die Funktionen pack und unpack, um mehrere Spalten in einer Unterauswahl zurückzugeben.

SELECT 
   *, 
   p.name AS name, 
   p.image, 
   p.price,
    unpack((select PACK (CCSID 1028,
               ps.price,
               ps.date)
         FROM product_special ps 
       WHERE p.id = ps.id
         AND ps.date < NOW() 
       ORDER BY ps.priority ASC, LIMIT 1)) .* AS (SPECIAL_PRICE double, DATE date)
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id);
0
Keith C