web-dev-qa-db-ja.com

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

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を一意にすることで問題が解決する場合は問題ありません。

26
Sparctus

組み合わせを想定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)
11
a1ex07

フィールドを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)を使用できる可能性があります

5
mpag

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では)。関数の内部で制限を行うことにより、最も優先度の高い行を抽出する関数を作成します。

しかし、関数には、クエリプランが関数の内部で何が行われているのかが表示されないという欠点があり、ネストされたループ結合が常に選択されると信じています。

2
Paul Vaughan

Dezsoの回答に触発 https://dba.stackexchange.com/a/222471/1274 配列を使用して、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つの値に簡単にアクセスできます。それもあなたのために働くことを願っています。

2
tobi42

次の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
2
SANTOSH APPANA

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

あなたは次のようなものを使うことができます:

SELECT (col1 || col2) as col3 

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

私は誰かがそれが役に立つと思うことを望みます。

2
H.A.H.

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

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