我正在寻找如何根据 PostgreSQL (10) 中子查询的结果从一个表中选择多行。我对高级数据库概念不是很熟悉,所以我可能会在这里遗漏一些明显的东西(即我的词汇量可能不够)。我不知道该怎么做PARTITION BY,但也许这是可能的。
数据库小提琴:https://www.db-fiddle.com/f/w7EwvYujxg4wmL7wHkfBdD/0
CREATE TABLE devices(id INTEGER PRIMARY KEY, name VARCHAR UNIQUE);
CREATE TABLE results(id INTEGER PRIMARY KEY, test_name VARCHAR, results JSON, state VARCHAR);
CREATE TABLE results_for_device(results_id INTEGER, device_id INTEGER, PRIMARY KEY(results_id, device_id));
INSERT INTO devices(id, name) VALUES
(1, 'Dev1'),
(2, 'Dev2');
INSERT INTO results(id, test_name, results, state) VALUES
# Results thread for 'test1' and 'Dev1'
(1, 'test1', '{}', 'CURRENT_BLUEPRINT'), # Select all the below results in this section
(2, 'test1', '{}', 'MATCHING_BLUEPRINT'),
(3, 'test1', '{}', 'MATCHING_BLUEPRINT'),
(4, 'test1', '{}', 'NOT_MATCHING_BLUEPRINT'),
# Results thread for 'test1' and 'Dev2'
(5, 'test1', '{}', 'OLD_BLUEPRINT'),
(6, 'test1', '{}', 'NOT_MATCHING_BLUEPRINT'),
(7, 'test1', '{}', 'CURRENT_BLUEPRINT'), # Select all the below results in this section
(8, 'test1', '{}', 'MATCHING_BLUEPRINT'),
(9, 'test1', '{}', 'MATCHING_BLUEPRINT'),
# Results thread for 'test2' and 'Dev1'
(10, 'test2', '{}', 'OLD_BLUEPRINT'),
(11, 'test2', '{}', 'NOT_MATCHING_BLUEPRINT'),
(12, 'test2', '{}', 'CURRENT_BLUEPRINT'), # Select all the below results in this section
(13, 'test2', '{}', 'MATCHING_BLUEPRINT'),
(14, 'test2', '{}', 'MATCHING_BLUEPRINT'),
# Results thread for 'test2' and 'Dev2'
(15, 'test2', '{}', 'OLD_BLUEPRINT'),
(16, 'test2', '{}', 'CURRENT_BLUEPRINT'), # Select all the below results in this section
(17, 'test2', '{}', 'MATCHING_BLUEPRINT'),
(18, 'test2', '{}', 'MATCHING_BLUEPRINT'),
(19, 'test2', '{}', 'NOT_MATCHING_BLUEPRINT');
INSERT INTO results_for_device(results_id, device_id) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 2),
(6, 2),
(7, 2),
(8, 2),
(9, 2),
(10, 1),
(11, 1),
(12, 1),
(13, 1),
(14, 1),
(15, 2),
(16, 2),
(17, 2),
(18, 2),
(19, 2);
# The desired results could be extracted by iterating the results of this subquery
# and querying for results >= results_id, but I want to do this in one go.
WITH result_threads AS (
SELECT r.id AS results_id,
d.id AS device_id
FROM results AS r
INNER JOIN results_for_device AS rfd ON rfd.results_id = r.id
INNER JOIN devices AS d ON d.id = rfd.device_id
WHERE r.state = 'CURRENT_BLUEPRINT'
)
SELECT * FROM result_threads; # WHAT TO DO HERE?
Run Code Online (Sandbox Code Playgroud)
我有一个包含多个结果的子查询 ( result_threads)。然后,对于子查询中的每个结果,我想从results表 where中选择数据id >= result_threads.id,也许还可以通过device_id和进行分区/排序results_id。也就是说,找到具有状态的 ID 'CURRENT_BLUEPRINT'(在任何时间点只能存在 1 个),然后为每个结果“线程”选择该 ID 之上的所有结果。
在代码中思考,我可以首先执行子查询,然后迭代每一行并使用 执行每行一个查询WHERE id > row.id,但如果可能的话,我想一次性执行此操作。
这是插入数据时注释中指出的所需结果:
-----------------------------------------------------------------
|device_id|results_id|test_name|results|state |
-----------------------------------------------------------------
| 1| 1| 'test1'| '{}'|'CURRENT_BLUEPRINT' |
| 1| 2| 'test1'| '{}'|'MATCHING_BLUEPRINT' |
| 1| 3| 'test1'| '{}'|'MATCHING_BLUEPRINT' |
| 1| 4| 'test1'| '{}'|'NOT_MATCHING_BLUEPRINT'|
| 2| 7| 'test1'| '{}'|'CURRENT_BLUEPRINT' |
| 2| 8| 'test1'| '{}'|'MATCHING_BLUEPRINT' |
| 2| 9| 'test1'| '{}'|'MATCHING_BLUEPRINT' |
| 1| 12| 'test2'| '{}'|'CURRENT_BLUEPRINT' |
| 1| 13| 'test2'| '{}'|'MATCHING_BLUEPRINT' |
| 1| 14| 'test2'| '{}'|'MATCHING_BLUEPRINT' |
| 2| 16| 'test2'| '{}'|'CURRENT_BLUEPRINT' |
| 2| 17| 'test2'| '{}'|'MATCHING_BLUEPRINT' |
| 2| 18| 'test2'| '{}'|'MATCHING_BLUEPRINT' |
| 2| 19| 'test2'| '{}'|'NOT_MATCHING_BLUEPRINT'|
-----------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
添加test_name到 CTE,您的结果线程将被完全定义。现在您可以使用 CTE 输出来生成所需的结果:
WITH
result_threads AS
(
SELECT
rfd.*,
r.test_name
FROM
results_for_device AS rfd
INNER JOIN results AS r ON rfd.results_id = r.id
WHERE
r.state = 'CURRENT_BLUEPRINT'
)
SELECT
rfd.device_id,
rfd.results_id,
r.test_name,
r.results,
r.state
FROM
results_for_device AS rfd
INNER JOIN results AS r ON rfd.results_id = r.id
INNER JOIN result_threads AS rt
ON rfd.device_id = rt.device_id
AND r.test_name = rt.test_name
AND r.id >= rt.results_id
;
Run Code Online (Sandbox Code Playgroud)
粗体部分显示了您加入result_threads以获得输出的方式。基本上,通过和起始result_threads定义输出的每个部分。您可以看到连接谓词 for 如何使用所有三个定义点。device_idtest_nameresults_idresult_threads
您可能还注意到该device表已从 CTE 定义中删除。results_for_device如果和之间的 PK/FK 关系device已正式定义并且device_id中的列results_for_device不可为空,则没有必要。否则,当然,您需要将其包含回来。如果初始集需要通过设备名称进行额外过滤,则也需要这样做。
如果性能是一个问题,您还可以尝试这个替代版本,它尝试在不多次访问基础表的情况下解决问题:
SELECT
device_id,
results_id,
test_name,
results,
state
FROM
(
SELECT
rfd.device_id,
rfd.results_id,
r.test_name,
r.results,
r.state,
MAX(r.id) FILTER (WHERE r.state = 'CURRENT_BLUEPRINT') OVER w AS this_id,
MAX(r.id) OVER w AS max_id
FROM
results_for_device AS rfd
INNER JOIN results AS r ON rfd.results_id = r.id
WINDOW
w AS (PARTITION BY rfd.device_id, r.test_name)
) AS derived
WHERE
results_id BETWEEN this_id AND max_id
;
Run Code Online (Sandbox Code Playgroud)
在此解决方案中,为 的每个不同部分定义了计算列this_id和形式的起始行和结束行。外部查询使用计算列来过滤值。max_id(device_id, test_name)results_id
两种解决方案都可以在以下位置进行测试:
db<>fiddle.uk。
| 归档时间: |
|
| 查看次数: |
1614 次 |
| 最近记录: |