{"id":16640637,"url":"https://github.com/wgzhao/presto-udfs","last_synced_at":"2025-10-30T09:31:39.953Z","repository":{"id":47211554,"uuid":"185109969","full_name":"wgzhao/presto-udfs","owner":"wgzhao","description":"some useful User Defined Functions(UDF) for both PrestoSQL and TrinoDB","archived":false,"fork":false,"pushed_at":"2023-02-16T08:36:26.000Z","size":3609,"stargazers_count":14,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-03-23T03:36:39.805Z","etag":null,"topics":["exchange","presto","securities","trino","udf"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wgzhao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-06T02:28:07.000Z","updated_at":"2023-03-20T07:56:04.000Z","dependencies_parsed_at":"2023-01-20T03:15:16.720Z","dependency_job_id":null,"html_url":"https://github.com/wgzhao/presto-udfs","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgzhao%2Fpresto-udfs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgzhao%2Fpresto-udfs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgzhao%2Fpresto-udfs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgzhao%2Fpresto-udfs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wgzhao","download_url":"https://codeload.github.com/wgzhao/presto-udfs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219856474,"owners_count":16556082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["exchange","presto","securities","trino","udf"],"created_at":"2024-10-12T07:24:33.732Z","updated_at":"2025-10-30T09:31:33.776Z","avatar_url":"https://github.com/wgzhao.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Presto User-Defined Functions(UDFs)\n\nPresto/Trino 自定义函数，当前仅针对 [Trino](https://trino.io) 349及以后的版本有有效\n如果想编译兼容名为 `prestosql` 的版本，则可以执行\n\n```shell\nbash build_for_presto.sh\n```\n脚本。该脚本执行完后，会在当前目录下，生产一个 `udfs-\u003cversion\u003e-presto-348.zip` 的压缩文件。使用方式和下面描述一样。\n\n## 使用方法\n\n### 直接下载\n\n如果不想编译，可以从 `release` 页面中找到对应的版本的压缩文件下载到本地。或者你也可以按照以下方式进行编译\n\n### 自行编译\n\n下载代码并编译打包\n\n```shell\ngit clone https://github.com/wgzhao/presto-udfs\nmvn clean package assembly:single\n```\n\n执行上述指令后，将在 `target` 目录下，生成一个 `udfs-\u003cversion\u003e-release.zip` 的压缩包\n\n### 安装\n\n假定你的 Presto 安装  `/usr/lib/presto` 目下，执行下面的命令进行安装\n\n```shell\nunzip -q -o udfs-\u003cversion\u003e-release.zip -d /usr/lib/presto/plugin/\n```\n\n如果安装的是 Trino， 假定安装目录为  `/usr/lib/trino`， 则执行下面的命令：\n\n```shell\nunzip -q -o udfs-\u003cversion\u003e-release.zip -d /usr/lib/trino/plugin/\n```\n\n上面命令完成后，将会在安装目录的 `plugin/` 目录下创建一个 `extra` 目录，所有需要的jar文件均在该目录。\n\n重启 Presto/Trino 服务，连接 Presto/Trino 服务后，执行 `show functions like 'udf_%'` 将得到类似如下的输出\n\n|       Function       | Return Type |  Argument Types  | Function Type | Deterministic |                      Description  |\n----------------------|--------------|------------------|---------------|---------------|-----------------------------------------------------|\n| udf_add_normal_days  | varchar     | varchar, integer | scalar        | true          | add days from base date |\n| udf_add_trade_days   | varchar     | varchar, integer | scalar        | true          | add days from base date with yyyyMMdd format |\n| udf_bank_name        | varchar     | varchar          | scalar        | true          | return bank name for giving card number |\n| udf_bank_code        | varchar     | varchar          | scalar        | true          | return bank code for giving card number |\n| udf_ch2num           | bigint      | varchar          | scalar        | true          | convert chinese number to Arabia number |\n| udf_count_trade_days | integer     | varchar, varchar | scalar        | true          | count the number of trade date between given two dates |\n| udf_eval             | double      | varchar          | scalar        | true          | the implement of javascript eval function |\n| udf_get_birthday     | integer     | varchar          | scalar        | true          | Extract birthday from valid ID card |\n| udf_int2ip           | varchar     | integer          | scalar        | true          | get region from IP Address |\n| udf_ip2int           | bigint      | varchar          | scalar        | true          | get region from IP Address |\n| udf_ip2region        | varchar     | varchar          | scalar        | true          | get region from IP Address |\n| udf_ip2region        | varchar     | varchar, varchar | scalar        | true          | get region from IP Address |\n| udf_is_idcard        | boolean     | varchar          | scalar        | true          | Check IdCard is valid or not |\n| udf_is_trade_date    | boolean     | varchar          | scalar        | true          | is close day or not |\n| udf_last_trade_date  | varchar     | varchar          | scalar        | true          | get the last exchange day before specified date |\n| udf_max_draw_down    | double      | varchar          | scalar        | true          | get the max drawdown rate |\n| udf_num2ch           | varchar     | bigint           | scalar        | true          | convert Chinese number to Arabia number |\n| udf_num2ch           | varchar     | bigint, boolean  | scalar        | true          | convert Chinese number to Arabia number |\n| udf_num2ch           | varchar     | varchar          | scalar        | true          | convert Chinese number to Arabia number |\n| udf_num2ch           | varchar     | varchar, boolean | scalar        | true          | convert Chinese number to Arabia number |\n| udf_pmod             | bigint      | bigint, bigint   | scalar        | true          | Returns the positive value of a mod b. |\n| udf_pmod             | double      | double, double   | scalar        | true          | Returns the positive value of a mod b |\n| udf_to_chinese       | varchar     | varchar          | scalar        | true          | convert number string to chinese number string |\n| udf_to_chinese       | varchar     | varchar, boolean | scalar        | true          | convert number string to chinese number string |\n| udf_pinyin           | varchar     | varchar          | scalar        | true          | convert chinese to pinyin                      |\n| udf_xpath            | array(varchar(x)) | varchar(x), varchar(y) | scalar        | true          | Returns a string array of values within xml nodes that match the xpath\n| udf_xpath_boolean    | boolean           | varchar(x), varchar(y) | scalar        | true          | Evaluates a boolean xpath expression\n| udf_xpath_double     | double            | varchar(x), varchar(y) | scalar        | true          | Returns a double value that matches the xpath expression\n| udf_xpath_float      | double            | varchar(x), varchar(y) | scalar        | true          | Returns a double value that matches the xpath expression\n| udf_xpath_int        | bigint            | varchar(x), varchar(y) | scalar        | true          | Returns a long value that matches the xpath expression\n| udf_xpath_long       | bigint            | varchar(x), varchar(y) | scalar        | true          | Returns a long value that matches the xpath expression\n| udf_xpath_number     | double            | varchar(x), varchar(y) | scalar        | true          | Returns a double value that matches the xpath expression\n| udf_xpath_str        | varchar           | varchar(x), varchar(y) | scalar        | true          | Returns a string value that matches the xpath expression\n| udf_xpath_string     | varchar           | varchar(x), varchar(y) | scalar        | true          | Returns a string value that matches the xpath expression\n\n## 已经实现的 UDF\n\n### 数学函数\n\n#### udf_pmod(n, m) -\u003e [same as input]\n\n返回 n mod m 的值，商取正数\n\n```sql\nselect udf_pmod(17, -5) -- -3\n```\n\n#### num2ch(string str, [boolean flag]) -\u003e string , num2ch(long num, [ boolean flag]) -\u003e string\n\n把阿拉伯数字转为汉字数字，数字汉字有两种写法，一种是普通写法，一种是大写写法：比如 1 ,普通写作 `一`, 大写则为 `壹`。\n`flag` 用来指定采取何种写法，`true` 表示普通写法，`false` 表示大写写法，默认值为 `false`\n\n```sql\nselect udf_num2ch('103543'); -- 拾万叁仟伍佰肆拾叁\nselect udf_num2ch(103543, true); -- 十万三千五百四十三\nselect udf_num2ch(103_543); -- 十万三千五百四十三\nselect udf_num2ch(); -- NULL\n```\n\n### 字符串函数\n\n#### udf_ch2num(string str) -\u003e long, ch2num(long a) -\u003e long\n\n返回中文标记的数字的阿拉伯数字形式 ，如果传递字符串为空或包含非汉字数字，则返回为 NULL。\n\n```sql\nselect udf_ch2num('一十万三千五百四十三'); -- 103543\nselect udf_ch2num('壹拾万叁仟伍佰肆拾叁'); -- 103543\nselect udf_ch2num(''); -- NULL\nselect udf_ch2num(null); -- NULL\nselect udf_ch2num('abc'); -- NULL\n```\n\n**注意**:\n\n1. 目前实现的限制，如果是十万XXX这种形式会报错，必须写成一十万\n2. 但如果一个不合法的汉字数字，目前无法正确识别, 比如 `select udf_ch2num('拾万万'); -- 10` 得到的是一个不正确的结果\n\n#### udf_to_chinese(string str, [boolean flag] ) -\u003e string\n\n把数字字符串转为汉字字符串，注意它和 `udf_num2ch` 函数区别是本函数不含数量关系，即仅仅把每个阿拉伯数字转为中文汉字。 同样的，使用 `flag` 来区分是普通大写，还是汉字大写。true` 表示普通写法，`false` 表示大写写法，默认值为 `false`\n\n```sql\nselect udf_to_chinese('2002'); -- 贰零零贰\nselect udf_to_chinese('2002', false); -- 贰零零贰\nselect udf_to_chinese('2002', true); -- 二〇〇二\n```\n\n#### eval(string str) -\u003e double\n\n实现Javascript中eval函数的功能， 暂时仅支持 `+`，`-`，`*`， `/` 运算\n\n```sql\nselect udf_eval('4*(5+2)'); -- 28\nselect udf_eval(null); -- NULL\nselect udf_eval('ab'); -- NULL\n```\n\n#### udf_is_idcard(string id) -\u003e bool\n\n检测给定的身份证号码是否有效， 支持中国大陆身份证（15位和18位）以及港澳台的10位证件号码\n\n```sql\nselect udf_is_idcard(null); -- false\nselect udf_is_idcard('23070719391110007X'); -- true\nselect udf_is_idcard('230707391110007'); -- true\nselect udf_is_idcard('1234566'); -- false\n```\n\n#### get_birthday(string id) -\u003e int\n\n从有效的身份证号码中提取生日，如果身份证无效，则返回为0\n\n```sql\nselect udf_get_birthday(null); -- 0\nselect udf_get_birthday('23070719391110007X'); -- 19391110\nselect udf_get_birthday('230707391110007'); -- 19391110\nselect udf_get_birthday('1234566'); -- NULL\n```\n\n### IP 相关函数\n\n#### udf_ip2int(string ip) -\u003e int\n\n将有效的IP地址转为长整数表达法，如果指定的IP地址无效，则返回为0\n\n```sql\nselect udf_ip2int('127.0.0.1'); -- 2130706433\nselect udf_ip2int('0.0.0.0'); -- 0\nselect udf_ip2int('a.b.c.d'); -- 0\n```\n\n#### udf_int2ip(int a) -\u003e string\n\n将长整数表示的IP地址转为十进制字符出表达法，如果`a` 小于0 ，则返回为 NULL\n\n```sql\nselect udf_int2ip(185999660); -- 11.22.33.44\nselect udf_int2ip(0); -- 0.0.0.0\nselect udf_int2ip(-1); -- NULL\n```\n\n#### udf_ip2region(string ip, [string flag]) -\u003e string\n\n`ip2region` 实现了IP地址归属地以及国家对应的国际编码查询，国际编码定义来源于 [ISO 3166-1](https://zh.wikipedia.org/wiki/ISO_3166-1)。\n该函数带一个必选参数和一个可选参数，语义如下：\n\n```\nudf_ip2region(ip, [country|g|province|p|city|c|isp|i|en|m2|m3|digit])\n```\n\n必选参数 `ip` 表示要查询的 IP 地址，目前仅支持点分字符串IP地址格式，比如\n\n```sql\nselect udf_ip2region('119.29.29.29'); -- 中国|0|北京|北京市|腾讯\n```\n\n上述查询结果的层级用 `|` 分隔，从第一列开始，分别表示 `国家|区域|省份|城市|供应商(ISP)`\n\n如果对应的列没有值，则用 `0` 表示（注意：不是用`null`表示)\n\n比如下面的查询:\n\n```sql\nselect udf_ip2region('1.1.1.1'); -- 澳大利亚|0|0|0|0\n```\n\n则表示只有国家信息，其他信息缺失\n\n如果IP地址非法，则返回`null`，比如\n\n```sql\nselect udf_ip2region('a.b.c.d'); -- NULL\nselect udf_ip2region('1.1.1.'); -- NULL\n```\n\n这里的IP地址也可以网络地址，比如\n\n```sql\nselect udf_ip2region('119.29.29.0'); -- 中国|0|北京|北京市|腾讯\nselect udf_ip2region('119.29.0.0'); -- 中国|0|广东省|广州市|电信\n```\n\n第二个参数为可选参数，用来指定想要获取哪个层级的信息，每个定义有完整单词以及缩写，含义如下：\n\n- `country|g` 表示查询IP地址所在国家\n- `province|p` 表示查询IP地址所在省份/州/道\n- `city|c` 表示查询IP地址所在城市\n- `isp|i` 表示查询\n- `en` 返回IP地址表示国家英语名称，比如 `China`\n- `m2` 返回IP地址表示国家两位自字母代码，比如 `CN`\n- `m3` 返回IP地址表示国家三位自字母代码，比如 `CHN`\n- `digit` 返回IP地址表示国家数字代码，比如 `156`\n\n以下是一些查询例子\n\n```sql\nselect udf_ip2region('119.29.29.29', 'c'); -- 北京市\nselect udf_ip2region('8.8.8.8', 'g'); -- 美国\nselect udf_ip2region('223.5.5.5', 'i'); -- 阿里云\nselect udf_ip2region('1.1.1.1', 'en') -- Australia\nselect udf_ip2region('1.1.1.1', 'm2'); -- AU\nselect udf_ip2region('1.1.1.1', 'm3'); -- AUS\nselect udf_ip2region('1.1.1.1', 'digit'); -- 36\n```\n\n### mobile2region\n\n`mobile2region` 实现了手机号码归属地查询，该函数接受一个必选参数和一个可选参数，语义如下：\n\n```\nudf_mobile2region(tel, [province|p|city|c|isp|i])\n```\n\n第一个参数 `tel` 表示要查询的手机号码，第二个参数表示要返回的层级，含义如下：\n\n- `province|p` 表示查询IP地址所在省份/州/道\n- `city|c` 表示查询IP地址所在城市\n- `isp|i` 表示查询\n\n以下是一些查询例子\n\n```sql\nselect udf_mobile2region('13410774560'); -- 广东|深圳|中国移动\nselect udf_mobile2region('13011'); -- NULL\nselect udf_mobile2region('18945871234', 'p'); -- 黑龙江\nselect udf_mobile2region('18945871234', 'c'); -- 伊春\nselect udf_mobile2region('18945871234', 'i'); -- 中国电信\n```\n\n### 证券交易日相关函数\n\n这里的函数都和中国大陆证券交易日相关的函数，国内证券交易日符合以下条件\n\n1. 双休日和国家法定节假日必然不是交易日\n2. 调休中遇到双休日（比如周六要求上班）也不是双休日\n\n由于每年的调休不同，也就导致证券交易日没有固定的规律，需要有交易所在头一年年底下发到各券商，同时遇到一些特别情况，还有临时调整（比如2020年1月31日周五，农历初七，本应为交易日，但受疫情影响，调整为非交易日）。因此交易日之间的计算是证券相关数据分析必然会遇到的问题。下面的函数试图简化交易日期计算难度。\n\n#### udf_add_normal_days(string base_date, int n) -\u003e string\n\n计算在给定日期后的 n 天内的第一个交易日, 如果 n 是正数，则往后计算；如果是负数，则往前计算。\n\n该函数的计算分成两步：\n\n1. 在指定的日期 `base_date`上增加 `n` 天，得到一个日期 `delta_date`；\n\n第二步是找到不超过 `delta_date` 日期的最近交易日。\n\n```sql\nselect udf_add_normal_days('20210903', 4); -- 20210907\nselect udf_add_normal_days('20210904', 1); -- 20210903\nselect udf_add_normal_days('20210906', -1); -- 20210903\n```\n\n#### udf_count_trade_days(string d1, string d2) -\u003e int\n\n计算两个给定的第一个日期（包括）和第二个日期（包括）之间有多少个交易日，日期格式为 `yyyyMMdd`\n\n```sql\nselect udf_count_trade_days('20210901', '20210906'); -- 4\nselect udf_count_trade_days('20210906', '20210906'); -- 1\nselect udf_count_trade_days('20210903', '20210906'); -- 2\n\n```\n\n#### udf_add_trade_days(string base_date, int n) -\u003e string\n\n计算在超过指定日期(`base_date`)的最近交易日上增加 n 个交易日后的日期并返回。这个计算实际上两个步骤\n\n1. 找到不超过 `base_date` 日期最近的交易日（`base_date` 如果本身是交易日，则为自身），然后\n2. 增加 n 个交易日（注意：不是自然日），等于找到指定日期后几个交易日期\n\n```sql\nselect udf_add_trade_days('20210903', 1); -- 20210906\nselect udf_add_trade_days('20210904', 1); -- 20210906\nselect udf_add_trade_days('20210101', 3); -- 20210104\n```\n\n注意该函数于 `udf_add_normal_days` 的逻辑区别。\n\n#### udf_last_trade_trade(string d) -\u003e string\n\n获取指定日期(`d`)的上一 日 交易日并返回，如果没有找到，则返回 NULL.\n\n```sql\nselect udf_last_trade_date('20210906'); -- 20210903\nselect udf_last_trade_date('20210907'); -- 20210906\nselect udf_last_trade_date('19920101'); -- NULL\n```\n\n#### udf_is_trade_date(string d) -\u003e bool\n\n判断给定的日期是否为交易日，如果是，返回 true，其他情况返回 false\n\n```sql\nselect udf_is_trade_date(null); -- false\nselect udf_is_trade_date(''); -- false\nselect udf_is_trade_date('20210906'); -- true\nselect udf_is_trade_date('20210904'); -- false\nselect udf_is_trade_date('20210904'); -- false\n```\n\n### xpath 相关函数\n\n这是一组用来使用 `xpath` 表达式来分析 xml 字符串的函数，其代码来自 [Apache Hive](https://github.com/apache/hive/blob/master/ql/src/java/org/apache/hadoop/hive/ql/udf/xml/)\n\n具体的用法，可以参考 [LanguageManual XPathUDF](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+XPathUDF)\n\n要注意的是，相对 Hive 的函数名而言，这里的函数都增加了 `udf_` 前缀\n\n这里列出函数的基本用法\n\n```sql\nselect udf_xpath('\u003ca\u003e\u003cb\u003eb1\u003c/b\u003e\u003cb\u003eb2\u003c/b\u003e\u003c/a\u003e','a/*'); -- []\nselect udf_xpath('\u003ca\u003e\u003cb\u003eb1\u003c/b\u003e\u003cb\u003eb2\u003c/b\u003e\u003c/a\u003e','a/*/text()'); -- [b1, b2]\nselect udf_xpath('\u003ca\u003e\u003cb id=\"foo\"\u003eb1\u003c/b\u003e\u003cb id=\"bar\"\u003eb2\u003c/b\u003e\u003c/a\u003e','//@id'); -- [foot, bar]\nSELECT udf_xpath_string('\u003ca\u003e\u003cb\u003ebb\u003c/b\u003e\u003cc\u003ecc\u003c/c\u003e\u003c/a\u003e', 'a/b'); -- bb\nSELECT udf_xpath_string ('\u003ca\u003e\u003cb\u003ebb\u003c/b\u003e\u003cc\u003ecc\u003c/c\u003e\u003c/a\u003e', 'a'); -- bbcc\nSELECT udf_xpath_boolean ('\u003ca\u003e\u003cb\u003eb\u003c/b\u003e\u003c/a\u003e', 'a/b'); -- true\nSELECT udf_xpath_boolean ('\u003ca\u003e\u003cb\u003eb\u003c/b\u003e\u003c/a\u003e', 'a/c'); -- false\nSELECT udf_xpath_int('\u003ca\u003eb\u003c/a\u003e', 'a = 10'); -- 0\nSELECT udf_xpath_int('\u003ca\u003e\u003cb class=\"odd\"\u003e1\u003c/b\u003e\u003cb class=\"even\"\u003e2\u003c/b\u003e\u003cb class=\"odd\"\u003e4\u003c/b\u003e\u003cc\u003e8\u003c/c\u003e\u003c/a\u003e', 'sum(a/*)'); -- 15\n```\n\n### 银行卡相关函数\n\n主要是根据给定的银行卡，获得对应的开户行名称以及编号\n\n```sql\nselect udf_bank_name('621700292010'); -- 中国建设银行\nselect udf_bank_name(''); -- NULL\nselect udf_bank_code('621768160266'); -- CITIC\nselect udf_bank_code(''); -- NULL\n```\n\n### 拼音函数\n\n把中文转换为拼音小写字母，多音字只返回常用的拼音\n\n```sql\nselect udf_pinyin('中文'); -- zhongwen\nselect udf_pinyin('中'); -- zhong\nselect udf_pinyin(''); -- ''\nselect udf_pinyin(); -- exception occurred\n```\n\n## 注意事项\n\n`src/main/resources/closedate.dat.gz` 文件存储的是从 2000 年开始到当年的所有交易日日期，每行一个日期。因此每到年底，需要将交易所下发的来年交易日追加到该文件中，并重新打包发布到生产环境。\n\n如果中途有交易日变更，也需要执行上述操作。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwgzhao%2Fpresto-udfs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwgzhao%2Fpresto-udfs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwgzhao%2Fpresto-udfs/lists"}