【问题标题】:Using JSONB_ARRAY_ELEMENTS with WHERE ... IN condition将 JSONB_ARRAY_ELEMENTS 与 WHERE ... IN 条件一起使用
【发布时间】:2016-06-10 23:35:16
【问题描述】:

在线扑克玩家可以选择购买游戏室 1 或游戏室 2 的访问权限。

他们可以因作弊而被暂时禁止。

CREATE TABLE users (
        uid SERIAL PRIMARY KEY,

        paid1_until timestamptz NULL,     -- may play in room 1
        paid2_until timestamptz NULL,     -- may play in room 2

        banned_until timestamptz NULL,    -- punished for cheating etc.
        banned_reason varchar(255) NULL
);

这里上表填写了4条测试记录:

INSERT INTO users (paid1_until, paid2_until, banned_until, banned_reason)
VALUES (NULL, NULL, NULL, NULL),
       (current_timestamp + interval '1 month', NULL, NULL, NULL),
       (current_timestamp + interval '2 month', current_timestamp + interval '4 month', NULL, NULL),
       (NULL, current_timestamp + interval '8 month', NULL, NULL);

所有 4 条记录都属于同一个人 - 通过不同的社交网络(例如通过 Facebook、Twitter、Apple Game Center 等)验证了自己的身份

我正在尝试创建一个存储函数,该函数将采用数字用户 ID 列表(作为 JSON 数组)并将属于同一个人的记录合并到一条记录中 - 而不会丢失她的付款或惩罚:

CREATE OR REPLACE FUNCTION merge_users(
        IN in_users jsonb,
        OUT out_uid integer)
        RETURNS integer AS
$func$
DECLARE
        new_paid1 timestamptz;
        new_paid2 timestamptz;
        new_banned timestamptz;
        new_reason varchar(255);
BEGIN
        SELECT min(uid),
                current_timestamp + sum(paid1_until - current_timestamp),
                current_timestamp + sum(paid2_until - current_timestamp),
                max(banned_until)
        INTO
                out_uid, new_paid1, new_paid2, new_banned
        FROM users 
        WHERE uid IN (SELECT JSONB_ARRAY_ELEMENTS(in_users));

        IF out_uid IS NOT NULL THEN
                SELECT banned_reason
                INTO new_reason
                FROM users
                WHERE new_banned IS NOT NULL
                AND banned_until = new_banned
                LIMIT 1;

                DELETE FROM users
                WHERE uid IN (SELECT JSONB_ARRAY_ELEMENTS(in_users))
                AND uid <> out_uid;

                UPDATE users 
                SET paid1_until = new_paid1,
                    paid2_until = new_paid2,
                    banned_until = new_banned,
                    banned_reason = new_reason
                WHERE uid = out_uid;
        END IF; 
END
$func$ LANGUAGE plpgsql;

不幸的是,它的使用导致以下错误:

# TABLE users;
 uid |          paid1_until          |          paid2_until          | banned_until | banned_reason 
-----+-------------------------------+-------------------------------+--------------+---------------
   1 |                               |                               |              | 
   2 | 2016-03-27 19:47:55.876272+02 |                               |              | 
   3 | 2016-04-27 19:47:55.876272+02 | 2016-06-27 19:47:55.876272+02 |              | 
   4 |                               | 2016-10-27 19:47:55.876272+02 |              | 
(4 rows)

# select merge_users('[1,2,3,4]'::jsonb);
ERROR:  operator does not exist: integer = jsonb
LINE 6:         WHERE uid IN (SELECT JSONB_ARRAY_ELEMENTS(in_users))
                          ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:  SELECT min(uid),
                current_timestamp + sum(paid1_until - current_timestamp),
                current_timestamp + sum(paid2_until - current_timestamp),
                max(banned_until)
                                                                               FROM users 
        WHERE uid IN (SELECT JSONB_ARRAY_ELEMENTS(in_users))
CONTEXT:  PL/pgSQL function merge_users(jsonb) line 8 at SQL statement

请帮我解决问题。

为了您的方便,这里是a gist with SQL code

【问题讨论】:

  • 您使用jsonb 数组是否有特定原因?为什么不使用int[] 数组?
  • 是的,我想使用 JSON,因为这是我的移动应用程序与 PHP + PostrgreSQL 后端对话的方式。我确定我的问题可以用 JSON 解决,我只是想念 smth。次要的。

标签: postgresql select where-in postgresql-9.5 postgresql-json


【解决方案1】:

jsonb_array_elements() 的结果是一组 jsonb 元素,因此您需要使用to_jsonb() 函数将uid 显式转换为jsonb,IN 将替换为&lt;@运营商:

WITH t(val) AS ( VALUES
  ('[1,2,3,4]'::JSONB)
)
SELECT TRUE
FROM t,jsonb_array_elements(t.val) element
WHERE to_jsonb(1) <@ element;

对于您的情况,应将 sn-p 调整为:

...SELECT ...,JSONB_ARRAY_ELEMENTS(in_users) user_ids
WHERE to_jsonb(uid) &lt;@ user_ids...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    • 2011-10-20
    • 2016-08-11
    • 1970-01-01
    相关资源
    最近更新 更多