带有动态子句的MySQL存储过程

时间:2017-07-01 22:40:39

标签: mysql stored-procedures

我需要构建一个动态的SQL子句,我看到一些只使用case的例子,但由于某些原因我的源代码不起作用。

有人可以帮助我吗?

create procedure sp_test(in iduser bigint, in name varchar(50), in company varchar(50), in city varchar(50), in profession varchar(50))
begin
    if not(name is null) then 
        begin   
            set name = '%' + lower(name) + '%'; 
        end;
    end if;

    if not(company is null) then 
        begin   
            set company = '%' + lower(company) + '%';
        end;
    end if;

    if not(city is null) then 
        begin   
            set city = '%' + lower(city) + '%'; 
        end;
    end if;

    if not(profession is null) then 
        begin   
            set profession = '%' + lower(profession) + '%';
        end;
    end if;

    select 
            usr.id_user,
            usr.ds_icon,
            usr.nm_user,
            usr.ds_slug,
            usr.ds_title,
            usr.nm_company
        from 
            tbl_user usr
            left join tbl_profession pro on (pro.id_profession = usr.id_profession)
            left join tbl_resume res on (res.id_user = usr.id_user)
        where
            (usr.ds_activation is null) and
            usr.id_user <> iduser and
            usr.id_user not in (select id_friend from tbl_user_friend where id_user = iduser) and
            usr.id_user not in (select id_user from tbl_user_friend where id_friend = iduser) and
            usr.id_user not in (select id_friend from tbl_user_friend_not_suggest where id_user = iduser) and
            case when not(name is null) then 
                lower (usr.nm_user) like lower(name) or 
            end
            case when not(company is null) then 
                lower (usr.nm_company) like lower(company) or 
            end
            case when not(profession is null) then 
                lower (pro.nm_profession) like lower(profession) or 
            end
            case when not(city is null) then 
                lower (res.ds_city) like lower(city) or 
            end
            1 = 1
        order by
            usr.nm_user
        limit
            0,20
        ;
end$$

我想这个想法是正确的,我准备使用%value%过滤字符串以在SQL命令上使用它,并在检查值是否为null之后,我想将它添加到WHERE子句中。

2 个答案:

答案 0 :(得分:0)

好的,没有真正看到你的代码做了什么,但你最有可能找到的是预处理语句:你不能在存储过程中连接一个sql,因为那是编译代码,但你可以连接一个字符串并放置它在准备语句中+执行该语句。

简短的例子:

SET @sql = CONCAT('SELECT some_columns FROM table
               WHERE a=b',  
               @your_generated_where_statement_parts);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

答案 1 :(得分:0)

SQL是一种声明性语言。您告诉服务器您要查找的内容,而不是如何找到它。查询优化器的任务是确定如何查找行。

简单的解决方案是让查询优化器负责优化不必要的条件,它会自动执行

WHERE
... AND
(name IS NULL OR usr.nm_user LIKE CONCAT('%',name,'%') AND
(company IS NULL OR usr.nm_company LIKE CONCAT('%',company,'%') AND
... -- repeat for other variables

在准备查询计划时,优化程序的工作是确定如何以尽可能少的工作实际找到所需的行。

由于name是在查询执行期间无法更改的变量,因此优化程序将其解析为常量。由于name是常量,因此name IS NULL是一个常量表达式,可以在查询执行开始之前解析为true或false。

如果为true,则OR表达式始终为true,因此无需解析表达式CONCAT('%',name,'%'),因此将其优化掉。

如果为false,则将表达式CONCAT('%',name,'%')解析为常量,并将每行与其进行比较。不需要为每一行处理CONCAT(),因为值也不会在行之间发生变化,因此无需提前执行此操作。

因此,无需重写您的查询。只需表达一个逻辑上有效的表达式,优化器将完成剩下的工作。

此外,默认情况下,字符排序规则不区分大小写,因此除非您更改此字符,否则LOWER()是不必要的。

并且,如上所述,您之前用于将%连接到变量的块也是不必要的,因为我们只有在需要时才能在WHERE中执行这些操作(当变量不是时)空)。