postgresql 内部SELF JOIN是否是此两步过滤的正确工具?

bjg7j2ky  于 2023-03-17  发布在  PostgreSQL
关注(0)|答案(2)|浏览(101)

在某些情况下,我使用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都将具有publishedunpublished条目,* 除了 * 最新的published,它可能还没有unpublished条目,您可以将其想象为表示[published, ?)
因此,我们的目标是,给定一个指定的QUERY_DATEname,返回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”。同样,如果我控制着数据,我只会尝试使用我发布的第二个模式使我的工作更轻松,但我没有。

2nbm6dog

2nbm6dog1#

窗口函数可以是:

select ID, name, event, date from 
(select ID, name, event, date,
LEAD(date,1) OVER (PARTITION BY ID ORDER BY date) next_date
   from items) e
where QUERY_DATE >= date and ( QUERY_DATE < next_date  or next_date is null)
and name = THE_NAME;

db-fiddle

2uluyalo

2uluyalo2#

我喜欢您将其逐字翻译为not exists的方式:demo

select distinct on(name)*     --"Give me the LATEST
from  items p                 -- item
where p.event='published'     -- whose publish
and   p.date<='2023-03-06'    -- date is before the given date,
and not exists (              -- unless
    select true
    from   items u
    where u.name = p.name     -- it
    and u.id   = p.id           
    and u.date>= p.date       -- also 
    and u.date < '2023-03-06' -- falls after 
    and u.event='unpublished' -- the unpublished date"
    )
and   p.name='Matrix'         -- (given a specified QUERY_DATE **and name**)
order by name,date desc;--this selects the the latest as the distinct one to return

1.确定结合包容性:不清楚在QUERY_DATE上完全未发布的名称是应该被视为截至该日期已经未发布,还是直到该日期结束时仍在发布。
1.如果你的ID是按照date的升序排列的,并且你只在ID之后,你可以用max(id)替换distinct on()...order by,你的伪代码SELECT id FROM...ORDERED DESCENDING LIMIT 1表明了这一点。
1.当没有匹配条件的行时,它不返回任何行,这与返回带有null的行不同。

相关问题