SQL Select 计数子查询,加入搞乱一切

SQL Select Count Subquery, Joins messing up everything

我有一个任务要处理 4 个不同的表。我认为我的“逻辑”是正确的,但我认为我未能将各种独立工作的事物结合在一起。

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(如果你不知道如何查找相关的子查询这个)