選択サブクエリから複数の列を取得する


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)

ご覧のとおり、別の列を取得するためだけに同じサブクエリを繰り返しています。これを行うより良い方法があるのだろうか?

idは両方のテーブルの主キーです。product_special.priorityを一意にすることで問題が解決する場合は問題ありません。

回答:


11

組み合わせproduct_special.id, product_special.priorityが一意である と仮定する

 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)

5

フィールドをspecial_price.priceおよびdate.dateとして返すつもりでない限り、サブクエリ内の名前をエイリアスしないのはなぜですか?例えば

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

クエリ言語にはFIRST()集約関数がありますか?product_specialのPKをidと優先順位(ASCソートの両方)の複合にすることができ、ORDER句をGROUP BY id, psi.priority

ORDER BY句を完全に削除して使用できる場合があります HAVING MIN(psi.priority)


2

SQL Serverの「相互適用」メカニズムでこれを解決できますが、PostgreSQLでは使用できないことに注意してください。基本的に、FROM句でテーブル式として呼び出される関数にパラメーター(現在のテーブル式の外部の列への参照である傾向があります)を渡す方法のソリューションでした。しかし、別のレベルのサブクエリのネストや、FROM句からSELECT句への移動を避けたいすべての種類の状況で役立つことがわかりました。PostgreSQLは、一種の例外を作成することでこれを可能にしました。式が単純な関数呼び出しであり、厳密に言えば埋め込みSELECTではない場合、そのようなパラメーターを渡すことができます。そう

left join highestPriorityProductSpecial(p.id) on true

大丈夫ですが、そうではありません

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

関数の定義はまさにそれですが。

そのため、実際には便利なソリューションです(少なくとも9.1では)。関数内で制限を行うことにより、最も優先度の高い行を抽出する関数を作成します。

しかし、関数には、クエリプランがその内部で何が起こっているかを表示しないという欠点があり、最良ではない場合でも、ネストループ結合を常に選択すると考えています。


6
cross apply 9.3(2013年にリリース)以降のPostgres 使用可能ですが、SQL標準に準拠し、標準lateral演算子を使用することを選択しました。あなたの2番目のクエリで置き換えleft joinleft join lateral
a_horse_with_no_name

2

次の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

1
回答にさらに情報を追加してもらえますか
Ahmad Abuhasna

問題のコードはLIMITDBMSを使用しており、DBMSでタグ付けされていません(したがって、MySQL、Postgres、SQLite、または他のdbmsである可能性があります)。回答のコードはを使用OUTER APPLYしているTOPため、を持たないSQL Server(およびSybase)のみで動作しますLIMIT
ypercubeᵀᴹ

これは、selectステートメント内で内部クエリを使用できる他のデータベースのSQLサーバーにのみ適用されます。
SANTOSH APPANA

PostgresにはありませんがOUTER APPLYLATERALがあり、これは同等です。それを使用した例:stackoverflow.com/a/47926042/4850646
ルーカスバスケロット

2

dezsoの答え/dba//a/222471/127433 に触発されて、次のように配列を使用してPostgreSQLの問題を解決しています。

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)

確かにそれはまだ1列だけですが、私のコードでは、2つの値に簡単にアクセスできます。それがあなたにも役立つことを願っています。


1

これを最後の手段として、ここで1つ以上の他の回答をサポートしないデータベースエンジンを使用するすべての人のために...

次のようなものを使用できます。

SELECT (col1 || col2) as col3 

(セパレーターを使用するか、col1とcol2を特定の長さにフォーマットします。)その後、サブストリングを使用してデータを描画します。

誰かがそれを役に立つと思うことを願っています。


0

DB2 for z / OSでは、packunpack関数を使用して、副選択で複数の列を返します。

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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.