如何包含不符合条件的行?
How do I include rows that don't meet the criteria?
我知道我可能只是想错了。我有以下结构:
CREATE TABLE mytable (
id serial PRIMARY KEY
, employee text UNIQUE NOT NULL
, data jsonb
);
以及以下数据:
INSERT INTO mytable (employee, data)
VALUES
('Jim', '{"sales": [{"value": 10, "yr": "2010"}, {"value": 5, "yr": "2011"}, {"value": 40, "yr": "2012"}]}'),
('Rob', '{"sales": [{"value": 10, "yr": "2009"}, {"value": 5, "yr": "2010"}, {"value": 41, "yr": "2011"}]}')
我正在尝试 return 所有员工及其 2012 年的 "value" 销售额。如果 2012 年没有销售额,则 return "No Data"。我有:
SELECT id, employee,
coalesce((SELECT s.value AS value FROM mytable, jsonb_to_recordset(mytable.data->'sales') AS s(yr text, value float)
WHERE s.yr='2012'), 0) AS b FROM mytable
我得到:
id |employee |b
53 |Jim |40
54 |Rob |40
'Rob' 的值是错误的。应该是'No Data'。 (我使用 0 作为合并的第二个参数,因为出现错误 "invalid input syntax for type double precision: 'No Data'"
问题是您的 inner SELECT
实际上并没有过滤员工的 ID。因此,Jim 的 40 也被选为 Rob。
如果您插入另一个具有 2012 年值的员工,实际上更容易看到。
你会得到
ERROR: 21000: more than one row returned by a subquery used as an expression
因为 SELECT value FROM ... WHERE yr = '2012'
会 return 多个值,即您(基本上)会要求(假设第二个员工有 41 个销售额)
SELECT COALESCE((VALUES (40), (41)));
您可以使用 CTE(虽然可能不是最有效的方法):
WITH sales_2012 AS (
SELECT id, s.value
FROM mytable,
jsonb_to_recordset(mytable.data->'sales') AS s(yr text, value float)
WHERE s.yr='2012'
)
SELECT employee, COALESCE(value, 0)
FROM mytable
LEFT OUTER JOIN sales_2012
ON mytable.id = sales_2012.id
;
┌──────────┬────────┐
│ employee │ value │
├──────────┼────────┤
│ Jim │ 40 │
│ Rob │ 0 │
└──────────┴────────┘
关键元素是使用LEFT JOIN LATERAL
而不是隐含的CROSS JOIN LATERAL
作为缩写只有一个逗号被解释。
Call a set-returning function with an array argument multiple times
查询可以简单地是:
SELECT t.id, t.employee, s.*
FROM mytable t
LEFT JOIN LATERAL jsonb_to_recordset(t.data->'sales')
AS s(yr int, value int) ON s.yr = 2012;
我们可以立即方便地挑选 yr = 2012
的销售人员,而不会因结果而流失员工。
要用 'NO Data' 美化,value
列必须是匹配的字符串类型:
SELECT t.id, t.employee
, COALESCE(s.yr, 2012) AS yr
, COALESCE(s.value, 'No Data') AS value
FROM mytable t
LEFT JOIN LATERAL jsonb_to_recordset(t.data->'sales')
AS s(yr int, value text) ON s.yr = 2012;
基于这些缺失的(可能的)细节:
- table 中每个员工恰好一行:
empoyee
是 UNIQUE NOT NULL
。
每个员工在 data
中可以有 0-n 年的销售额 - data
可以为 NULL。
yr
和 value
存储有效整数。否则调整类型。
正确的table定义:
CREATE TABLE mytable (
id serial PRIMARY KEY
, employee text UNIQUE NOT NULL
, data jsonb
);
什么是 LATERAL JOIN
?
根据评论中的要求,这些链接应该有所帮助,尤其是初学者的第一个链接:
我知道我可能只是想错了。我有以下结构:
CREATE TABLE mytable (
id serial PRIMARY KEY
, employee text UNIQUE NOT NULL
, data jsonb
);
以及以下数据:
INSERT INTO mytable (employee, data)
VALUES
('Jim', '{"sales": [{"value": 10, "yr": "2010"}, {"value": 5, "yr": "2011"}, {"value": 40, "yr": "2012"}]}'),
('Rob', '{"sales": [{"value": 10, "yr": "2009"}, {"value": 5, "yr": "2010"}, {"value": 41, "yr": "2011"}]}')
我正在尝试 return 所有员工及其 2012 年的 "value" 销售额。如果 2012 年没有销售额,则 return "No Data"。我有:
SELECT id, employee,
coalesce((SELECT s.value AS value FROM mytable, jsonb_to_recordset(mytable.data->'sales') AS s(yr text, value float)
WHERE s.yr='2012'), 0) AS b FROM mytable
我得到:
id |employee |b
53 |Jim |40
54 |Rob |40
'Rob' 的值是错误的。应该是'No Data'。 (我使用 0 作为合并的第二个参数,因为出现错误 "invalid input syntax for type double precision: 'No Data'"
问题是您的 inner SELECT
实际上并没有过滤员工的 ID。因此,Jim 的 40 也被选为 Rob。
如果您插入另一个具有 2012 年值的员工,实际上更容易看到。
你会得到
ERROR: 21000: more than one row returned by a subquery used as an expression
因为 SELECT value FROM ... WHERE yr = '2012'
会 return 多个值,即您(基本上)会要求(假设第二个员工有 41 个销售额)
SELECT COALESCE((VALUES (40), (41)));
您可以使用 CTE(虽然可能不是最有效的方法):
WITH sales_2012 AS (
SELECT id, s.value
FROM mytable,
jsonb_to_recordset(mytable.data->'sales') AS s(yr text, value float)
WHERE s.yr='2012'
)
SELECT employee, COALESCE(value, 0)
FROM mytable
LEFT OUTER JOIN sales_2012
ON mytable.id = sales_2012.id
;
┌──────────┬────────┐
│ employee │ value │
├──────────┼────────┤
│ Jim │ 40 │
│ Rob │ 0 │
└──────────┴────────┘
关键元素是使用LEFT JOIN LATERAL
而不是隐含的CROSS JOIN LATERAL
作为缩写只有一个逗号被解释。
Call a set-returning function with an array argument multiple times
查询可以简单地是:
SELECT t.id, t.employee, s.*
FROM mytable t
LEFT JOIN LATERAL jsonb_to_recordset(t.data->'sales')
AS s(yr int, value int) ON s.yr = 2012;
我们可以立即方便地挑选 yr = 2012
的销售人员,而不会因结果而流失员工。
要用 'NO Data' 美化,value
列必须是匹配的字符串类型:
SELECT t.id, t.employee
, COALESCE(s.yr, 2012) AS yr
, COALESCE(s.value, 'No Data') AS value
FROM mytable t
LEFT JOIN LATERAL jsonb_to_recordset(t.data->'sales')
AS s(yr int, value text) ON s.yr = 2012;
基于这些缺失的(可能的)细节:
- table 中每个员工恰好一行:
empoyee
是UNIQUE NOT NULL
。 每个员工在
data
中可以有 0-n 年的销售额 -data
可以为 NULL。yr
和value
存储有效整数。否则调整类型。
正确的table定义:
CREATE TABLE mytable (
id serial PRIMARY KEY
, employee text UNIQUE NOT NULL
, data jsonb
);
什么是 LATERAL JOIN
?
根据评论中的要求,这些链接应该有所帮助,尤其是初学者的第一个链接: