2012-05-06 81 views
3

假設我有一個名爲t的表存儲在postgresql數據庫中。 我有6列名爲a,b,c,d,e,f。 列A,B和c 0和100之間的取值,但上aritmetic刻度0 <一個< b <Ç< 100列第d,e和f取範圍爲1的任何值至10帶有IF ELSE語句的postgresql plpsql函數

我想要計算列d,e和f的加權平均值,但需要與列a,b和c相關的條件。條件是平均僅在A,B和C柱具有值小於50

我覺得這需要一個函數來計算,所以我開始做它:

CREATE OR REPLACE FUNCTION example(t character varying, a character varying, b character varying, c character varying, d character varying, e character varying, f character varying, g character varying) RETURNS double precision AS $$ 

BEGIN 
    ALTER TABLE t ADD COLUMN g double precision; 
     UPDATE t 
      IF a > 50 THEN 
       SET g = d; 
      ELSE 
       IF b > 50 THEN; 
        SET g = (d+e)/2; 
       END IF c > 50 THEN 
         SET g = (d+e+f)/3; 
      END IF; 
END; 
$$ LANGUAGE plpgsql; 

我出現以下錯誤:

ERROR: syntax error at or near "$1" 
LINE 1: ALTER TABLE $1 ADD COLUMN $2 double precision 
       ^
QUERY: ALTER TABLE $1 ADD COLUMN $2 double precision 
CONTEXT: SQL statement in PL/PgSQL function "example" near line 2 

********** Error ********** 

ERROR: syntax error at or near "$1" 
SQL state: 42601 
Context: SQL statement in PL/PgSQL function "example" near line 2 

誰能告訴我是我錯了,所以我可以繼續與計算所需的平均?

+0

您不能以這種方式將參數表的名稱作爲參數傳遞。但是,每次運行該功能時添加一個新列似乎都毫無用處。你爲什麼想這樣做? –

+0

感謝您的積分!關於SET命令,indedd是錯誤的。但是我可以在SET中使用IF嗎?我將如何使用它? 'UPDATE t SET g = D IF a> 50'? –

回答

2

錯誤的直接原因是命名衝突。您可以定義參數tg,並在ALTER TABLE語句中使用相同的名稱。我習慣於給參數名稱加上前綴(如_t,_g),這樣它們就不會與函數體中的其他名稱發生衝突。

此外,您的參數不應該被定義爲character varying,因爲它們具有數值。使用適當的numerical type,可能是double precision

但我不認爲你需要一個功能。這可以通過簡單的SQL語句來解決:

ALTER TABLE tbl ADD COLUMN g double precision; 

UPDATE tbl 
SET g = CASE 
      WHEN a > 50 THEN d 
      WHEN b > 50 THEN (d+e)/2 
      WHEN c > 50 THEN (d+e+f)/3 
      ELSE 0 -- definition for ELSE case is missing 
     END; 

你也可以完全劃傷的整體思路和使用視圖爲宗旨,爲g僅持有功能相關的數據:

CREATE VIEW tbl_with_g AS 
SELECT * 
    , CASE 
      WHEN a > 50 THEN d 
      WHEN b > 50 THEN (d+e)/2 
      WHEN c > 50 THEN (d+e+f)/3 
      ELSE 0 
     END AS g 
FROM tbl; 
+0

感謝您的積分!但參數不在函數中使用?在我定義它們之後,我應該添加要在函數中使用的變量? –

+0

關於您使用CASE功能的想法,我已經在創建功能之前應用過。它確實計算了一些東西,但是當我進行驗證時,似乎在CASE函數中不能使用不同的列,因爲如果第一個WHEN條件爲真,評估就會停止。因此,雖然第一個條件爲真,但該值設置爲d,但第二個條件未評估,但這是我需要的,在條件爲假時進行平均。 –

+0

@MihaiNiculita然後你應該調整你的條件是互斥的。如果你想在'b> 50'時跳過第一個'THEN'子句,那麼把它作爲你的第一個'WHEN':'a> 50 AND b <= 50'(相當於'a> 50 AND NOT b> 50')。 – jpmc26

3

我完全同意一切都在Erwin的答案中,但要指出另一種選擇。您可以創建一種「生成列」,這將按需計算這樣的:

CREATE FUNCTION g(rec t) 
    RETURNS double precision 
    IMMUTABLE 
    LANGUAGE SQL 
AS $$ 
    SELECT CASE 
      WHEN $1.a > 50 THEN $1.d 
      WHEN $1.b > 50 THEN ($1.d+$1.e)/2 
      WHEN $1.c > 50 THEN ($1.d+$1.e+$1.f)/3 
      ELSE 0 
     END; 
$$; 

之後就可以引用g就像你一列,只要基準由表名限定或別名。例如:

SELECT *, t.g FROM t; 

當一個明顯的限定的列引用未解析爲列,PostgreSQL規劃查找這個名字這需要表的記錄類型作爲其唯一的參數的函數,並執行該功能。有時這種方法比使用視圖更方便,但效果基本相同。

+0

+1 PostgreSQL的一個很酷的功能!一個相關的問題出現了,我添加了一個詳細的解釋爲什麼這個工程:http://stackoverflow.com/questions/11165450/store-common-query-as-column/11166268#11166268 –