使用JOIN优化查询

时间:2014-03-04 17:03:07

标签: sql tsql sql-server-2008-r2

我正在尝试找出一种更有效的方法来编写我公司使用的查询。目前我们正在使用LEFT JOIN,但我觉得这可能是一个不好的方法来解决这个问题。

你们怎么会接近这个?我正在努力熟悉EXISTS和CROSS APPLY。也许这是我应该使用这些类型的语句的情况。

SELECT  p.people_id ,
        p.date_created ,
        p.last_name ,
        p.first_name ,
        p.middle_name ,
        p.known_as ,
        p.ssn ,
        p.home_phone ,
        p.work_mobile ,
        p.other_phone ,
        p.display_email ,
        s.source ,
        ISNULL(p.address_1, '') AS address_1 ,
        ISNULL(p.address_2, '') AS address_2 ,
        p.city ,
        p.state ,
        p.zip_code ,
        pec.emergency_name ,
        pec.work_phone ,
        pec.emergency_relationship ,
        jc.job_category ,
        et.education_type ,
        pp.part_time_only ,
        pp.perm_job ,
        pp.temp_job ,
        p.applied_online ,
        p.owner_division_id ,
        p.role_id ,
        p.older_18 ,
        p.disclaimer ,
        SUBSTRING(p.ssn, 6, 4) AS L4_ssn ,
        pp.custom_code_4 AS job_title ,
        p.external_id ,
        p.last4 ,
        p.resume_category ,
        rc.resume_category_description ,
        p.home_phone_perm ,
        p.work_mobile_perm
FROM    people p
        LEFT OUTER JOIN lkp_resume_category rc ON p.resume_category = rc.resume_category_id
        LEFT OUTER JOIN people_profile pp ON pp.people_id = p.people_id
        LEFT OUTER JOIN companies_job_titles cjt ON cjt.job_title_id = pp.job_title_1
        LEFT OUTER JOIN lkp_job_categories jc ON jc.job_category_id = pp.job_class_id
        LEFT OUTER JOIN lkp_education_types et ON et.education_type_id = pp.education_id
        LEFT OUTER JOIN lkp_sources s ON pp.source_id = s.source_id
        LEFT OUTER JOIN people_emergency_contacts pec ON p.people_id = pec.people_id
WHERE   ( p.role_id <= 4 )

Results Plan Diagram

1 个答案:

答案 0 :(得分:3)

这里实际上有两个不同的问题:

  1. 我应该使用LEFT JOIN吗?
  2. 如何提高查询效率?
  3. 我会首先回答#2,因为我觉得它更容易。在您的查询计划中,超过70%的成本来自“人员”表的表扫描。因此,您可以整天优化您的JOIN,但仍然无法提高效率。关键问题是,你的“人”中有多少百分比的“role_id&lt; = 4”?如果它低于10%,您可以根据索引的方式进行优化;如果它超过70% - 也就是说,如果这个查询的目的是为了在“人”表中提供几乎完整的每个人列表 - 那么你几乎必须支付这样做的成本。

    现在,关于问题#1:只要以下关于您的数据模型的推论是正确的,那么您的LEFT JOIN可能是您尝试做的最佳方式。推论是:

    1. “人”条目具有零对一的相应简历类别;也就是说,people.resume_category_id可以为NULL或者可以具有有意义的值。 (如果它可能在父表中找不到无效值,那么您就会出现参照完整性问题,而您需要的是外键约束。)
    2. “人”条目具有零对多的紧急联系。
    3. “人”条目具有零对多人的个人资料。
    4. “人物档案”条目具有零对一职位(如上所述,带有resume_category)
    5. “人员档案”条目具有零对一作业类别(如上所述)
    6. “人员档案”条目具有零对一教育类型(如上所述)
    7. “人物档案”条目具有零对一来源(如上所述)
    8. 您希望列出所有人,无论其他任何表格中是否存在数据
    9. 希望有所帮助,一切顺利。

      ---编辑---

      嘿,有些事一直困扰着我这个答案,我现在才知道它是什么。您的查询结构存在实际问题,但它与您使用LEFT JOIN无关。这是您一次加入两个不同的子表,两者都具有相同的“人员”父表。根据您的数据实际分配方式,这将为您提供笛卡尔积作为结果集。例如,假设您有一个人“Bob”,其中包含两个配置文件(“Work”和“Home”)和两个紧急联系人(“Alice”和“Carol”)。然后像你这样构建的查询会给出:

      Person   Profile   Contact
      ------   -------   -------
      Bob      Work      Alice
      Bob      Home      Alice
      Bob      Work      Carol
      Bob      Home      Carol
      

      如果结构化为零到多的关系实际上可以有多个子行,那么解决方案取决于您的应用使用数据的方式。但是,有两种基本的可能方法:

      1. 将每个零对多JOIN分隔为自己的查询,因此您总共有三个查询而不是一个。
      2. 使用某种聚合运算符,如FIRST或MAX(稍微粗略一点,因为它可以为结果集中的不同行提供不可预测的结果和/或混合匹配字段。)
      3. 作为旁注,如果子表不能有多个子行,那么您应该通过在每个表的“people_id”字段上放置一个唯一约束来确保这一点。