2011-12-17 39 views
2

postgreSQL問題...我有一個更新查詢,它使用來自子查詢的結果更新列,但是在某些情況下,子查詢將返回null,從而引發'not null'約束在列上,如果子查詢返回null,我怎麼才能更新它?postgresql where條件返回至少一個結果

我已經嘗試過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 /更新/刪除):的PostgreSQL版本號? – 2011-12-17 13:30:33

+0

請張貼您的表格定義(至少相關的列和PK/FK約束),並儘可能提供一個小腳本來用測試數據填充表格。 – tscho 2011-12-17 13:33:10

+0

@ErwinBrandstetter:我很想知道你在找哪個功能。它是CTE,因此最小的PostgreSQL 8.4? – tscho 2011-12-17 13:42:32

回答

4

聚結,NVL,IFNULL在大多數數據庫引擎將做一個有條件的聲明說,拿在這種情況下,字符串中的第一個非空值時,再選擇返回null它將設置ave_price =本身。

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的要求,但它對數據有類似的影響。

有關COALESCE查看更多資訊:PostgreSQL

要真正防止更新你會needto上的更新添加一個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) 
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 

邏輯上執行子查詢兩次會對性能產生兩次影響;而合併只需要執行一次。總是有多種方式來完成任務,並且根據要求,必須選擇哪種選項最適合他們。

+0

感謝,coalesce解決方案將正常工作(儘管執行需要近1分鐘!) – DaveB 2011-12-17 13:40:01

+2

@DaveB:使用'coalesce'的第一個版本不是一個好的解決方案。導致很多無意義的更新,需要更長的時間,在表中留下很多額外的死元組,並且可能觸發不應該被觸發的ON UPDATE觸發器。第二個版本更好(雖然不必要慢)。有更好的方法來做到這一點(這讓我發表了另一個答案)。 – 2011-12-17 13:51:40

+0

是的,有很多方法可以完成我指出的兩項任務,DaveB提供的選項是第三個更好的解決方案 – xQbert 2011-12-17 22:22:51

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_id相關的比例存在於表user_sessions,但不是在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; 

的點第二個版本是儘早排除不相關的行。 的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

在WITH子句

+0

@tscho:謝謝!我修改了我的答案以澄清這一點。 – 2011-12-17 14:07:13

+0

好,謝謝_you_!我刪除了我的原始評論,建議將8.4作爲最低版本,因爲我無法將其編輯爲9.1。 – tscho 2011-12-17 14:15:24

相關問題