如何重构被破坏的复杂SQL查询
这是该领域的简化模型 简而言之,unit将文档授予客户。有两种类型的单位:主单位和它们的子单位。两者都属于同一个省,一个省可能属于多个城市。文档有许多事件(处理历史记录)。客户属于一个省市 我必须编写查询,它返回随机的文档集,给定目标主单元代码。标准如下:如何重构被破坏的复杂SQL查询,sql,database,oracle,plsql,Sql,Database,Oracle,Plsql,这是该领域的简化模型 简而言之,unit将文档授予客户。有两种类型的单位:主单位和它们的子单位。两者都属于同一个省,一个省可能属于多个城市。文档有许多事件(处理历史记录)。客户属于一个省市 我必须编写查询,它返回随机的文档集,给定目标主单元代码。标准如下: 返回10个文档,其中最新事件代码=10 每份文件必须属于居住在该单位所在地区任何城市的不同客户(首选不同城市) 返回符合条件的客户最新文档 结果中必须同时存在两种文档类型 每次查询的结果(选择的客户)应为随机 但是 如果没有足够的客户
- 返回10个文档,其中最新事件代码=10
- 每份文件必须属于居住在该单位所在地区任何城市的不同客户(首选不同城市)
- 返回符合条件的客户最新文档
- 结果中必须同时存在两种文档类型
- 每次查询的结果(选择的客户)应为随机
- 如果没有足够的客户,尝试使用同一客户的多个文档作为最后手段
- 如果没有足够的文档,请尽可能多地返回
- 如果没有其他文档类型的单个实例,则返回所有相同的实例
CURSOR c_documents IS
WITH documents_cte AS
SELECT d.document_id AS document_id, d.create_dt AS create_dt,
c.customer_id
FROM documents d
JOIN customers c ON (c.customer_id = d.customer_id AND
c.province_id = (SELECT region_id FROM unit WHERE unit_code = 1234))
WHERE exists (
SELECT 1
FROM event
where document_id = d.document_id AND
event_code = 10
AND create_dt =
SELECT MAX(create_dt)
FROM event
WHERE document_id = d.document_id)
SELECT * FROM documents_cte d
WHERE create_dt = (SELECT MAX(create_dt)
from documents_cte
WHERE customer_id = d.customer_id)
如何在考虑效率和随机性的情况下正确地进行此查询?我不是要求精确的解决方案,而是至少要有指导原则。我会尽可能避免层次表。在您的例子中,您使用层次表来允许无限的深度,但最后您只存储了两个级别:省份和它们的城市。最好是两张表:一张是省一张是市。没什么大不了的,但这会使您的数据模型更简单,更容易查询 下面我从一个
with
子句开始,以获得一个城市表,因为这样的表并不存在。然后我一步一步地做:获取属于该部门的客户,然后获取他们的文档并对他们进行排名。最后,我选择了排名靠前的文档,并随机抽取了10个排名靠前的文档
with cities as
(
select
c.region_id as city_id,
o.region_id as province_id
from region c
join region p on p.region_id = c.parent_region_id
)
, unit_customers as
(
select customer_id
from customer
where city_id in
(
select city_id
from cities
where
(
select region_id
from unit
where unit_code = 1234
) in (city_id, province_id)
)
)
, ranked_documents as
(
select
document.*,
row_number(partition by customer_id order by create_dt desc) as rn
from document
where customer_id in -- customers belonging to the unit
(
select customer_id
from unit_customers
)
and document_id in -- documents with latest event code = 10
(
select document_id
from event
group by document_id
having max(event_code) keep (dense_rank last order by create_dt) = 10
)
)
select *
from ranked_documents
order by rn, dbms_random.value
fetch first 10 rows only;
这不考虑获取这两种文档类型,因为这违反了为每个客户获取最新文档的规则
从Oracle 12c开始,可以使用“先提取”。在早期版本中,您将使用另一个子查询和另一个行编号
至于速度,我建议查询使用以下索引:
create index idx_r1 on region(region_id); -- already exists for region_id = primary key
create index idx_r2 on region(parent_region_id, region_id);
create index idx_u1 on unit(unit_code, region_id);
create index idx_c1 on customer(city_id, customer_id);
create index idx_e1 on event(document_id, create_dt, event_code);
create index idx_d1 on document(document_id, customer_id, create_dt);
create index idx_d2 on document(customer_id, document_id, create_dt);
最后两个中的一个将使用,另一个不使用。用
解释计划检查哪一个
并删除未使用的计划。文档和事件之间的关系是什么?我没有看到公共字段名。实际上我没有看到很多对应的字段名。IIRC,mysql没有CTE。@jbrahy我只是忘了在图片中包含外键。正如你所看到的,它们之间存在一对多的关系,所以区域实际上只是两个层次;各省及其城市?我可以通过观察一个城市的类型或注意到它有一个父区域来检测它。我可以通过查看一个省的类型或注意到它没有父区来检测它。对的因此,只有两个级别具有冗余的类型信息。我会为每个城市找到一个省。所有这些都正确吗?这是正确的