

 Amazon Redshift 將不再支援從修補程式 198 開始建立新的 Python UDFs。現有 Python UDF 將繼續正常運作至 2026 年 6 月 30 日。如需詳細資訊，請參閱[部落格文章](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 純量 Python UDF
<a name="udf-creating-a-scalar-udf"></a>

純量 Python UDF 包含呼叫函數時執行的 Python 程式，並傳回單一值。[CREATE FUNCTION](r_CREATE_FUNCTION.md) 命令定義下列參數：
+ (選用) 輸入引數。每個引數必須具有一個名稱和一個資料類型。
+ 一個傳回資料類型。
+ 一個可執行的 Python 程式。

Python UDF 的輸入和傳回資料類型可以是下列任一類型：
+  SMALLINT 
+  INTEGER 
+  BIGINT 
+  DECIMAL 
+  REAL 
+  DOUBLE PRECISION 
+  BOOLEAN 
+  CHAR 
+  VARCHAR 
+  DATE 
+  TIMESTAMP 
+  ANYELEMENT 

這些類型的別名也有效。如需資料類型及其別名的完整清單，請參閱 [資料類型](c_Supported_data_types.md)。

當 Python UDF 使用資料類型 ANYELEMENT 時，Amazon Redshift 會根據執行時期提供的引數，自動將其轉換為標準資料類型。如需詳細資訊，請參閱[ANYELEMENT 資料類型](udf-data-types.md#udf-anyelement-data-type)。

當 Amazon Redshift 查詢呼叫純量 UDF 時，下列步驟會在執行期發生。

1. 函數會將輸入引數轉換為 Python 資料類型。

   如需將 Amazon Redshift 資料類型映射至 Python 資料類型，請參閱 [Python UDF 資料類型](udf-data-types.md)。

1. 函數會執行 Python 程式，剖析已轉換的輸入引數。

1. Python 程式碼會傳回單一值。傳回值的資料類型必須對應至函數定義所指定的 RETURNS 資料類型。

1. 函數會將 Python 傳回值轉換為指定的 Amazon Redshift 資料類型，然後將該值傳回至佇列。

**注意**  
Python 3 不適用於 Python UDF。若要取得 Amazon Redshift UDF 的 Python 3 支援，請改用 [純量 Lambda UDF](udf-creating-a-lambda-sql-udf.md)。

# 純量 Python UDF 範例
<a name="udf-scalar-function-example"></a>

下列範例會建立一個函數，比較兩個數字並傳回較大的值。請注意，雙貨幣符號 (\$1\$1) 之間程式碼的縮排是 Python 需求。如需詳細資訊，請參閱[CREATE FUNCTION](r_CREATE_FUNCTION.md)。

```
create function f_py_greater (a float, b float)
  returns float
stable
as $$
  if a > b:
    return a
  return b
$$ language plpythonu;
```

下列查詢會呼叫新的 `f_greater` 函數來查詢 SALES 資料表，並傳回 COMMISSION 或 20% 的 PRICEPAID，以較大者為準。

```
select f_py_greater (commission, pricepaid*0.20) from sales;
```

# Python UDF 資料類型
<a name="udf-data-types"></a>

Python UDF 可對輸入引數和函數的傳回值使用任何標準 Amazon Redshift 資料類型。除了標準資料類型外，UDF 還支援資料類型 *ANYELEMENT*，而 Amazon Redshift 會根據執行期提供的引數自動將其轉換為標準資料類型。純量 UDF 可以傳回資料類型 ANYELEMENT。如需詳細資訊，請參閱[ANYELEMENT 資料類型](#udf-anyelement-data-type)。

在執行期間，Amazon Redshift 會將引數從 Amazon Redshift 資料類型轉換為 Python 資料類型以進行處理。然後，它將傳回值從 Python 資料類型轉換為對應的 Amazon Redshift 資料類型。如需 Amazon Redshift 資料類型的相關資訊，請參閱[資料類型](c_Supported_data_types.md)。

下表將 Amazon Redshift 資料類型映射至 Python 資料類型。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/redshift/latest/dg/udf-data-types.html)

## ANYELEMENT 資料類型
<a name="udf-anyelement-data-type"></a>

ANYELEMENT 是*多型資料類型*。這表示，若對引數的資料類型使用 ANYELEMENT 來宣告函數，則函數可以接受任何標準 Amazon Redshift 資料類型，做為呼叫函數時該引數的輸入。ANYELEMENT 引數會設為呼叫函數時實際傳遞至其中的資料類型。

如果函數使用多個 ANYELEMENT 資料類型，則在呼叫函數時，它們必須全都解析為相同的實際資料類型。所有 ANYELEMENT 引數資料類型都會設為傳遞至 ANYELEMENT 之第一個引數的實際資料類型。例如，宣告為 `f_equal(anyelement, anyelement)` 的函數將採用任何兩個輸入值，只要它們是相同的資料類型。

如果函數的輸入值宣告為 ANYELEMENT，則至少一個輸入引數必須是 ANYELEMENT。傳回值的實際資料類型將與提供給 ANYELEMENT 輸入引數的實際資料類型相同。

# UDF 的 Python 語言支援
<a name="udf-python-language-support"></a>

您可以根據 Python 程式設計語言建立自訂 UDF。[Python 2.7 standard library](https://docs.python.org/2/library/index.html) 可供 UDF 使用，但下列模組除外：
+ ScrolledText
+ Tix
+ Tkinter
+ tk
+ turtle
+ smtpd

除了 Python 標準程式庫外，下列模組是 Amazon Redshift 實作的一部分：
+ [numpy 1.8.2](http://www.numpy.org/)
+ [pandas 0.14.1](https://pandas.pydata.org/)
+ [python-dateutil 2.2](https://dateutil.readthedocs.org/en/latest/)
+ [pytz 2014.7](https://pypi.org/project/pytz/2014.7/)
+ [scipy 0.12.1](https://www.scipy.org/)
+ [six 1.3.0](https://pypi.org/project/six/1.3.0/)
+ [wsgiref 0.1.2](https://pypi.python.org/pypi/wsgiref)

您也可以匯入自己的自訂 Python 模組，並執行 [CREATE LIBRARY](r_CREATE_LIBRARY.md) 命令，使它們可供 UDF 使用。如需詳細資訊，請參閱[範例：匯入自訂 Python 程式庫模組](udf-importing-custom-python-library-modules.md)。

**重要**  
Amazon Redshift 會透過 UDF 全面封鎖檔案系統的網路存取和寫入存取。

**注意**  
Python 3 不適用於 Python UDF。若要取得 Amazon Redshift UDF 的 Python 3 支援，請改用 [純量 Lambda UDF](udf-creating-a-lambda-sql-udf.md)。

# 範例：匯入自訂 Python 程式庫模組
<a name="udf-importing-custom-python-library-modules"></a>

您可以使用 Python 語言語法來定義純量函數。您可以使用 Python 標準程式庫模組和 Amazon Redshift 預先安裝模組。您也可以建立自己的自訂 Python 程式庫模組，並將程式庫匯入叢集，或使用 Python 或第三方的現有程式庫。

您建立的程式庫不可包含與 Python 標準程式庫模組或 Amazon Redshift 預先安裝 Python 模組同名的模組。如果現有使用者安裝的程式庫使用相同的 Python 套件做為您建立的程式庫，則您必須捨棄現有的程式庫，然後才能安裝新的程式庫。

您必須是超級使用者或具有 `USAGE ON LANGUAGE plpythonu` 權限，才能安裝自訂程式庫；不過，任何具有足夠權限來建立函數的使用者都可以使用已安裝的程式庫。您可以查詢 [PG\$1LIBRARY](r_PG_LIBRARY.md) 系統目錄，來檢視叢集上已安裝之程式庫的相關資訊。

## 將自訂 Python 模組匯入您的叢集
<a name="udf-import-custom-python-module-procedure"></a>

本節提供將自訂 Python 模組匯入至您的叢集的範例。若要執行本節中的步驟，您必須具有 Amazon S3 儲存貯體，而您會在其中上傳程式庫套件。然後，在您的叢集中安裝套件。如需建立儲存貯體的相關資訊，請前往《Amazon Simple Storage Service 使用者指南》**中的[建立儲存貯體](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CreatingaBucket.html)。

在此範例中，讓我們假設您建立 UDF，來使用資料中的位置和距離。從 SQL 用戶端工具連接至您的 Amazon Redshift 叢集，並執行下列命令來建立函數。

```
CREATE FUNCTION f_distance (x1 float, y1 float, x2 float, y2 float) RETURNS float IMMUTABLE as $$
    def distance(x1, y1, x2, y2):
        import math
        return math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2)
 
    return distance(x1, y1, x2, y2)
$$ LANGUAGE plpythonu;
 
CREATE FUNCTION f_within_range (x1 float, y1 float, x2 float, y2 float) RETURNS bool IMMUTABLE as $$ 
    def distance(x1, y1, x2, y2):
        import math
        return math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2)
 
    return distance(x1, y1, x2, y2) < 20
$$ LANGUAGE plpythonu;
```

請注意，會複製先前函數中的幾行程式碼。需要進行此複製的原因是 UDF 無法參考另一個 UDF 的內容，而且這兩個函數都需要相同功能。然而，不是複製多個函數中的程式碼，而是您可以建立自訂程式庫，並設定您的函數來使用它。

若要這樣做，首先遵循下列步驟來建立程式庫套件：

1. 建立名為 **geometry** 的資料夾。此資料夾是程式庫的最上層套件。

1. 在 **geometry** 資料夾中，建立名為 `__init__.py` 的檔案。請注意，檔案名稱包含兩個雙底線字元。此檔案向 Python 表示可以初始化套件。

1. 另外，在 **geometry** 資料夾中，建立名為 **trig** 的資料夾。此資料夾是程式庫的子套件。

1. 在 **trig** 資料夾中，建立另一個名為 `__init__.py` 的檔案，以及一個名為 `line.py` 的檔案。在此資料夾中，`__init__.py` 向 Python 表示可以初始化子套件，而且 `line.py` 是包含程式庫程式碼的檔案。

   您的資料夾和檔案結構應該與下列資料夾和檔案結構相同：

   ```
   geometry/
      __init__.py
      trig/
         __init__.py
         line.py
   ```

    如需套件結構的相關資訊，請前往 Python 網站上 Python 教學課程中的[模組](https://docs.python.org/2/tutorial/modules.html)。

1.  下列程式碼包含程式庫的類別和成員函數。複製它並貼至 `line.py`。

   ```
   class LineSegment:
     def __init__(self, x1, y1, x2, y2):
       self.x1 = x1
       self.y1 = y1
       self.x2 = x2
       self.y2 = y2
     def angle(self):
       import math
       return math.atan2(self.y2 - self.y1, self.x2 - self.x1)
     def distance(self):
       import math
       return math.sqrt((self.y2 - self.y1) ** 2 + (self.x2 - self.x1) ** 2)
   ```

 在您建立了套件之後，請執行下列動作，來準備套件並將其上傳至 Amazon S3。

1. 將 **geometry** 資料夾的內容壓縮成名為 **geometry.zip** 的 .zip 檔案。不要包括 **geometry** 資料夾本身；只包括資料夾的內容，如下所示：

   ```
   geometry.zip
      __init__.py
      trig/
         __init__.py
         line.py
   ```

1. 將 **geometry.zip** 上傳至您的 Amazon S3 儲存貯體。
**重要**  
 如果 Amazon S3 儲存貯體不是位於與 Amazon Redshift 叢集所在的同一區域，您必須使用 REGION 選項來指定資料所在的區域。如需詳細資訊，請參閱[CREATE LIBRARY](r_CREATE_LIBRARY.md)。

1.  從您的 SQL 用戶端工具中，執行下列命令來安裝程式庫。將 *<bucket\$1name>* 取代為您的儲存貯體名稱，並將 *<access key id>* 和 *<secret key>* 取代為您的 AWS Identity and Access Management (IAM) 使用者登入資料中的存取金鑰和私密存取金鑰。

   ```
   CREATE LIBRARY geometry LANGUAGE plpythonu FROM 's3://<bucket_name>/geometry.zip' CREDENTIALS 'aws_access_key_id=<access key id>;aws_secret_access_key=<secret key>';
   ```

 在您的叢集中安裝程式庫之後，您需要設定函數來使用程式庫。若要這樣做，請執行下列命令。

```
CREATE OR REPLACE FUNCTION f_distance (x1 float, y1 float, x2 float, y2 float) RETURNS float IMMUTABLE as $$ 
    from trig.line import LineSegment
 
    return LineSegment(x1, y1, x2, y2).distance()
$$ LANGUAGE plpythonu;
 
CREATE OR REPLACE FUNCTION f_within_range (x1 float, y1 float, x2 float, y2 float) RETURNS bool IMMUTABLE as $$ 
    from trig.line import LineSegment
 
    return LineSegment(x1, y1, x2, y2).distance() < 20
$$ LANGUAGE plpythonu;
```

在上述命令中，`import trig/line` 會從本節中的原始函數中刪除重複的程式碼。您可以在多個 UDF 中使用此程式庫所提供的功能。請注意，若要匯入模組，您只需要指定子套件和模組名稱的路徑 (`trig/line`)。

# Python UDF 限制
<a name="udf-constraints"></a>

在本主題中列出的限制內，只要可以使用 Amazon Redshift 內建純量函數之處，就可以使用 UDF。如需詳細資訊，請參閱[SQL 函數參考](c_SQL_functions.md)。

Amazon Redshift Python UDF 有下列限制：
+ Python UDF 無法存取網路，也無法讀取或寫入至檔案系統。
+ 使用者安裝的 Python 程式庫的總和大小不得超過 100 MB。
+ 對於使用自動工作負載管理 (WLM) 的佈建叢集和無伺服器工作群組，Amazon Redshift 一次只能執行一個 Python UDF。如果您嘗試並行執行多個 UDF，Amazon Redshift 會將其餘 Python UDF 排入佇列，以在工作負載管理佇列中執行。使用自動 WLM 時，SQL UDF 沒有並行限制。
+  針對佈建叢集使用手動 WLM 時，每個叢集可以並行執行的 Python UDF 數目限制為叢集的總計並行層級的四分之一。例如，並行為 15 的佈建叢集最多可以執行三個並行 Python UDF。
+ 使用 Python UDF 時，Amazon Redshift 不支援 SUPER 和 HLLSKETCH 資料類型。

# 記錄 Python UDF 中的錯誤和警告
<a name="udf-logging-messages"></a>

您可以使用 Python 記錄模組，在 UDF 中建立使用者定義的錯誤和警告訊息。您可以在查詢執行之後，查詢 [SVL\$1UDF\$1LOG](r_SVL_UDF_LOG.md) 系統檢視來擷取日誌記錄的訊息。

**注意**  
UDF 記錄會使用叢集資源，因此可能會影響系統效能。我們建議只針對開發和故障診斷實作記錄。

在查詢執行期間，日誌處理常式會將訊息連同對應的函數名稱、節點和配量一起寫入至 SVL\$1UDF\$1LOG 系統檢視。日誌處理常式會在每個配量每則訊息寫入一個資料列至 SVL\$1UDF\$1LOG。訊息會截斷至 4096 個位元組。UDF 日誌限制為每個配量 500 個資料列。當日誌滿時，日誌處理常式會捨棄較舊訊息，並將警告訊息新增至 SVL\$1UDF\$1LOG。

**注意**  
Amazon Redshift UDF 日誌處理常式會將換行 (`\n`)、縱線 (`|`) 字元和反斜線 (`\`) 字元加上反斜線 (`\`)，使它們逸出。

依預設，UDF 日誌層級會設為 WARNING。日誌層級為 WARNING、ERROR 和 CRITICAL 的訊息都會加以記錄。具有較低嚴重性 INFO、DEBUG 和 NOTSET 的訊息則會略過。若要測試 UDF 日誌層級，請使用 Python 記錄器方法。例如，下列程式碼會將日誌層級設為 INFO。

```
logger.setLevel(logging.INFO)
```

如需使用 Python 記錄模組的相關資訊，請參閱 Python 文件中的 [Python 的記錄機能](https://docs.python.org/2.7/library/logging.html)。

下列範例會建立名為 f\$1pyerror 的函數，用來匯入 Python 記錄模組、將記錄器執行個體化，以及記錄錯誤。

```
CREATE OR REPLACE FUNCTION f_pyerror() 
RETURNS INTEGER
VOLATILE AS
$$
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.info('Your info message here') 
return 0
$$ language plpythonu;
```

下列範例會查詢 SVL\$1UDF\$1LOG，來檢視前一個範例中記錄的訊息。

```
select funcname, node, slice, trim(message) as message 
from svl_udf_log;

  funcname  | query | node | slice |   message  
------------+-------+------+-------+------------------
  f_pyerror | 12345 |     1|     1 | Your info message here
```