」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 在 PostgreSQL 中建立自訂函數

在 PostgreSQL 中建立自訂函數

發佈於2024-07-30
瀏覽:573

Creating Custom Functions In PostgreSQL

在PostgreSQL中,可以创建自定义函数来解决复杂的问题。

这些可以使用默认的 PL/pgSQL 脚本语言编写,也可以使用其他脚本语言编写。

Python、Perl、Tcl 和 R 是支持的一些脚本语言。

虽然 PL/pgSQL 随任何 Postgres 安装一起提供,但要使用其他语言需要进行一些设置。

安装扩展

在使用扩展之前,需要安装扩展包。

在 Ubuntu 上你可以运行:

Perl

sudo apt-get -y install postgresql-plperl-14

软件包名称“postgresql-plperl-14”特定于 PostgreSQL 版本 14。如果您使用的是不同版本的 PostgreSQL,则需要更改软件包名称中的版本号以匹配您安装的 PostgreSQL 版本。

Python 3

sudo apt-get install postgresql-plpython3-14

激活扩展

要在 PostgreSQL 中激活扩展,必须使用 CREATE EXTENSION 语句定义扩展。

Perl

CREATE EXTENSION plperl;

Python

CREATE EXTENSION plpython3;

你好世界示例

创建扩展后,可以使用该扩展创建自定义函数。

Perl

CREATE OR REPLACE FUNCTION hello(name text) 
RETURNS text AS $$
    my ($name) = @_;
    return "Hello, $name!";
$$ LANGUAGE plperl;

Python

CREATE OR REPLACE FUNCTION hello(name text)
RETURNS text AS $$
    return "Hello, "   name   "!"
$$ LANGUAGE plpython3;

逐行分解

CREATE OR REPLACE FUNCTION hello(name text)

这一行是在 Postgres 中创建函数的方式。通过使用 CREATE 或 REPLACE,它将用新函数覆盖已使用名称 hello 定义的任何函数。

使用 CREATE FUNCTION hello(name text) 将阻止该函数覆盖现有函数,并且如果该函数已存在,则会出错。


RETURNS text AS $$

这定义了将返回什么 Postgres 数据类型,重要的是指定的数据类型是 Postgres 识别的类型。如果已经定义了自定义数据类型,则可以指定自定义数据类型。

$$ 是一个分隔符,用于标记代码块的开始和结束。在这一行中,它标记了代码块的开始。

开始和结束$$之间的所有代码都将由Postgres执行


$$ LANGUAGE plperl;

$$ 表示脚本的结尾,并告诉 Postgres 脚本应该解析为哪种语言。

使用功能

函数可以像任何内置 Postgres 函数一样使用

SELECT hello('world');

这将返回一个值为 Hello world!

的列

函数可以是更复杂查询的一部分:

SELECT id, title, hello('world') greeting FROM table;

更复杂的例子

这是一个示例函数,它接受来自字段的文本并返回字数。

CREATE OR REPLACE FUNCTION word_count(paragraph text)
RETURNS json AS $$
use strict;
use warnings;

my ($text) = @_;

my @words = $text =~ /\w /g;
my $word_count = scalar @words;

my $result = '{' .
    '"word_count":' . $word_count .
'}';
return $result;
$$ LANGUAGE plperl;

这将返回带有字数统计的 JSON 格式结果。


我们可以在函数中添加更详细的统计数据。

CREATE OR REPLACE FUNCTION word_count(paragraph text)
RETURNS json AS $$
use strict;
use warnings;

my ($text) = @_;

my @words = $text =~ /\w /g;

my $word_count = scalar @words;

my $sentence_count = ( $text =~ tr/!?./!?./ ) || 0;

my $average_words_per_sentence =
  $sentence_count > 0 ? $word_count / $sentence_count : 0;

my $result = '{' .
    '"word_count":' . $word_count . ',' .
    '"sentence_count":' . $sentence_count . ',' .
    '"average_words_per_sentence":"' . sprintf("%.2f", $average_words_per_sentence) . '"' .
'}';

return $result;
$$ LANGUAGE plperl SECURITY DEFINER;

现在当我们在查询中使用它时

SELECT word_count(text_field) word_count FROM table

它将返回 JSON,如

{"word_count":116,"sentence_count":15,"average_words_per_sentence":"7.73"}

安全考虑

使用自定义函数或外部脚本语言时,需要考虑其他安全注意事项。在可用性和安全性之间取得适当的平衡可能是一种兼顾的行为。

安全定义者与安全调用者

在上一个函数中,SECURITY DEFINER 选项被添加到创建函数语句中。

从安全角度考虑您希望函数如何运行非常重要。

默认行为是使用 SECURITY INVOKER。这将以运行该函数的用户的权限运行该函数。

SECURITY DEFINER 提供了对授予功能的权限的更多控制。使用此模式,该函数将以创建该函数的用户的权限运行。

这可能是好事,也可能是坏事,如果一个函数是由具有有限权限的用户创建的,那么对数据库造成的损害就很小。

如果该函数是由具有高访问权限的用户创建的,则该函数将以相同的权限运行。根据函数的类型,这可能允许用户以比授予的更多开放权限运行该函数。

有时这很有用,例如,如果用户没有表的读取权限,但在函数内需要读取,则使用 SECURITY DEFINER 可以允许函数运行所需的读取权限。


受信任和不受信任的扩展

创建上面的扩展时,使用了plperl和plpython3。在大多数情况下,这些是正确使用的扩展。

这些扩展对服务器文件系统和系统调用的访问受到限制。

扩展也可以使用 u (plpython3u, plperlu)

创建

这些是不受信任的扩展,允许对服务器文件系统进行更多访问。

在某些情况下可能需要这样做,例如,如果您想使用 Perl 模块、Python 库或使用系统调用。

在上面的示例中,JSON 输出生成为字符串,如果需要,可以使用 perl JSON 模块将数据编码为 JSON。为此,需要使用不受信任的扩展来访问 JSON 模块。

建议不要使用不受信任的扩展,但如有必要,请谨慎使用并了解潜在风险。

如果正在使用 Perl,当使用不受信任的扩展时,Perl 将以污点模式运行。

最后的想法

能够利用 Perls 高级文本处理和内存管理,或者 PostgreSQL 中的 Python 数据分析库可能是一个非常强大的工具。

将复杂的任务交给更适合处理该任务的工具可以减少数据库的开销。

一如既往,在使用自定义函数和外部脚本语言时,请采取预防措施以确保安全使用。

版本聲明 本文轉載於:https://dev.to/mrpercival/creating-custom-functions-in-postgresql-52bn?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta:列偏移的刪除和恢復Bootstrap 4 在其Beta 1 版本中引入了重大更改柱子偏移了。然而,隨著 Beta 2 的後續發布,這些變化已經逆轉。 從 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    程式設計 發佈於2024-12-21
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-12-21
  • 插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入記錄時如何解決「一般錯誤:2006 MySQL 伺服器已消失」介紹:將資料插入MySQL 資料庫有時會導致錯誤「一般錯誤:2006 MySQL 伺服器已消失」。當與伺服器的連線遺失時會出現此錯誤,通常是由於 MySQL 配置中的兩個變數之一所致。 解決方案:解決此錯誤的關鍵是調整wait_tim...
    程式設計 發佈於2024-12-21
  • 儘管程式碼有效,為什麼 POST 請求無法擷取 PHP 中的輸入?
    儘管程式碼有效,為什麼 POST 請求無法擷取 PHP 中的輸入?
    解決PHP 中的POST 請求故障在提供的程式碼片段中:action=''而非:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"檢查$_POST陣列:表單提交後使用 var_dump 檢查 $_POST 陣列的內...
    程式設計 發佈於2024-12-21
  • 大批
    大批
    方法是可以在物件上呼叫的 fns 數組是對象,因此它們在 JS 中也有方法。 slice(begin):將陣列的一部分提取到新數組中,而不改變原始數組。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index ...
    程式設計 發佈於2024-12-21
  • 如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    在 PHP 中組合關聯數組在 PHP 中,將兩個關聯數組組合成一個數組是常見任務。考慮以下請求:問題描述:提供的代碼定義了兩個關聯數組,$array1 和 $array2。目標是建立一個新陣列 $array3,它合併兩個陣列中的所有鍵值對。 此外,提供的陣列具有唯一的 ID,而名稱可能重疊。要求是建...
    程式設計 發佈於2024-12-21
  • 如何在 Python 中存取和處理命令列參數?
    如何在 Python 中存取和處理命令列參數?
    在 Python 中處理命令列參數在 Python 中,命令列參數位於名為 sys.argv 的清單中。若要存取這些參數,請使用下列語法:import sys # Print all command line arguments print("\n".join(sys.argv)...
    程式設計 發佈於2024-12-20
  • Python If 語句中邏輯 AND (&&) 運算子的等價物是什麼?
    Python If 語句中邏輯 AND (&&) 運算子的等價物是什麼?
    Python中的If語句中&&(邏輯與)的等價是什麼? 在Python中,無法像其他程式語言一樣使用&&作為邏輯與運算子。使用if語句時,必須使用and關鍵字。 範例:以下範例嘗試使用&&作為邏輯與運算符,但會引發語法錯誤:if cond1 &amp;&amp; cond2:正確方法...
    程式設計 發佈於2024-12-20
  • 如何修改 Go 中作為函數參數傳遞的切片?
    如何修改 Go 中作為函數參數傳遞的切片?
    將切片作為函數參數傳遞並修改原始切片在Go 中,將參數傳遞給函數是按值完成的,這意味著對參數所做的任何更改函數內的內容不會反映在原始變數中。當使用可變資料類型(例如切片)時,這可能會出現問題,因為附加到函數內的切片不會影響超出函數範圍的原始切片。 考慮以下範例:nums := []int{1, 2,...
    程式設計 發佈於2024-12-20
  • 為什麼模板類別定義必須包含在頭檔中?
    為什麼模板類別定義必須包含在頭檔中?
    在頭文件中包含模板類定義:必要性問題是為什麼需要模板類的實現和聲明駐留在同一頭文件中。為了解決這個問題,至關重要的是要了解編譯器需要存取整個模板定義(而不僅僅是其簽名)才能為模板的每個實例化產生程式碼。因此,函數定義必須移至標題。 包含模型提供了對此要求的全面解釋。本質上,當實例化模板類別時,編譯器...
    程式設計 發佈於2024-12-20
  • 在 JavaScript 中使用浮點數時如何控制小數精確度?
    在 JavaScript 中使用浮點數時如何控制小數精確度?
    控制 JavaScript 中的小數精度在 JavaScript 中處理浮點數時,您可能會遇到需要控制小數點後顯示的位數的情況觀點。例如,您可能希望只顯示兩位小數的價格。 以固定精度格式化浮點型為了實現此目的,JavaScript 提供了 toFixed() 函數。此函數採用一個參數來指定要保留的小...
    程式設計 發佈於2024-12-20
  • 填充空 Python 清單時如何避免 IndexError?
    填充空 Python 清單時如何避免 IndexError?
    修復將元素分配給列表時的IndexError嘗試通過依次分配每個元素來創建列表時,您可能會遇到IndexError如果目標清單最初為空。出現此錯誤的原因是您試圖存取清單中不存在的索引。 要解決此問題並將元素正確添加到列表中,您可以使用追加方法:for l in i: j.append(l)此...
    程式設計 發佈於2024-12-20
  • 如何在 Android 中解析 ISO 8601 日期/時間字串?
    如何在 Android 中解析 ISO 8601 日期/時間字串?
    在Android 解析ISO 8601 日期/時間字串問題:您已收到來自Web 服務的標準ISO 8601字串,例如“2010-10-15T09:27:37Z”。如何在Android中將此字串轉換為日期/時間物件以進行進一步操作? 答案:Android提供了一個SimpleDateFormat類,讓...
    程式設計 發佈於2024-12-20
  • 如何使用正規表示式檢測 URL(包括裸 URL)?
    如何使用正規表示式檢測 URL(包括裸 URL)?
    使用正規表示式偵測 URL您目前的程式碼無法符合缺少「http://」前綴的裸 URL。為了解決這個問題,可以考慮採用綜合正規表示式:https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\ ~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9(...
    程式設計 發佈於2024-12-20
  • 如何在 AngularJS 中有效地求和數組屬性?
    如何在 AngularJS 中有效地求和數組屬性?
    AngularJS 中的高級數組求和在 AngularJS 中,對數組屬性求和可能是一項常見任務。基本方法包括迭代數組並累積屬性值。然而,當面對多個陣列和不同的屬性名稱時,這種方法變得乏味。 為了解決這個問題,需要一個更靈活、可重複使用的解決方案,它允許對任何陣列屬性進行方便的求和。這可以使用 re...
    程式設計 發佈於2024-12-20

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3