【问题标题】:Where clause affecting two different select statements影响两个不同选择语句的 where 子句
【发布时间】:2016-04-20 05:37:25
【问题描述】:

我需要分别计算邮政编码为 44444 的付款人和凡人。他们的邮政编码存储在同一个表中,但他们的 ID 在多对多关系中的两个单独的表中。我写了这个:

select count(mortal_zip) as "Mortals in 44444"
      ,count(payer_zip) as "Payers in 44444"
  from
      (select am.zip_code as mortal_zip
         from address am
         join mortal_address
           on mortal_address.address_id = am.address_id
         join mortal
           on mortal.mortal_id = mortal_address.mortal_id
        where trim(am.zip_code) = '44444'
       ) m,
      (select ap.zip_code as payer_zip
         from address ap
         join payer_address
           on payer_address.address_id = ap.address_id
         join payer
           on payer.payer_id = payer_address.payer_id
        where trim(ap.zip_code) = '44444'
       ) p;

我知道:没有邮政编码为 44444 的付款人,但有 3 个邮政编码为 44444 的凡人。出于某种原因,我知道有 0 个邮政编码为 44444 的凡人。如果我问对于 44444 中的凡人,我得到了我需要的东西。如果我用 444444 询问凡人的数量和付款人的数量,我得到的两边都是 0。

此外,我尝试使用子选择重新编写整个内容。

select count(m.mortal_zip) as "Mortals in 44444"
      ,count(p.payer_zip) as "Payers in 44444"
  from
      (select am.zip_code as mortal_zip
         from address am
        where trim(am.zip_code) = '44444'
          and am.address_id in
             (select mortal_address.address_id
                from mortal_address
               where mortal_address.mortal_id in
                  (select mortal.mortal_id
                     from mortal
                   )
              )
       ) m,
      (select ap.zip_code as payer_zip
         from address ap
        where trim(ap.zip_code) = '44444'
          and ap.address_id in
             (select payer_address.address_id
                from payer_address
               where payer_address.payer_id in
                    (select payer.payer_id
                       from payer
                    )
              ) 
       ) p;

我得到相同的结果。

为什么另一个 select 语句中的 where 函数会影响另一个 select 语句?

更新

我已经重新编写了查询,但这两个查询返回的值不同。

这个:

select m.mortal_zip as "Mortals in 44444"
      ,p.payer_zip as "Payers in 44444"
  from
      (select count(am.zip_code) as mortal_zip
         from address am
        where trim(am.zip_code) = '44444'
          and am.address_id in
             (select mortal_address.address_id
                from mortal_address
               where mortal_address.mortal_id in
                  (select mortal.mortal_id
                     from mortal
                   )
              )
       ) m,
      (select count(ap.zip_code) as payer_zip
         from address ap
        where trim(ap.zip_code) = '44444'
          and ap.address_id in
             (select payer_address.address_id
                from payer_address
               where payer_address.payer_id in
                    (select payer.payer_id
                       from payer
                    )
              ) 
       ) p;

返回:

Mortals in 44444 Payers in 44444
---------------- ---------------
               3               0

这个:

select mortal_zip as "Mortals in 44444"
      ,payer_zip as "Payers in 44444"
  from
      (select count(am.zip_code) as mortal_zip
         from address am
         join mortal_address
           on mortal_address.address_id = am.address_id
         join mortal
           on mortal.mortal_id = mortal_address.mortal_id
        where trim(am.zip_code) = '44444'
       ) m,
      (select count(ap.zip_code) as payer_zip
         from address ap
         join payer_address
           on payer_address.address_id = ap.address_id
         join payer
           on payer.payer_id = payer_address.payer_id
        where trim(ap.zip_code) = '44444'
       ) p;

返回:

Mortals in 44444 Payers in 44444
---------------- ---------------
               5               0

【问题讨论】:

  • 请尽量避免您在那里进行的连接类型。阅读笛卡尔连接。您实际上是在进行 CROSS JOIN,但由于查询的一侧是空的,因此您没有得到任何结果。您要做的是使用基表,然后对您所做的每个子选择执行 LEFT JOIN。这将允许在不抑制数据的情况下返回 NULL 值。
  • 为什么需要修剪邮政编码?您是否知道或怀疑它周围可能有不必要的空间?如果是这样,使用类似这样的东西会更有效: where am.zip_code like '%44444%' - 尽可能避免在函数中包装列值。
  • @mathguy 邮政编码字段是 varchar 数据类型,包含 15 个字符空格。

标签: sql oracle oracle11g


【解决方案1】:

您的查询正在尝试JOIN 一个空结果集,其结果集包含三行。当然,这会返回空结果集。相反,您可以运行自己获取 COUNTs 的子选择,或者:

SELECT
    COUNT(MA.address_id) AS "Mortals in 44444",
    COUNT(PA.address_id) AS "Payers in 44444"
FROM
    Address A
LEFT OUTER JOIN Mortal_Address MA ON MA.address_id = A.address_id
LEFT OUTER JOIN Payer_Address PA ON PA.address_id = A.address_id
WHERE
    A.zip_code = '44444'

【讨论】:

    【解决方案2】:

    我建议使用 UNION 来获取您想要的值,然后对这些值求和:

    SELECT SUM(MORTAL_ZIP) AS MORTAL_ZIP,
           SUM(PAYER_ZIP) AS PAYER_ZIP
    FROM (select COUNT(*) as mortal_zip,
                 0 as payer_zip
            from address a
            inner join mortal_address ma
               on ma.address_id = a.address_id
            inner join mortal m
               on m.mortal_id = ma.mortal_id
            where trim(a.zip_code) = '44444'
          UNION ALL
          select 0 as mortal_zip,
                 count(*) as payer_zip
            FROM address a
            inner join payer_address pa
              on pa.address_id = a.address_id
            inner join payer p
              on p.payer_id = pa.payer_id
            where trim(a.zip_code) = '44444')
    

    祝你好运。

    【讨论】:

      【解决方案3】:

      我可能不需要为此询问 Stack Overflow,但这是我寻求的答案。希望它可以帮助其他可能在此线程上发茬的人。

      select m.mortal_zip as "Mortals in 44444"
            ,p.payer_zip as "Payers in 44444"
        from
            (select count(am.zip_code) as mortal_zip
               from address am
              where trim(am.zip_code) = '44444'
                and am.address_id in
                   (select mortal_address.address_id
                      from mortal_address
                     where mortal_address.mortal_id in
                        (select mortal.mortal_id
                           from mortal
                         )
                    )
             ) m,
            (select count(ap.zip_code) as payer_zip
               from address ap
              where trim(ap.zip_code) = '44444'
                and ap.address_id in
                   (select payer_address.address_id
                      from payer_address
                     where payer_address.payer_id in
                          (select payer.payer_id
                             from payer
                          )
                    ) 
             ) p;
      

      我只需要将count 放入子选择中。正如@TomH 所说。

      【讨论】:

      • 这个查询做了大量的工作。列表中的第一个答案会快得多。事实上,第二个也是如此。
      • @TGray 就我的目的而言,它有效。它是一个小数据集,我的时间很紧。我将对其进行修改,并可能接受您提到的答案之一。尽管如此,我还是忍不住问你如何仅通过查看我的查询来衡量它的效率。
      • 基于相同表的计数和子查询。充其量,这将导致查询散列,如果优化器举手,则进行一些重大扫描。与个人无关。我一直在写查询很长时间。如果您的数据集非常小,它可能会起作用。除非您有基于函数的索引,否则邮政编码上的修剪将忽略该列上的索引。此外,针对邮政编码的数据分布将会很小。添加“in”子句,您最多只能面对部分扫描。对于您的小型数据集来说可能不是问题。
      猜你喜欢
      • 2015-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      相关资源
      最近更新 更多