SQL Select 计数子查询,加入搞乱一切
SQL Select Count Subquery, Joins messing up everything
我有一个任务要处理 4 个不同的表。我认为我的“逻辑”是正确的,但我认为我未能将各种独立工作的事物结合在一起。
- Case不知何故returns两行比较为真时;如果不是,它只显示(正确)一个。没有连接也能正常工作。
- count 子查询单独运行时有效,但当我尝试将其连接在一起时,它会显示任何内容,从到处显示相同的数字到显示太大的数字(可能是倍数或倍数)。
Select Distinct RPD_PERSONS.PERSON_ID "id",
RPD_PERSONS.SURN_TXT ||' '|| RPD_PERSONS.NAME_TXT "Name",
Case ADD_ROLE_PERS.ROLE_CODE When 'Manager'
Then 'yes'
Else 'no'
End "Manager",
(
Select Count(LDD_CERTS.Cert_ID)
From LDD_CERTS
Join LDD_PERS_CERTS
On LDD_PERS_CERTS.CERT_ID = LDD_CERTS.CERT_ID
Where MONTHS_BETWEEN(LDD_CERTS.VALID_TO,SYSDATE)>0
And LDD_PERS_CERTS.CERT_CHANGE_TYPE>=0
) "no. of certificates"
From RPD_PERSONS
Join ADD_ROLE_PERS
On ADD_ROLE_PERS.Person_ID = RPD_PERSONS.Person_ID
Where RPD_PERSONS.Partic_ID = 1
Group By RPD_PERSONS.PERSON_ID, RPD_PERSONS.SURN_TXT ||' '|| RPD_PERSONS.NAME_TXT, ADD_ROLE_PERS.ROLE_CODE
Order By RPD_PERSONS.Person_ID;
这是一个子查询,它本身似乎工作得很好。
Select LDD_PERS_CERTS.PERSON_UID,Count(LDD_CERTS.Cert_ID)
From LDD_CERTS
Join LDD_PERS_CERTS
ON LDD_PERS_CERTS.CERT_ID = LDD_CERTS.CERT_ID
Where MONTHS_BETWEEN(LDD_CERTS.VALID_TO,SYSDATE)>0
AND LDD_PERS_CERTS.CERT_CHANGE_TYPE>=0
Group By LDD_PERS_CERTS.PERSON_UID
order by LDD_PERS_CERTS.PERSON_UID;
你有很多事情要做,虽然一个简短的查询就可以得到它,但让我试着总结一下我认为你想要得到的东西。
您想要一份公司内不同人员的列表,其中包含每个人有多少 ACTIVE 证书(未过期)。由此,您还想知道他们是否处于管理职位(通过角色)。
问:对于一个可能是经理的人,同时也是 under-manager 到 higher-up 的人,您是否希望看到这个人同时担任这两种角色,因为典型的业务结构可能有多个层次管理层,或者...您是否只关心见一个人一次,以及他们是经理还是其他级别。如果一个人有 3 个或更多角色怎么办,您是否希望每次都看到它们?如果您的主要护理是经理是或否,查询会变得更加简单。
现在,您对有效证书计数的查询。 MONTHS_BETWEEN() 函数似乎是您在 Oracle 中的 运行。基于 Valid_To 日期与 sysdate 相比的两个参数,表明有效日期始终是未来的(即:仍然具有活动证书)。如果是这种情况,您将无法优化查询,因为函数调用不是 Sargable
相反,您只需要执行 where Valid_To > SysDate,换句话说,只有那些尚未过期的。 pre-aggregating 每个证书 ID 的所有仍然有效的证书计数,然后加入个人证书 table 可能会更好地为您提供服务,因为个人证书检查适用于 cert_change_type > = 0 这可能意味着全部。 Cert_Change_Type 小于零的条件是什么,如果不小于零,则 where 子句毫无意义。
接下来,您的 SELECT DISTINCT 查询需要进行一些调整。您的 column-based select 没有外部人员 ID 的上下文,只是汇总了所有证书。人员 ID 与计算的证书没有关联。我只能猜测存在某种关系,例如
RPD_Persons.Person_id = LDD_Pers_Certs.Person_UID
综上所述,我会有以下内容 table/indexes
table index
LDD_PERS_CERTS ( CERT_CHANGE_TYPE, PERSON_UID, CERT_ID )
LDD_CERTS ( valid_to, cert_id )
RPD_PERSONS ( partic_id, person_id, surn_txt, name_txt )
ADD_ROLE_PERS ( person_id, role_code )
我会尝试
Select
lpc.PERSON_UID,
ValCerts.CertCount
From
( select
Cert_id,
count(*) CertCounts
from
LDD_CERTS
where
Valid_To > sysDate
group by
Cert_id ) ValCerts
JOIN LDD_PERS_CERTS lpc
on ValCerts.Cert_id = lpc.cert_id
Where
lpc.CERT_CHANGE_TYPE >= 0
现在,如果您只关心给定的人是否是经理,我会 pre-query 只是因为您实际上并没有返回一个人的特定角色,只是他们是不是经理的事实.我的最终查询可能看起来像'
select
p.PERSON_ID id,
max( p.SURN_TXT || ' ' || p.NAME_TXT ) Name,
max( Case when arp.Person_id IS NULL
then 'no' else 'yes' end ) Manager,
max( coalesce( certs.CertCount, 0 )) ActiveCertsForUser
from
RPD_PERSONS p
LEFT Join ADD_ROLE_PERS arp
On p.Person_ID = arp.Person_ID
AND arp.role_code = 'Manager'
LEFT JOIN
( Select
lpc.PERSON_UID,
ValCerts.CertCount
From
( select
Cert_id,
count(*) CertCounts
from
LDD_CERTS
where
Valid_To > sysDate
group by
Cert_id ) ValCerts
JOIN LDD_PERS_CERTS lpc
on ValCerts.Cert_id = lpc.cert_id
AND lpc.CERT_CHANGE_TYPE >= 0 )
) Certs
on p.Person_id = Certs.Person_uid
Where
p.Partic_ID = 1
GROUP BY
p.PERSON_ID
现在,如果 p.partic_id = 1 只代表 1 个人,那么查询所有具有给定证书状态的人等就没有意义了。但是如果 Partic_id = 1 代表一群人,比如在公司的给定协会/部门内,那应该没问题。
任何问题,让我知道,我可以修改/更新答案
- CASE 问题:据推测,每个人在 ADD_ROLE_PERS 中可能有多个记录。如果一个人可以同时拥有两个或更多角色 运行,那么您需要决定您需要使用什么业务逻辑来处理这个问题。如果一个人一次只能拥有一个活动角色,那么大概有一个“active/disabled”列或生效日期列,您应该使用它来识别活动记录(或者,可能存在数据问题)。
- 子查询应该 return 结果集中每一行的值都相同,因为它完全 isolated/standalone 来自主查询。如果你想让它产生与每一行相关的计数,那么你需要将它连接到主table中的tables(如果你不知道如何查找相关的子查询这个)
我有一个任务要处理 4 个不同的表。我认为我的“逻辑”是正确的,但我认为我未能将各种独立工作的事物结合在一起。
- Case不知何故returns两行比较为真时;如果不是,它只显示(正确)一个。没有连接也能正常工作。
- count 子查询单独运行时有效,但当我尝试将其连接在一起时,它会显示任何内容,从到处显示相同的数字到显示太大的数字(可能是倍数或倍数)。
Select Distinct RPD_PERSONS.PERSON_ID "id",
RPD_PERSONS.SURN_TXT ||' '|| RPD_PERSONS.NAME_TXT "Name",
Case ADD_ROLE_PERS.ROLE_CODE When 'Manager'
Then 'yes'
Else 'no'
End "Manager",
(
Select Count(LDD_CERTS.Cert_ID)
From LDD_CERTS
Join LDD_PERS_CERTS
On LDD_PERS_CERTS.CERT_ID = LDD_CERTS.CERT_ID
Where MONTHS_BETWEEN(LDD_CERTS.VALID_TO,SYSDATE)>0
And LDD_PERS_CERTS.CERT_CHANGE_TYPE>=0
) "no. of certificates"
From RPD_PERSONS
Join ADD_ROLE_PERS
On ADD_ROLE_PERS.Person_ID = RPD_PERSONS.Person_ID
Where RPD_PERSONS.Partic_ID = 1
Group By RPD_PERSONS.PERSON_ID, RPD_PERSONS.SURN_TXT ||' '|| RPD_PERSONS.NAME_TXT, ADD_ROLE_PERS.ROLE_CODE
Order By RPD_PERSONS.Person_ID;
这是一个子查询,它本身似乎工作得很好。
Select LDD_PERS_CERTS.PERSON_UID,Count(LDD_CERTS.Cert_ID)
From LDD_CERTS
Join LDD_PERS_CERTS
ON LDD_PERS_CERTS.CERT_ID = LDD_CERTS.CERT_ID
Where MONTHS_BETWEEN(LDD_CERTS.VALID_TO,SYSDATE)>0
AND LDD_PERS_CERTS.CERT_CHANGE_TYPE>=0
Group By LDD_PERS_CERTS.PERSON_UID
order by LDD_PERS_CERTS.PERSON_UID;
你有很多事情要做,虽然一个简短的查询就可以得到它,但让我试着总结一下我认为你想要得到的东西。
您想要一份公司内不同人员的列表,其中包含每个人有多少 ACTIVE 证书(未过期)。由此,您还想知道他们是否处于管理职位(通过角色)。
问:对于一个可能是经理的人,同时也是 under-manager 到 higher-up 的人,您是否希望看到这个人同时担任这两种角色,因为典型的业务结构可能有多个层次管理层,或者...您是否只关心见一个人一次,以及他们是经理还是其他级别。如果一个人有 3 个或更多角色怎么办,您是否希望每次都看到它们?如果您的主要护理是经理是或否,查询会变得更加简单。
现在,您对有效证书计数的查询。 MONTHS_BETWEEN() 函数似乎是您在 Oracle 中的 运行。基于 Valid_To 日期与 sysdate 相比的两个参数,表明有效日期始终是未来的(即:仍然具有活动证书)。如果是这种情况,您将无法优化查询,因为函数调用不是 Sargable 相反,您只需要执行 where Valid_To > SysDate,换句话说,只有那些尚未过期的。 pre-aggregating 每个证书 ID 的所有仍然有效的证书计数,然后加入个人证书 table 可能会更好地为您提供服务,因为个人证书检查适用于 cert_change_type > = 0 这可能意味着全部。 Cert_Change_Type 小于零的条件是什么,如果不小于零,则 where 子句毫无意义。
接下来,您的 SELECT DISTINCT 查询需要进行一些调整。您的 column-based select 没有外部人员 ID 的上下文,只是汇总了所有证书。人员 ID 与计算的证书没有关联。我只能猜测存在某种关系,例如
RPD_Persons.Person_id = LDD_Pers_Certs.Person_UID
综上所述,我会有以下内容 table/indexes
table index
LDD_PERS_CERTS ( CERT_CHANGE_TYPE, PERSON_UID, CERT_ID )
LDD_CERTS ( valid_to, cert_id )
RPD_PERSONS ( partic_id, person_id, surn_txt, name_txt )
ADD_ROLE_PERS ( person_id, role_code )
我会尝试
Select
lpc.PERSON_UID,
ValCerts.CertCount
From
( select
Cert_id,
count(*) CertCounts
from
LDD_CERTS
where
Valid_To > sysDate
group by
Cert_id ) ValCerts
JOIN LDD_PERS_CERTS lpc
on ValCerts.Cert_id = lpc.cert_id
Where
lpc.CERT_CHANGE_TYPE >= 0
现在,如果您只关心给定的人是否是经理,我会 pre-query 只是因为您实际上并没有返回一个人的特定角色,只是他们是不是经理的事实.我的最终查询可能看起来像'
select
p.PERSON_ID id,
max( p.SURN_TXT || ' ' || p.NAME_TXT ) Name,
max( Case when arp.Person_id IS NULL
then 'no' else 'yes' end ) Manager,
max( coalesce( certs.CertCount, 0 )) ActiveCertsForUser
from
RPD_PERSONS p
LEFT Join ADD_ROLE_PERS arp
On p.Person_ID = arp.Person_ID
AND arp.role_code = 'Manager'
LEFT JOIN
( Select
lpc.PERSON_UID,
ValCerts.CertCount
From
( select
Cert_id,
count(*) CertCounts
from
LDD_CERTS
where
Valid_To > sysDate
group by
Cert_id ) ValCerts
JOIN LDD_PERS_CERTS lpc
on ValCerts.Cert_id = lpc.cert_id
AND lpc.CERT_CHANGE_TYPE >= 0 )
) Certs
on p.Person_id = Certs.Person_uid
Where
p.Partic_ID = 1
GROUP BY
p.PERSON_ID
现在,如果 p.partic_id = 1 只代表 1 个人,那么查询所有具有给定证书状态的人等就没有意义了。但是如果 Partic_id = 1 代表一群人,比如在公司的给定协会/部门内,那应该没问题。
任何问题,让我知道,我可以修改/更新答案
- CASE 问题:据推测,每个人在 ADD_ROLE_PERS 中可能有多个记录。如果一个人可以同时拥有两个或更多角色 运行,那么您需要决定您需要使用什么业务逻辑来处理这个问题。如果一个人一次只能拥有一个活动角色,那么大概有一个“active/disabled”列或生效日期列,您应该使用它来识别活动记录(或者,可能存在数据问题)。
- 子查询应该 return 结果集中每一行的值都相同,因为它完全 isolated/standalone 来自主查询。如果你想让它产生与每一行相关的计数,那么你需要将它连接到主table中的tables(如果你不知道如何查找相关的子查询这个)