在某些情况下,我使用postgres,我有一个表,其中的项目定义如下:
CREATE TYPE EventType AS ENUM ('published', 'unpublished');
CREATE TABLE items (
id integer NOT NULL,
name varchar(40) NOT NULL,
event EventType NOT NULL,
date date
);
这里要认识到的关键一点是,名称不一定是唯一的,因此某个东西可能在日期X以名称发布,然后在日期Y取消发布,然后在日期Z再次重新发布。给定给定name
的任何特定连续[published, unpublished)
,它们将通过共享相同的id
而彼此关联。唯一的约束是,不要将[published, unpublished)
范围与数据库中相同的name
重叠。因此,您可以将其想象为表示给定name
的“不存在范围”列表。通常,任何id
都将具有published
和unpublished
条目,* 除了 * 最新的published
,它可能还没有unpublished
条目,您可以将其想象为表示[published, ?)
。
因此,我们的目标是,给定一个指定的QUERY_DATE
和name
,返回id
,表示它福尔斯的范围(QUERY_DATE >= published && QUERY_DATE < unpublished
),并考虑特殊情况('QUERY_DATE〉= published and there is no“unpublished entry”)。
你可以想象一个单独的表示法(这无疑会使我的问题更容易解决,但我不能控制这些数据),如下所示:
CREATE TABLE items (
id integer NOT NULL,
name varchar(40) NOT NULL,
published date NOT NULL,
unpublished date
);
在这里,上面的查询将是相当直接的,您只需执行以下操作:
SELECT id FROM items
WHERE
name = THE_NAME AND
published <= QUERY_DATE AND
(unpublished = NULL OR unpublished > QUERY_DATE)
然而,我们没有上面的模式,我们有一个与前面分开的范围,如果我把它分成两个查询,概念上很简单(下面的伪代码)假定已知日期范围不重叠,我们可以仅首先请求LATEST匹配的发布项目,然后我们只需要在最后做一个快速的检查,以确保它没有未发布。但我仍然在做两个查询:
const lastPublishedAfterDateID = query(`
SELECT id FROM items
WHERE
name = ${name} AND
event = published AND
published <= ${date}
ORDERED DESCENDING
LIMIT 1`));
const isUnpublished = query(`
SELECT id FROM items
WHERE
id = ${lastPublishedAfterDateID} AND
event = unpublished AND
unpublished > ${date}`);
const result = isUnpublished ? null : lastPublishedAfterDateID;
我 * 认为 * 有一种替代方法可以在查询中完成这一切,即执行内部自连接。
SELECT id
FROM items AS published
LEFT JOIN items AS unpublished
WHERE
published.name = THE_NAME AND
published.type = published AND
published.date <= QUERY_DATE AND
unplublished.id = published.id AND
unpublished.type = unpublished AND
unpublished.date > QUERY_DATE
LIMIT 1
我认为他们在这里限制1可能是多余的,因为除非表中的数据不正确,否则只有一个。LEFT JOIN处理没有结束日期的情况。无论哪种方式,我 * 认为 * 上面的方法可能会得到与上面两步过程等效的结果。但我不清楚这是否效率极低或不符合习惯等。从概念上讲,这很容易解释。“请给予我发布日期在给定日期之前的LATEST项,除非它福尔斯未发布日期之后,在这种情况下,只返回null”。同样,如果我控制着数据,我只会尝试使用我发布的第二个模式使我的工作更轻松,但我没有。
2条答案
按热度按时间2nbm6dog1#
窗口函数可以是:
db-fiddle
2uluyalo2#
我喜欢您将其逐字翻译为
not exists
的方式:demo1.确定结合包容性:不清楚在
QUERY_DATE
上完全未发布的名称是应该被视为截至该日期已经未发布,还是直到该日期结束时仍在发布。1.如果你的ID是按照
date
的升序排列的,并且你只在ID之后,你可以用max(id)
替换distinct on()...order by
,你的伪代码SELECT id FROM...ORDERED DESCENDING LIMIT 1
表明了这一点。1.当没有匹配条件的行时,它不返回任何行,这与返回带有
null
的行不同。