Uzyskaj wiele kolumn z wybranego podzapytania

24
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)

Jak widać, powtarzam to samo podzapytanie, aby uzyskać kolejną kolumnę. Zastanawiam się, czy jest na to lepszy sposób?

id jest kluczem podstawowym w obu tabelach. Nie mam problemu z unikaniem product_special.priority, jeśli to może pomóc.

Sparctus
źródło

Odpowiedzi:

11

Zakładając, że kombinacja product_special.id, product_special.priorityjest wyjątkowa

 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)
a1ex07
źródło
5

chyba że zamierzasz zwrócić pola jako special_price.price i date.date, dlaczego nie masz aliasu w nazwach podzapytania? na przykład

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

Czy Twój język zapytań ma funkcję agregującą FIRST ()? Nie jestem pewien, czy można uczynić PK produktu_special złożonym między id a priorytetem (oba sortowanie ASC) i zmienić klauzulę ORDER naGROUP BY id, psi.priority

MOŻESZ być w stanie całkowicie usunąć klauzulę ORDER BY i użyć HAVING MIN(psi.priority)

mpag
źródło
2

Zauważ, że mechanizm „krzyżowego zastosowania” z SQL Server rozwiązałby to, ale nie jest dostępny w PostgreSQL. Zasadniczo było to ich rozwiązanie dotyczące przekazywania parametrów (które są zwykle odniesieniami do kolumn zewnętrznych względem bieżącego wyrażenia tabelowego) do funkcji zwanych wyrażeniami tabelowymi w klauzuli FROM. Okazało się to jednak przydatne we wszystkich sytuacjach, w których chcesz uniknąć innego poziomu zagnieżdżania podkwerend lub przenoszenia rzeczy z klauzuli FROM do klauzuli SELECT. PostgreSQL umożliwił to, robiąc coś w rodzaju wyjątku - możesz przekazać takie parametry, jeśli wyrażenie jest prostym wywołaniem funkcji, ale nie ściśle mówiąc osadzonym SELECT. Więc

left join highestPriorityProductSpecial(p.id) on true

jest w porządku, ale nie

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

nawet jeśli definicja funkcji jest właśnie taka.

Jest to więc przydatne rozwiązanie (przynajmniej w wersji 9.1): utwórz funkcję, aby wyodrębnić wiersz o najwyższym priorytecie, wykonując limit wewnątrz funkcji.

Jednak funkcje mają tę wadę, że plan zapytań nie pokazuje, co się w nich dzieje, i wierzę, że zawsze wybierze połączenie zagnieżdżonej pętli, nawet jeśli nie jest to najlepsze.

Paul Vaughan
źródło
6
cross apply jest dostępny w Postgres od wersji 9.3 (wydanej w 2013 r.), ale zdecydowali się przestrzegać standardu SQL i użyć standardowego lateraloperatora. W swoim drugim zapytaniu zastąpić left joinzleft join lateral
a_horse_with_no_name
2

Spróbuj wykonać następujące polecenie SQL:

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
SANTOSH APPANA
źródło
1
czy możesz dodać więcej informacji do swojej odpowiedzi
Ahmad Abuhasna
Kod, o którym mowa, używa LIMITDBMS i nie jest oznaczony (więc może to być MySQL lub Postgres lub SQLite lub ewentualnie inne dbms). Kod w zastosowaniach odpowiedziami OUTER APPLYi TOPtak to będzie działać tylko w SQL Server (i Sybase), które nie mają LIMIT.
ypercubeᵀᴹ
Ten dotyczy tylko serwera SQL. W przypadku innych baz danych możemy użyć wewnętrznego zapytania w instrukcji select.
SANTOSH APPANA
W Postgres nie ma OUTER APPLY, ale istnieje PÓŹNIEJ , które powinny być równoważne. Przykład z wykorzystaniem: stackoverflow.com/a/47926042/4850646
Lucas Basquerotto
2

Zainspirowany odpowiedzią dezso /dba//a/222471/127433 Rozwiązuję problem w PostgreSQL przy użyciu tablic:

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)

Wprawdzie to wciąż tylko jedna kolumna, ale w moim kodzie mogę łatwo uzyskać dostęp do dwóch wartości. Mam nadzieję, że to również dla ciebie działa.

tobi42
źródło
1

Chcę po prostu umieścić to tutaj w ostateczności, dla każdego, kto korzysta z silnika bazy danych, który nie obsługuje jednej lub więcej innych odpowiedzi ...

Możesz użyć czegoś takiego jak:

SELECT (col1 || col2) as col3 

(Z separatorem lub formatowaniem col1 i col2 do określonej długości.) A później narysuj dane za pomocą podłańcuchów.

Mam nadzieję, że ktoś uzna to za przydatne.

HAH
źródło
0

W DB2 for z / OS użyj packi unpackfunkcji, aby zwrócić wiele kolumn w podselekcji.

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);
Keith C.
źródło