如何在 Postgresql 的 SELECT 语句中创建“即时”映射表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17532603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create an "on-the-fly" mapping table within a SELECT statement in Postgresql
提问by Divey
I'm creating a select statement that combines two tables, zone
and output
,
based on a referenced device
table and on a mapping of zone_number
to output_type_id
.
The mapping of zone_number
to output_type_id
doesn't appear
anywhere in the database, and I would like to create it "on-the-fly" within the select
statement. Below is my schema:
我正在创建一个 select 语句,它结合了两个表zone
和output
,基于引用的device
表和zone_number
to的映射output_type_id
。zone_number
to的映射output_type_id
不会出现在数据库中的任何地方,我想在 select 语句中“即时”创建它。以下是我的架构:
CREATE TABLE output_type (
id INTEGER NOT NULL,
name TEXT,
PRIMARY KEY (id)
);
CREATE TABLE device (
id INTEGER NOT NULL,
name TEXT,
PRIMARY KEY (id)
);
CREATE TABLE zone (
id SERIAL NOT NULL,
device_id INTEGER NOT NULL REFERENCES device(id),
zone_number INTEGER NOT NULL,
PRIMARY KEY (id),
UNIQUE (zone_number)
);
CREATE TABLE output (
id SERIAL NOT NULL,
device_id INTEGER NOT NULL REFERENCES device(id),
output_type_id INTEGER NOT NULL REFERENCES output_type(id),
enabled BOOLEAN NOT NULL,
PRIMARY KEY (id)
);
And here is some example data:
这是一些示例数据:
INSERT INTO output_type (id, name) VALUES
(101, 'Output 1'),
(202, 'Output 2'),
(303, 'Output 3'),
(404, 'Output 4');
INSERT INTO device (id, name) VALUES
(1, 'Test Device');
INSERT INTO zone (device_id, zone_number) VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4);
INSERT INTO output (device_id, output_type_id, enabled) VALUES
(1, 101, TRUE),
(1, 202, FALSE),
(1, 303, FALSE),
(1, 404, TRUE);
I need to get the associated enabled
field from the output table for each zone for a given device.
Each zone_number
maps to an output_type_id
. For this example:
我需要enabled
从给定设备的每个区域的输出表中获取相关字段。每个zone_number
映射到一个output_type_id
. 对于这个例子:
zone_number | output_type_id
----------------------------
1 | 101
2 | 202
3 | 303
4 | 404
One way to handle the mapping would be to create a new table
处理映射的一种方法是创建一个新表
CREATE TABLE zone_output_type_map (
zone_number INTEGER,
output_type_id INTEGER NOT NULL REFERENCES output_type(id)
);
INSERT INTO zone_output_type_map (zone_number, output_type_id) VALUES
(1, 101),
(2, 202),
(3, 303),
(4, 404);
And use the following SQL to get all zones, plus the enabled
flag, for device 1:
并使用以下 SQL 获取enabled
设备 1 的所有区域以及标志:
SELECT zone.*, output.enabled
FROM zone
JOIN output
ON output.device_id = zone.device_id
JOIN zone_output_type_map map
ON map.zone_number = zone.zone_number
AND map.output_type_id = output.output_type_id
AND zone.device_id = 1
However, I'm looking for a way to create the mapping of zone nunbers to output types without creating a new table and without piecing together a bunch of AND/OR statements. Is there an elegant way to create a mapping between the two fields within the select statement? Something like:
但是,我正在寻找一种方法来创建区域编号到输出类型的映射,而无需创建新表,也无需将一堆 AND/OR 语句拼凑在一起。有没有一种优雅的方法来在 select 语句中的两个字段之间创建映射?就像是:
SELECT zone.*, output.enabled
FROM zone
JOIN output
ON output.device_id = zone.device_id
JOIN (
SELECT (
1 => 101,
2 => 202,
3 => 303,
4 => 404
) (zone_number, output_type_id)
) as map
ON map.zone_number = zone.zone_number
AND map.output_type_id = output.output_type_id
AND zone.device_id = 1
Disclaimer: I know that ideally the enabled
field would exist in the zone
table. However, I don't have control over that piece. I'm just looking for the
most elegant solution from the application side. Thanks!
免责声明:我知道理想情况下该enabled
字段将存在于zone
表中。但是,我无法控制那件作品。我只是从应用程序方面寻找最优雅的解决方案。谢谢!
回答by mu is too short
You can use VALUES
as an inline table and JOIN to it, you just need to give it an alias and column names:
您可以将其VALUES
用作内联表并加入它,您只需要给它一个别名和列名:
join (values (1, 101), (2, 202), (3, 303), (4, 304)) as map(zone_number, output_type_id)
on ...
From the fine manual:
从精美的手册:
VALUES
can also be used where a sub-SELECT
might be written, for example in aFROM
clause:SELECT f.* FROM films f, (VALUES('MGM', 'Horror'), ('UA', 'Sci-Fi')) AS t (studio, kind) WHERE f.studio = t.studio AND f.kind = t.kind; UPDATE employees SET salary = salary * v.increase FROM (VALUES(1, 200000, 1.2), (2, 400000, 1.4)) AS v (depno, target, increase) WHERE employees.depno = v.depno AND employees.sales >= v.target;
VALUES
也可以用在子句SELECT
可能写的地方,例如在FROM
子句中:SELECT f.* FROM films f, (VALUES('MGM', 'Horror'), ('UA', 'Sci-Fi')) AS t (studio, kind) WHERE f.studio = t.studio AND f.kind = t.kind; UPDATE employees SET salary = salary * v.increase FROM (VALUES(1, 200000, 1.2), (2, 400000, 1.4)) AS v (depno, target, increase) WHERE employees.depno = v.depno AND employees.sales >= v.target;
回答by Valentin Waeselynck
So just to complement the accepted answer, the following code is a valid, self-containedPostgresql expression which will evaluate to an 'inline' relation with columns (zone_number, output_type_id)
:
因此,为了补充已接受的答案,以下代码是一个有效的、自包含的Postgresql 表达式,它将评估为与列的“内联”关系(zone_number, output_type_id)
:
SELECT * FROM
(VALUES
(1, 101),
(2, 202),
(3, 303),
(4, 304)
) as i(zone_number, output_type_id)
(The (VALUES ... AS ...)
part alone will not make a valid expression, which is why I added the SELECT * FROM
.)
((VALUES ... AS ...)
单独的部分不会产生有效的表达式,这就是为什么我添加了SELECT * FROM
.)
回答by Francisco Soto
JOIN
(SELECT 1 zone_number, 101 as output_type_id
UNION ALL
SELECT 2 zone_number, 202 as output_type_id
UNION ALL
SELECT 3 zone_number, 303 as output_type_id
) mappings on mappings.zone_number = zone.zone_number