使用连接查询 JSONB 列以通过引用外部查询的子查询进行过滤

Query JSONB column using joins to filter by subquery referencing outer query

我需要分析调查数据(存储在记录中),其中一个问题可以有多个选项。我的目标是找出不在该问题允许选项范围内的答案。但是,我的查询 return 包含所有内容(我怀疑是子查询),我不知道如何修复它。

架构

记录将其数据存储在 data JSONB 列中。在那里,键是 question UID,例如uid00000006 有答案 option1option1 是对 select 的选择。 (并非所有问题都需要下拉列表,因此其他一些值也可以,例如 42。)

{"uid00000006": {"value": "option1"}, "uid00000008": {"value": 42}}

A question 可选地引用 optionset(下拉列表),其范围为 optionvalues(下拉列表的值),例如option1option2option3

create table record
(
    recordid bigint not null primary key,
    uid varchar(11) unique,
    data jsonb default '{}'::jsonb not null
);


create table question
(
    questionid bigint not null primary key,
    uid varchar(11) not null unique,
    optionsetid bigint 
);

create table optionset
(
    optionsetid bigint not null primary key,
    uid varchar(11) not null unique
);

create table optionvalue
(
    optionvalueid bigint not null primary key,
    uid varchar(11) not null unique,
    code varchar(230) not null,
    optionsetid bigint
);



-- create optionset
INSERT INTO optionset (optionsetid, uid) VALUES (1, 'uid00000001');

-- insert optionvalues into optionset
INSERT INTO optionvalue (optionvalueid, uid, code, optionsetid) VALUES (100, 'uid00000002', 'option1', 1);
INSERT INTO optionvalue (optionvalueid, uid, code, optionsetid) VALUES (101, 'uid00000003', 'option2', 1);
INSERT INTO optionvalue (optionvalueid, uid, code, optionsetid) VALUES (102, 'uid00000004', 'option3', 1);
INSERT INTO optionvalue (optionvalueid, uid, code, optionsetid) VALUES (103, 'uid00000005', 'option4', 1);

-- insert questions
INSERT INTO question (questionid, uid, optionsetid) VALUES (1001, 'uid00000006', 1);
INSERT INTO question (questionid, uid, optionsetid) VALUES (1002, 'uid00000007', 1);
INSERT INTO question (questionid, uid, optionsetid) VALUES (1003, 'uid00000008', NULL);

-- insert records
INSERT INTO record (recordid, uid, data) VALUES (10001, 'uid00000009', '{"uid00000006": {"value": "option1"}, "uid00000008": {"value": 42}}'::jsonb);
INSERT INTO record (recordid, uid, data) VALUES (10002, 'uid00000010', '{"uid00000006": {"value": "option2"}}'::jsonb);
INSERT INTO record (recordid, uid, data) VALUES (10003, 'uid00000011', '{"uid00000006": {"value": "UNMAPPED"}}'::jsonb);

我的查询

我起草的查询是:

SELECT r.uid             AS record_uid,
       key               AS question_uid,
       os.uid            AS optionset_uid,
       value ->> 'value' AS value
FROM record r, JSONB_EACH(r.data)
JOIN question q ON q.uid = key
JOIN optionset os ON q.optionsetid = os.optionsetid
WHERE q.optionsetid IS NOT NULL
AND value::varchar NOT IN (SELECT DISTINCT code FROM optionvalue WHERE optionsetid = q.optionsetid)
;

DBFiddle

问题

上面的查询 returns 所有记录而不是只有一个。参考示例数据,预期结果将是 return 仅值为 UNMAPPED 的记录(这意味着它是给出的答案不是“有效”的记录)。

您应该将 value::varchar NOT IN 更改为 value ->> 'value' NOT IN

SELECT
  r.uid             AS record_uid,
  key               AS question_uid,
  os.uid            AS optionset_uid,
  value ->> 'value' AS value
FROM
  record r, jsonb_each(r.data)
  JOIN question q ON q.uid = key
  JOIN optionset os ON q.optionsetid = os.optionsetid
WHERE 
 q.optionsetid IS NOT NULL
 AND value ->> 'value' NOT IN (SELECT DISTINCT code FROM optionvalue WHERE optionsetid = q.optionsetid);