【问题标题】:Handling select condition dynamically动态处理选择条件
【发布时间】:2014-04-10 04:38:08
【问题描述】:

我有一个类似的程序

Procedure P_GENDER_REP
 (
  l_province  IN  varchar,
  l_district  IN  varchar,
  l_village   IN  varchar,
  l_tribe     IN  varchar,
  l_clan      IN  varchar,
  l_refcursor out sys_refcursor
 )

我在这样的过程中有一个选择查询

select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
from itaukei_data_store b,itaukei_data_store_key a 
where a.reference_no = b.reference_no and a.record_no = b.record_no and a.province = l_province 
  and a.district = l_district and a.village = l_village and a.tribe= l_tribe 
    and a.clan = l_clan      
order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;

现在我需要一个选择查询来处理给定的输入。例如,如果只给出 l_province,我需要动态忽略所有其他 where 条件。请帮帮我。

【问题讨论】:

    标签: sql oracle select plsql


    【解决方案1】:

    通常您通过在每个条件中添加OR l_variable IS NULL 来做到这一点:

    select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
    from itaukei_data_store b INNER JOIN itaukei_data_store_key a 
    ON a.reference_no = b.reference_no 
      and a.record_no = b.record_no 
      and a.province = l_province 
    WHERE l_district IS NULL OR a.district = l_district
      and l_village IS NULL OR a.village = l_village
      and l_tribe IS NULL OR a.tribe= l_tribe
      and l_clan IS NULL OR a.clan = l_clan
    order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    

    但是,这有时可能会影响性能。对每种组合使用 IF 会更好。或者,您可以尝试使用支持短路的 CASE 语句,如下所示:

    select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
    from itaukei_data_store b INNER JOIN itaukei_data_store_key a 
    ON a.reference_no = b.reference_no 
      and a.record_no = b.record_no 
      and a.province = l_province 
    WHERE a.district = CASE WHEN l_district IS NULL THEN a.district ELSE l_district END
      and a.village = CASE WHEN l_village IS NULL THEN a.village ELSE l_village END
      and a.tribe = CASE WHEN l_tribe IS NULL THEN a.tribe ELSE l_tribe END
      and a.clan = CASE WHEN l_clan IS NULL THEN a.clan ELSE l_clan END
    order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    

    你有第三种选择。您可以使用COALESCE 代替CASE,如下所示:

    select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
    from itaukei_data_store b INNER JOIN itaukei_data_store_key a 
    ON a.reference_no = b.reference_no 
      and a.record_no = b.record_no 
      and a.province = l_province 
    WHERE a.district = COALESCE(l_district, a.district)
      and a.village = COALESCE(l_village, a.village)
      and a.tribe = COALESCE(l_tribe, a.tribe)
      and a.clan = COALESCE(l_clan, a.clan)
    order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    

    尝试每一种,看看哪一种表现更好。顺便说一句,我还将您的 FROM 子句从逗号分隔的表列表更改为标准的 INNER JOIN。请养成使用 JOIN 的习惯,因为它现在被认为是“最佳选择”

    【讨论】:

    • “但是,这有时可能会影响性能。”我猜你对它的了解比我多,但我想如果你先做空检查,那么数据库就不会打扰OR 的另一边(短路)。在最坏的情况下,我们所说的性能损失是用户在一次调用中就可以检测到,还是需要多次调用 SP 才能让用户注意到?
    • 不幸的是,几乎所有的 RDBMS 都不支持短路。 “更好的质量”(如 Oracle)可以优化查询。如果您愿意,我可以更新问题以将检查 NULL 放在前面,但我觉得它不应该有任何区别。我相信实现短路的唯一方法是使用 CASE。我将更新答案以包括使用 CASE 语句的选项。
    【解决方案2】:

    给出的示例是 SQL Server, 第一种方法,简单的如果

    Declare @l_province int
    Declare @l_district int
    Declare @l_village int
    
    If @l_province is not null and @l_district is null  and @l_village is null -- Only l_province is given
    Begin
        select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
            from itaukei_data_store b,itaukei_data_store_key a 
        order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    End
    Else
    Begin
        select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
            from itaukei_data_store b,itaukei_data_store_key a 
        where a.reference_no = b.reference_no 
                and a.record_no = b.record_no 
                and a.province = l_province 
                and a.district = l_district 
                and a.village = l_village 
                and a.tribe= l_tribe 
                and a.clan = l_clan      
        order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    End
    

    ---------------------使用 OR , AND 的第二种方式

    select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data 
            from itaukei_data_store b,itaukei_data_store_key a 
        where (
                (
                    (@l_province is not null and @l_district is null  and @l_village is null) -- Only l_province is given
                    AND
                    (1=1)
                )
                OR
                (
                    (@l_province is not null and (@l_district is not null  OR @l_village is not null))
                    AND
                    (
                            a.reference_no = b.reference_no 
                            and a.record_no = b.record_no 
                            and a.province = l_province 
                            and a.district = l_district 
                            and a.village = l_village 
                            and a.tribe= l_tribe 
                            and a.clan = l_clan 
                    )
                )
                OR
                (
                    (@l_province is null)
                    AND
                    (
                            a.reference_no = b.reference_no 
                            and a.record_no = b.record_no 
                            and a.province = l_province 
                            and a.district = l_district 
                            and a.village = l_village 
                            and a.tribe= l_tribe 
                            and a.clan = l_clan 
                    )
    
                )
            )   
        order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
    

    其实为了简化存储过程,我建议你带一个参数来判断where子句是否应用!为了计算该变量,您需要在代码后面或存储过程开始时编写一些代码。它会让生活变得轻松。

    【讨论】:

    • SQL Server 语法。请将其更改为 PL/SQL,因为这是 OP 要求的
    【解决方案3】:

    只是为了添加另一个技巧。

    如果每个列的值不同,您可以使用coalesce

    select  a.province,a.district,a.village,a.tribe,a.clan,b.cdr_data 
    from    itaukei_data_store b,itaukei_data_store_key a 
    where   a.reference_no = b.reference_no 
    and     a.record_no = b.record_no 
    and     coalesce(a.province,a.district,a.village,a.tribe,a.clan) = 
            coalesce(l_province,l_district,l_village,l_tribe,l_clan)
    order by a.district,a.village,a.tribe,a.clan;
    

    合并返回列表中的第一个非空值

    【讨论】:

      【解决方案4】:

      您可以对参数进行 nvl,以便在设置它们时使用它们,否则将采用比较表中的匹配值..应该具有预期的效果:

      select a.province,a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN,b.cdr_data
      from itaukei_data_store b,itaukei_data_store_key a 
      where a.reference_no = b.reference_no 
      and a.record_no = b.record_no 
      and a.province = nvl(l_province, a.province)
      and a.district = nvl(l_district, a.district)
      and a.village = nvl(l_village, a.village)
      and a.tribe = nvl(l_tribe, a.tribe) 
      and a.clan = nvl(l_clan, a.clan)
      order by a.DISTRICT,a.VILLAGE,a.TRIBE,a.CLAN;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-03
        • 2020-08-13
        • 2019-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-23
        相关资源
        最近更新 更多