相关文章推荐

基本规则

引号、反引号

在 PostgreSQL 中,单引号 AS 'col1' 和反引号 `` 都不支持,要么去掉,要么用双引号代替。

在 Java 的 MyBitis xml 文件中,如果返回结果是 Map 结构,并且返回字段用 AS 转为驼峰,则必须用双引号包起来,否则返回后会转换为小写,如

<select id="getWaitAuditPurchaseReturn" resultType="java.util.Map">
select
coalesce(count(1),0) as "waitAuditNum",
min(prepared_bill_time) as "preparedBillMintime"
...

WHERE 中字段值的大小写与实际数据不一致

-- 这样在 MySQL 可以正常查询出来结果,但是在 PostgreSQL 中查询不到
-- 表中存的实践数据是大写的 MATERIAL_USE_TYPE
select id from system_user_privilege where tableAlias.privilege_type = 'material_use_type'

-- 可以用 UPPER 或 LOWER 函数处理一下
select id from system_user_privilege where tableAlias.privilege_type = UPPER('material_use_type')

WHERE 后面用 ||

如果有,替换成 OR ,否则对应的过滤条件不生效。

因为在 PG 中 || 通常只用于字符串连接。同时它也可以连接数组、行、列等,其行为将取决于这些数据类型的定义和 PostgreSQL 中的类型转换规则。

类型错误

PostgreSQL 是强类型的,不同类型的字段进行比较时需要显示的转换成同一类型,否则会报错;而 MySQL 中会自动进行转换。

转换时候可以使用 cast() 函数或 :: 符号。

-- MySQL
AND (DATE(NOW()) - DATE(wb.start_time)) > ss.value

-- 上面的写法,pg 报错:nested exception is org.postgresql.sdsdf.il.PSQLException: ERROR: operator does not exist: integer > text

-- PostgreSQL
AND (DATE(NOW()) - DATE(wb.start_time)) > ss.value::integer

NULL 值的默认排序规则

在 PostgreSQL 中,NULL 值的排序规则与 MySQL 不一致,需要手动指定,才能与原 MySQL 排序规则一致

-- 降序 NULL 值放最后显示
ORDER BY material_code DESC NULLS LAST

-- 升序 NULL 值放最前面显示
ORDER BY material_code ASC NULLS FIRST

默认排序行为差异

下面这段 SQL

SELECT * FROM sys_dict_data ORDER BY dict_sort ASC

查询到的结果中 dict_sort 都是 0,这种情况的查询结果 MySQL 和 PostgreSQL 的默认处理方式会存在差异:

  • MySQL会再按照表中的主键 id 进行排序;
  • PostgreSQL 则会根据记录在表中的物理存储顺序排序;
  • 这种差异会导致两边排序不一致,在 PostgreSQL 中需要明确指定再按 id 排序,以保证与 MySQL 的默认行为一致。

    FOR UPDATE 不能跟着 GROUP BY 一起使用

    -- 下面这样写会报错:Cause: org.postgresql.util.PSQLException: ERROR: FOR UPDATE is not allowed with GROUP BY clause
    WHERE
    tableAlias.id IN ( '' )
    GROUP BY tableAlias.id
    FOR UPDATE
    -- 再包一层,外层加 FOR UPDATE
    SELECT t.* FROM (
    -- .....
    GROUP BY tableAlias.id
    ) t
    WHERE
    t.id IN ( '' )
    FOR UPDATE

    聚合函数不能嵌套

    -- wa.warehouse_area_type 不在后面的 GROUP BY 里,所以需要加聚合函数
    -- 下面这样写,会报错:### Cause: org.postgresql.util.PSQLException: ERROR: aggregate function calls cannot be nested
    SELECT
    -- .....
    SUM(CASE WHEN MIN(wa.warehouse_area_type) = 'STORE_AREA' THEN COALESCE(wsb.frozen_stock_num, 0) ELSE 0 END) AS occupyNum
    -- .....
    -- 使用子查询,外层 sum ,内层使用 case...when 计算明细
    SELECT
    -- .....
    SUM(occupyNum) AS occupyNum,
    -- .....
    FROM (
    SELECT
    -- .....
    (CASE WHEN wa.warehouse_area_type = 'STORE_AREA' THEN COALESCE(wsb.frozen_stock_num,0) ELSE 0 END) AS occupyNum
    -- .....
    FROM warehouse_stock_batch wsb
    ) t
    GROUP BY
    -- .....

    聚合函数构造的字段,不能直接跟在 HAVING 后面

    -- 下面这样写会报错:column "custommd5" does not exist LINE 56:  having customMd5 in ('')
    SELECT
    -- .....
    -- customMd5 是由聚合函数 CONCAT_WS() 构造出来的
    MD5(CONCAT_WS('_', xx, xx, xx,xx)) AS customMd5
    FROM warehouse_stock_batch wsb
    GROUP BY
    -- .....
    HAVING
    customMd5 IN ('')

    需要像下面这样再包一层,或者把聚合的字段在 having 中再写一遍,这种适合聚合的内容不多的场景。

    SELECT t.* FROM (
    SELECT
    -- .....
    md5(concat_ws('_', xx, xx, xx,xx)) as customMd5
    -- .....
    FROM warehouse_stock_batch wsb
    GROUP BY
    -- .....
    ) t
    WHERE
    t.customMd5 IN ('')

    GROUP BY 区分大小写

    MySQL 中 GROUP BY 的字段默认不区分大小写,PG 中严格区分,会造成数据行数不一致。

    常用函数替换

    like –> ilike

    PostgreSQL 中 like 默认区分大小,直接改成 ilike 以与 MySQL 保持一致。经测试,二者性能差异不大。

    group_concat –> string_agg

    -- MySQL
    GROUP_CONCAT(DISTINCT c.hospital_area_id SEPARATOR ',') AS hospital_area_id

    -- PostgreSQL
    STRING_AGG(DISTINCT c.hospital_area_id, ',') AS hospital_area_id

    ifnull –> coalesce

    -- MySQL
    IFNULL(sn.price, 0)

    -- PostgreSQL
    COALESCE(sn.price, 0)

    date_format –> to_char

    -- MySQL
    SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s')

    -- PostgreSQL
    SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS')

    date_add

    -- MySQL 返回 2023-03-03
    SELECT DATE_ADD(CURDATE(), INTERVAL 1 DAY)

    -- PostgreSQL
    SELECT TO_CHAR(CURRENT_DATE::TIMESTAMP + '1 day', 'YYYY-MM-DD')

    last_day –> date_trunc

    -- MySQL 中获取某个时间所在月的最后一天,返回 2023年03月31日
    select DATE_FORMAT(LAST_DAY('2023-03-02'), '%Y年%m月%d日')

    -- PostgreSQL
    SELECT TO_CHAR(DATE_TRUNC('month', '2023-03-02'::TIMESTAMP) + interval '1 month - 1 day', 'YYYY年MM月DD日')

    date_iff

    -- MySQL 返回相差的天数
    SELECT DATEDIFF('2023-03-31 23:59:59', NOW())

    -- PostgreSQL
    SELECT DATE('2023-03-31 23:59:59') - DATE(NOW())

    if –> case when

    -- MySQL
    SUM( IF(in_out_flag = 'OUT', material_amount, 0) ) AS out_house_amount

    -- PostgreSQL
    SUM( CASE WHEN in_out_flag = 'OUT' THEN material_amount ELSE 0 END ) AS out_house_amount

    json_extract –> json_extract_path_text

    -- MySQL
    SELECT JSON_EXTRACT(`json_col1`, '$[*].financeKind') AS v

    -- PostgreSQL
    SELECT json_extract_path_text(json_array_elements(json_col1::JSON), 'financeKind') AS v

    find_in_set –> any

    -- MySQL
    SELECT 1 FROM material_kind mk
    WHERE find_in_set(mk.id, 'id1,id2,id3,id4,id5')

    -- PostgreSQL
    SELECT 1 FROM material_kind mk
    WHERE mk.id = any(string_to_array('id1,id2,id3,id4,id5'))

    其它常用代码片段

    REPLACE INTO 转成 INSERT INTO … ON CONFLICT … DO UPDATE

    -- MySQL
    REPLACE INTO `version_info` (`id`, `version_no`, `platform`, `update_by`, `update_date`, `update_desc`)
    VALUES (2, 'v1.17.4.2', 'SPD', '16094bcb5000004', now(), '生产');

    -- PostgreSQL
    INSERT INTO version_info (id, version_no, platform, update_by, update_date, update_desc)
    VALUES (2, 'v1.17.4.2', 'SPD', '16094bcb5000004', NOW(), '生产')
    ON CONFLICT (id)
    DO UPDATE SET version_no = 'v1.17.4.2', platform = 'SPD', update_by = '16094bcb5000004', update_date = NOW(), update_desc = '生产';

    INSERT IGNORE INTO 转成 ON CONFLICT DO NOTHING

    -- MySQL
    INSERT IGNORE INTO `cloud_sys_role_menu` (`role_id`, `menu_id`) VALUES ('100', '2xt807qn3g1lymlrx');

    -- PostgreSQL
    INSERT INTO cloud_sys_role_menu (role_id, menu_id) VALUES ('100', '2xt807qn3g1lymlrx') ON CONFLICT DO NOTHING;

    给表添加新字段

    -- MySQL
    ALTER TABLE `department_limit_audit_his`
    ADD COLUMN `department_practical_amount` decimal(19, 6) NULL DEFAULT NULL COMMENT '科室实际收货量' AFTER `limit_value`;

    -- PostgreSQL
    -- 1. 添加字段,多个用逗号间隔,如:ADD COLUMN col1, ADD COLUMN col2, ...;
    ALTER TABLE department_limit_audit_his
    ADD COLUMN department_practical_amount NUMERIC(19, 6) DEFAULT NULL;
    -- AFTER limit_value;

    -- 2. 给字段添加注释
    COMMENT ON COLUMN material_charge.medical_advice_stage IS '科室实际收货量';

    -- 3. 调整位置
    -- 暂时没发现好的实现方式
     
    推荐文章