PostgreSQL 务实应用(五/5)常用表达

在实际应用中,对于具体的数据计算我们会找相应的函数来实现。而计算需求不同的表达,往往会使得我们使用不同的函数或方式来实现。或者也可以说,同一计算可以使用多种不同的表达方式实现。

PostgreSQL 提供非常丰富的类型及函数,一些常用的函数可参考此文


以下我们通过实例来展示几个常用的函数表达场景

首先,我们创建示例数据如下

CREATE TABLE table_test (
    id int PRIMARY KEY,    
    test_name varchar(10),  -- 名称
    group_code varchar(10), -- 组号
    create_at timestamp,    -- 创建时间
    status bool,            -- 状态
    test_desc varchar(100)  -- 描述
);

-- 插入以下示例数据
INSERT INTO table_test (id, test_name, group_code, create_at, status, test_desc)
 VALUES 
(1, '一号', '01', now(), true, '评为A'),
(2, '一号', '01', now(), true, '评为A'),
(3, '二号', '02', now(), true, '评为B'),
(4, '三号', '01', now(), null, '评为AC'),
(5, '四号', '03', now(), true, '评为C'),
(6, '五号', '03', now(), true, '评为AB');

NULL 相关

当查询条件中相关字段存在 NULL 值时,如果直接使用 WHERE 字段 = @参数值,则怎么都匹配不了为 NULL 值的记录,因为NULL=任何值,包括 NULL=NULL 本身都不会返回 true,只会仍然是 NULL。

此时,可以将 NULL 进行转换,当字段值为 NULL 时,转换为一个默认值。

使用 coalesce() 可以表达此功能,它的功能是返回参数列表(支持不限定数量的参数)中,第一个非空值。

那如果我们需要根据 status (包含 NULL 值,NULL时默认 false)来查询,参数值可能是 true/false,则可以表达为 coalesce(status, false),当 status 为 NULL 时,自然返回的就是后边的 false。

此时:coalesce(status, false) = (case when status is null then false else status end)

-- status 中存在 NULL 值,以下参数可能为 true/false
SELECT * FROM table_test 
 WHERE coalesce(status, false) = ?;

SELECT * FROM table_test 
 WHERE (case when status is null then false else status end) = ?;

时间相关

(1)当前时间及时间格式化输出

函数 now() 取得当前系统时间,等同于系统变量 CURRENT_TIMESTAMP

使用 to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS US') 可以格式化时间,格式符含义如下:

YYYY 年,MM 月,DD 日,HH24 二十四小时制的时,MI 分,SS 秒, US 微秒

SELECT now(), CURRENT_TIMESTAMP, to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS US')
-- 输入结果 
"2019-04-21 23:24:45.276729+08" 
"2019-04-21 23:24:45.276729+08" 
"2019-04-21 23:24:45 276729"

(2)时间差

两个时间相差的秒数使用:

extract(epoch FROM ( 时间1 - 时间2 ))

通过该函数,我们可以制作一个用于表示时间已过去多久的函数。

CREATE OR REPLACE FUNCTION public.f_time_span(
    t timestamp without time zone)
    RETURNS character varying
    LANGUAGE 'plpgsql' 
AS $$

DECLARE 
    lv_span_double double precision; 
    lv_span int8;
BEGIN
    -- 取得当前时间与参数时间 相差的秒数
    SELECT extract(epoch FROM (now() - t )) into lv_span_double;
    -- 转换为整型
    lv_span := cast(lv_span_double as int8);

    IF lv_span < 11 THEN
       RETURN '几秒前';
    END IF;

    IF lv_span < 60 then
       RETURN lv_span || ' 秒前';
    END IF;

    IF lv_span < 3600 THEN
       RETURN (lv_span / 60) || ' 分钟前';
    END IF;       

    IF lv_span < 86400 THEN
       RETURN (lv_span / 3600) || ' 小时前';
    END IF;      

    IF lv_span < 30 * 86400 THEN
       RETURN (lv_span / 86400) || ' 天前';
    END IF;    

    IF lv_span < 365 * 86400 THEN
       RETURN (lv_span / (30*86400)) || ' 月前';
    END IF;

    RETURN (lv_span / (365*86400)) || ' 年前';
END 
$$;

我们应用于查询中,如作为创建时间隔现在多久的一个描述

SELECT test_name, create_at, f_time_span(create_at) span_desc FROM table_test
image

UUID 相关

使用 UUID,需要安装扩展 uuid-ossp,扩展安装成功以后,就可以通过 uuid_generate_v4() 或 uuid_generate_v1() 取得 UUID。

CREATE EXTENSION "uuid-ossp";

SELECT uuid_generate_v4()
-- 输出 "da28ce8a-ca9b-483f-918e-dce96fe7137f"

聚合相关

PostgreSQL 提供了除 SUM, COUNT, MAX, MIN, AVG 等之外更多的便捷的聚合函数。

string_agg(表达式, '分隔符') -- 按聚合的表达式值用分隔符连接成一串文本

array_agg(表达式) -- 将聚合的表达式值形成一个数组

如示例数据中,我们希望返回每一个组号内的名称列表,则可以:

SELECT GROUP_CODE,
      string_agg(test_name, ','), 
      array_agg(test_name) 
  FROM table_test GROUP BY GROUP_CODE

得到结果如下图

其它补充

(1)MD5 加密

在用户信息表中,密码信息通常不明文存储,有一种方式是使用密码的 MD5 进行单向加密存储从而保密性更强,PostgreSQL 想人之所想,提供了直接的 md5 函数,md5(text) 直接得到 32 位的MD5 加密结果。

(2)正则相关

PostgreSQL 支持正则表达式,这为查询提供了极大的灵活性与表达空间。

语法格式为:'字符串' ~ '正则表达式',含义类似 IsMatch('字符串' ,'正则表达式')

~* 则表示忽略大小写,!~ 或 !~* 表示否定

也可以使用 not '字符串' ~ '正则表达式' 表示否定

-- test_desc 包含 B 或 C 的
SELECT * FROM table_test  WHERE test_desc ~ 'B|C';
-- test_desc 包含ABC中两个字母相邻的
SELECT * FROM table_test  WHERE test_desc ~ '[A-C]{2}';
-- test_desc A结尾的
SELECT * FROM table_test  WHERE test_desc ~ 'A$';
-- test_desc 不是A结尾的
SELECT * FROM table_test  WHERE test_desc !~ 'A$';

PostgreSQL 务实应用(一/5)树形层级
PostgreSQL 务实应用(二/5)插入冲突
PostgreSQL 务实应用(三/5)分表复制
PostgreSQL 务实应用(四/5)JSON
PostgreSQL 务实应用(五/5)常用表达

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356