2011-12-17 18 views
2

postgreSQL質問...サブクエリの結果で列を更新する更新クエリがありますが、場合によってはサブクエリがnullを返すnullでない制約をスローしますサブクエリがnullを返す場合は、列で、どのように更新しないようにすることができますか?postgresql where条件が少なくとも1つの結果を返します

私はEXISTSを試しましたが、これはWHERE句でのみ動作するようです。

UPDATE user_stats as stats 
SET ave_price = (
    SELECT AVG(l.price) 
    FROM lengths as l, user_sessions as us 
    WHERE l.product_type = 'car' 
    AND l.session_id = us.session_id 
    AND stats.user_id = us.user_id 
) 
+0

重要で(INSERT/UPDATE/DELETE)データ変更コマンドを許可:PostgreSQLのバージョン番号? –

+0

テーブル定義(少なくとも関連する列とPK/FK制約)を投稿してください。可能であれば、テストデータをテーブルに取り込むための小さなスクリプトを用意してください。 – tscho

+0

@ErwinBrandstetter:あなたが探している機能を知りたいのですが。それはCTEなので、PostgreSQL 8.4は最小ですか? – tscho

答えて

4

合体、ほとんどのDBエンジンでNVL、IFNULLは、副選択に戻りますが、それは自分自身に= ave_priceを設定しますヌルこの場合では、文字列の最初のnull以外の値を取ると言う条件文を行います。

UPDATE user_stats as stats 
SET ave_price = coalesce((
    SELECT AVG(l.price) 
    FROM lengths as l, user_sessions as us 
    WHERE l.product_type = 'car' 
    AND l.session_id = us.session_id 
    AND stats.user_id = us.user_id 
),ave_price) 

これは要求通りのudpateを防ぎませんが、データに同様の影響を与えます。合体を参照してください。詳細については

論理的に実行
UPDATE user_stats as stats 
    SET ave_price = (
     SELECT AVG(l.price) 
     FROM lengths as l, user_sessions as us 
     WHERE l.product_type = 'car' 
     AND l.session_id = us.session_id 
     AND stats.user_id = us.user_id) 
WHERE (SELECT AVG(l.price) 
     FROM lengths as l, user_sessions as us 
     WHERE l.product_type = 'car' 
     AND l.session_id = us.session_id 
     AND stats.user_id = us.user_id) is not null 

PostgreSQL

が実際にあなたのような更新にwhere句、再度実行サブクエリを追加needtoう更新を防ぐためにサブクエリは2回パフォーマンスに2倍の影響を与えます。合体は一度だけ実行する必要があります。物事を行うには常に複数の方法があり、要件に応じて、どのオプションが最良のものになるかを選択する必要があります。

+0

ありがとう、合体ソリューションは正常に動作します(ただし、実行に約1分かかります!) – DaveB

+2

@DaveB: 'coalesce'の最初のバージョンは良い解決策ではありません。多くの無意味な更新をもたらし、時間がかかり、テーブルに多くのデッドタプルが残され、起動されないようにトリガーを起動する可能性があります。 2番目のバージョンは優れています(ただし、不必要に遅いです)。これを行うためのよりよい方法があります(これにより私は別の答えを投稿しました)。 –

+0

同じタスクを達成するにはたくさんの方法がありますが、私は2つを指摘しました.DaveBが提示するオプションは3番目で、より良い解決策です。 – xQbert

1

ではなくサブクエリ式の中から選択するために、実際のサブクエリを使用します。

UPDATE user_stats s 
SET ave_price = x.ave_price 
FROM (
    SELECT user_id 
      ,avg(l.price) AS ave_price 
    FROM lengths l 
    JOIN user_sessions us ON us.session_id = l.session_id 
    WHERE l.product_type = 'car' 
    GROUP BY us.user_id 
    HAVING avg(l.price) IS NOT NULL 
    ) x 
WHERE x.user_id = s.user_id; 

これも、速くなります。 あなたがテーブルuser_sessionsに存在するuser_idの関連する割合を持っていますが、user_statsで、次のクエリは速いかもしれない場合(一方で、両方のすべてのケースでも同じ結果):

UPDATE user_stats s 
SET ave_price = x.ave_price 
FROM (
    SELECT user_id 
      ,avg(l.price) AS ave_price 
    FROM lengths l 
    JOIN user_stats usr USING (user_id) 
    JOIN user_sessions us ON us.session_id = l.session_id 
    WHERE l.product_type = 'car' 
    GROUP BY us.user_id 
    HAVING avg(l.price) IS NOT NULL 
    ) x 
WHERE x.user_id = s.user_id; 

のポイントを2番目のバージョンは、無関係な行を早期に除外することです。 (ややエレガントで読みやすい)CTEと書かれた 同じクエリ:

WITH x AS (
    SELECT user_id 
      ,avg(l.price) AS ave_price 
    FROM lengths l 
    JOIN user_stats usr USING (user_id) 
    JOIN user_sessions us ON us.session_id = l.session_id 
    WHERE l.product_type = 'car' 
    GROUP BY us.user_id 
    HAVING avg(l.price) IS NOT NULL 
    ) 
UPDATE user_stats s 
SET ave_price = x.ave_price 
FROM x 
WHERE x.user_id = s.user_id; 

は、CTEがSELECTクエリはPostgreSQL 8.4で導入された一方で、CTEは、データ変更コマンドのためにしたことを通知することのみintroduced with PostgreSQL 9.1

この質問のための条項

+0

@tscho:ありがとう!私はこれを明確にするために私の答えを修正した。 –

+0

よく、ありがとうございました! 9.1に編集することができなかったので、私は8.4を最低限のバージョンとして提案した私の元のコメントを削除しました。 – tscho

関連する問題