使用Oracle 10g,我有一个看起来像这样的表(为简洁起见缩短了语法):
CREATE TABLE "BUZINESS"."CATALOG"
( "ID" NUMBER PRIMARY KEY,
"ENTRY_ID" VARCHAR2(40 BYTE) NOT NULL,
"MSG_ID" VARCHAR2(40 BYTE) NOT NULL,
"PUBLISH_STATUS" VARCHAR2(30 BYTE) NOT NULL, /* Can be NEW or PUBLISHED */
CONSTRAINT "CATALOG_UN1" UNIQUE (ENTRY_ID, MSG_ID)
)
一个流程,即流程A,使用PUBLISH_STATUS' NEW'编写目录条目。然后进入第二个流程,流程B,抓住所有“新”'消息,然后将PUBLISH_STATUS更改为“已发布”'。
我需要编写一个查询来获取所有PUBLISH_STATUS =' NEW'行,但
我试图阻止无序提取,因此如果进程B将一行标记为PUBLISH_STATUS =' PUBLISHED'使用MSG_ID' 1000',然后处理A将无序行写为PUBLISH_STATUS =' NEW'使用MSG_ID' 999',当抓取所有' NEW'时,查询将永远不会获取该行。行。
所以,如果我从数据开始:
INSERT INTO BUZINESS.CATALOG VALUES (1, '1000', '999', 'NEW');
INSERT INTO BUZINESS.CATALOG VALUES (2, '1000', '1000', 'PUBLISHED');
INSERT INTO BUZINESS.CATALOG VALUES (3, '1000', '1001', 'NEW');
INSERT INTO BUZINESS.CATALOG VALUES (4, '2000', '1999', 'NEW');
INSERT INTO BUZINESS.CATALOG VALUES (5, '2000', '2000', 'PUBLISHED');
INSERT INTO BUZINESS.CATALOG VALUES (6, '2000', '2001', 'NEW');
INSERT INTO BUZINESS.CATALOG VALUES (7, '3000', '3001', 'NEW');
然后我的查询应该只抓取ID为的行: 3,6,7
然后我必须将这些行与其他数据连接起来,因此结果必须是JOINable。
到目前为止,我有一个非常大的,丑陋的查询UNIONing两个相关的子查询来执行此操作。有人可以帮我写一个更好的查询吗?
答案 0 :(得分:2)
要求不存在可连接数据最好通过外部联接来过滤掉匹配的联接(只留下不匹配)。
在您的情况下,加入条件是"已发布"具有稍后(更高)消息的相同条目的行,如果。
此查询产生您想要的输出:
select t1.*
from buziness_catalog t1
left join buziness_catalog t2
on t2.entry_id = t1.entry_id
and to_number(t2.msg_id) > to_number(t1.msg_id)
and t2.publish_status = 'PUBLISHED'
where t1.publish_status = 'NEW'
and t2.id is null
order by t1.id
请参阅此查询的live demo,使用您的示例数据生成所需的输出。注意,使用表名为" buziness_catalog"而不是" buziness.catalog"所以演示会运行 - 你必须将下划线改回点。
作为一个连接,而不是基于exists
相关子查询,这将表现得非常好。
如果您的msg_id
列是数字类型,则此查询会更简单一些(不需要从字符到数字的转换)。如果您的ID数据实际上是数字,请考虑将entry_id和msg_id的数据类型更改为数字类型。
答案 1 :(得分:1)
在线之间阅读,我认为这可能有效:
select
*
from
buziness.catalog b1
where
b1.publish_status = 'NEW' and
not exists (
select
'x'
from
buziness.catalog b2
where
b1.entry_id = b2.entry_id and
b2.publish_status = 'PUBLISHED' and
to_number(b2.msg_id) > to_number(b1.msg_id) -- store numbers as numbers!
);
答案 2 :(得分:1)
@Laurence的查询看起来不错,但为了满足我的好奇心,你是否介意解析这个问题呢?
我认为存储为varchar的那些数字会在TO_NUMBER()
时终止你的索引使用功能,但我不确定Oracle,所以你最好检查一下。
如果他们这样做,您可以随时添加在编辑行时使用触发器更新的其他数字列 - 这样您就不会破坏原始设计。
SELECT *
FROM buziness b1
WHERE PUBLISH_STATUS = 'NEW'
AND TO_NUMBER(msg_id) > COALESCE((
SELECT MAX(TO_NUMBER(msg_id))
FROM buziness b2
WHERE PUBLISH_STATUS = 'PUBLISHED'
AND b2.entry_id = b1.entry_id
), 0)