Dapatkan beberapa kolom dari subquery pilihan

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)

Seperti yang Anda lihat, saya mengulangi subquery yang sama hanya untuk mengeluarkan kolom lain. Saya bertanya-tanya apakah ada cara yang lebih baik untuk melakukan ini?

id adalah kunci utama di kedua tabel. Saya tidak punya masalah membuat product_special.priority unik jika itu dapat membantu.

Sparctus
sumber

Jawaban:

11

Anggap kombinasi product_special.id, product_special.priorityitu unik

 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
sumber
5

kecuali jika Anda berniat mengembalikan bidang sebagai special_price.price dan date.date mengapa tidak alias nama di dalam subquery? misalnya

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

Apakah bahasa permintaan Anda memiliki fungsi agregat PERTAMA ()? Tidak yakin apakah Anda bisa menjadikan PK product_special komposit antara id dan prioritas (keduanya ASC sort) dan mengubah klausa ORDER menjadiGROUP BY id, psi.priority

Anda mungkin dapat menghapus klausa ORDER BY sepenuhnya dan menggunakannya HAVING MIN(psi.priority)

mpag
sumber
2

Perhatikan bahwa mekanisme "berlaku lintas" dari SQL Server akan menyelesaikan ini, tetapi tidak tersedia di PostgreSQL. Pada dasarnya, itu adalah solusi mereka untuk cara melewatkan parameter (yang cenderung menjadi referensi ke kolom eksternal ke ekspresi tabel saat ini) ke fungsi yang disebut sebagai ekspresi tabel dalam klausa FROM. Tetapi ternyata berguna untuk semua jenis situasi di mana Anda ingin menghindari tingkat lain dari bersarang subquery atau memindahkan hal-hal dari klausa FROM ke klausa SELECT. PostgreSQL memungkinkan untuk melakukan ini dengan membuat semacam pengecualian - Anda dapat melewatkan parameter seperti itu jika ekspresi adalah panggilan fungsi yang sederhana tetapi tidak secara tegas berbicara SELECT yang tertanam. Begitu

left join highestPriorityProductSpecial(p.id) on true

tidak apa-apa, tapi tidak

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

meskipun definisi fungsi tepat seperti itu.

Jadi, itu sebenarnya solusi praktis (setidaknya 9,1): buat fungsi untuk mengekstrak baris prioritas tertinggi Anda dengan melakukan batas di dalam fungsi.

Tetapi fungsi memiliki kelemahan bahwa rencana kueri tidak akan menunjukkan apa yang terjadi di dalamnya dan saya percaya itu akan selalu memilih bergabung loop bersarang, bahkan ketika itu mungkin bukan yang terbaik.

Paul Vaughan
sumber
6
cross apply adalah tersedia di Postgres dimulai dengan 9,3 (dirilis pada 2013) tetapi mereka memilih untuk mematuhi standar SQL dan menggunakan standar lateraloperator. Dalam kueri kedua Anda ganti left joindenganleft join lateral
a_horse_with_no_name
2

Coba perintah SQL berikut:

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
sumber
1
tolong tambahkan informasi lain ke jawaban Anda
Ahmad Abuhasna
Kode yang dimaksud menggunakan LIMITdan tidak ditandai dengan DBMS (sehingga bisa berupa MySQL atau Postgres atau SQLite atau mungkin beberapa dbms lainnya). Kode dalam jawaban menggunakan OUTER APPLYdan TOPitu hanya akan bekerja di SQL Server (dan Sybase) yang tidak memiliki LIMIT.
ypercubeᵀᴹ
Yang ini hanya berlaku untuk server sql untuk database lain, kita dapat menggunakan kueri dalam dalam pernyataan pilih.
SANTOSH APPANA
Di Postgres tidak ada OUTER APPLY, tetapi ada LATERAL , yang harus setara. Contoh menggunakannya: stackoverflow.com/a/47926042/4850646
Lucas Basquerotto
2

Terinspirasi oleh jawaban dezso /dba//a/222471/127433 Saya sedang memecahkan masalah di PostgreSQL menggunakan array, seperti ini:

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)

Memang itu masih hanya satu kolom, tetapi dalam kode saya, saya dapat dengan mudah mengakses dua nilai. Semoga berhasil juga untuk Anda.

tobi42
sumber
1

Saya hanya ingin meletakkan ini di sini sebagai pilihan terakhir, untuk semua orang yang menggunakan mesin database yang tidak mendukung satu atau lebih dari jawaban lain ...

Anda dapat menggunakan sesuatu seperti:

SELECT (col1 || col2) as col3 

(Dengan pemisah, atau memformat col1 dan col2 dengan panjang tertentu.) Dan kemudian menggambar data Anda menggunakan sub-string.

Saya harap seseorang menemukannya bermanfaat.

HAH
sumber
0

Dalam DB2 untuk z / OS, gunakan packdan unpackfungsinya untuk mengembalikan beberapa kolom dalam sebuah subselect.

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
sumber