仅当过滤器值不是空字符串、空格或空值时过滤 MySQL 查询最佳实践

Posted

技术标签:

【中文标题】仅当过滤器值不是空字符串、空格或空值时过滤 MySQL 查询最佳实践【英文标题】:Filtering MySQL query only when the filter values are not empty-string, white-space or null best practice 【发布时间】:2021-12-16 02:19:12 【问题描述】:

我想在这里过滤列 A 的值,然后是列 B 等,但是我希望如果过滤器 valueA 只是一个空字符串,则不会发生过滤并且所有值都被接受。如果 valueB 为空,则相同,则不会发生过滤。

这是一个通用的 mysql 查询,我希望仅在 valueA 或 valueB(包括或)具有不为 null、空格或空字符串的值时过滤。

   using (var cmd = new MySqlCommand(
                    " SELECT columnA, columnB etc
                    " FROM table" +
                    " WHERE columnA = '" + valueA + "' " +
                    " THEN BY columnB = '" + valueB + "' " +
                    " GROUP BY columnA" +
                    " LIMIT 0, 100",
                    connection))

【问题讨论】:

您应该使用参数,而不是串联 - 否则如果 valueA 包含撇号,您的查询将中断。 或者更糟糕的是,你最终成为causing this的唯一负责人 谢谢,我不知道有更好的方法来插入 where 子句的值。你能详细说明一下吗? bobby-tables.com 深入讨论了使用 concat 值构建 SQL 字符串的风险,并给出了如何在代码中避免这种风险的示例。从本质上讲,SQL 是一种完全属于自己的编程语言,您将其放入您的应用程序中,然后发送到其他地方,编译并运行。如果您让某人在程序中间插入任何旧东西,这是一个安全漏洞;他们不需要破解密码即可进入您的数据库,他们只需获取您的代码即可为他们运行代码 thanx,这是我猜的注入漏洞? 【参考方案1】: SQL 作为一种语言,不支持动态谓词。 ISO SQL 设计委员会知道这一点,看到我们所有人都不得不使用他们极其不符合人体工程学和过于冗长的查询语言,他们感到很高兴。 要拥有条件谓词或动态谓词,您需要使用某种形式的字符串连接来重建查询。 有些人建议将查询参数设置为 NULLable 并滥用 @value IS NULL OR [Column] = @value 但是这在生产数据库中(可能)是一件可怕的事情,因为(即使到 2021 年)我也不是意识到任何主要的 RDMBS 都能正确处理这种情况,而您最终会得到非常非常糟糕的执行计划。

在你的情况下,做这样的事情:

String sql;
List<MySqlParameter> parameters = new List<MySqlParameter>();


    StringBuilder sb = new StringBuilder( "SELECT columnA, columnB, FROM table" ).AppendLine();
    
    List<String> predicates = new List<String>();
    if( valueA != null )
    
        predicates.Add( "columnA = @pA" );
        parameters.Add( new MySqlParameter( "@pA", valueA ) );
    

    if( valueB != null )
    
        predicates.Add( "columnB = @pB" );
        parameters.Add( new MySqlParameter( "@pB", valueB ) );
    

    // etc
    
    if( predicates.Count > 0 )
    
        _ = sb.AppendLine( "WHERE" );
        _ = sb.AppendLine( String.Join( " AND ", predicates ) );
    

    //

    _ = sb.AppendLine( "ORDER BY foo GROUP BY bar LIMIT 0, 100" );

    sql = sb.ToString();


using( var cmd = new MySqlCommand( sql, connection ) )

    cmd.Parameters.AddRange( parameters );

    cmd.Execute...


【讨论】:

另外,一边思考,您的“返回值未使用”分析器是否比大多数分析器更具侵略性? @CaiusJard 是的 - 我知道这听起来很无聊,但是自从将其设置为警告(而不是被禁用)后,我在我的编程代码中发现(并防止了)无数错误。未使用的方法参数同上。 (但很奇怪,当只读属性从未设置时,C# 仍然不会发出警告(#nullable 警告不适用于值类型属性或可为空的引用类型属性)。【参考方案2】:

您可以使用简单的OR 逻辑,并且在 C# 端检查空格更容易

注意使用参数化防止SQL注入

注意使用多行字符串以使查询更易于阅读

const string query = @"
SELECT columnA, MAX(columnB) etc
FROM table
WHERE (columnA = @valueA OR @valueA IS NULL)
  AND (columnB = @valueB OR @valueB IS NULL)
GROUP BY columnA
LIMIT 0, 100;
";
using (var cmd = new MySqlCommand(query, connection))

    cmd.Parameters.AddWithValue("@valueA", string.IsNullOrWhiteSpace(valueA) ? (object)DBNull.Value : valueA);
    cmd.Parameters.AddWithValue("@valueB", string.IsNullOrWhiteSpace(valueB) ? (object)DBNull.Value : valueB);

大型表的性能可能不是很好,因此您可能需要使用完全独立的查询

【讨论】:

不要这样做。它的性能会很差,因为 SQL 查询引擎不会将( x = @x OR @x IS NULL ) 风格的谓词视为 SARGable 以及破坏缓存的执行计划。如果 MySQL 有,那我就吃我的话,但 Oracle、MS SQL 和其他人不能很好地处理这种情况。 我确定我在某处读过一些关于 MySQL 如何处理这种性质的查询的文章,即它可以确定例如 @valueA IS NULL 始终为真,它删除了整个谓词..可以'现在找不到它,但我认为它在官方文档中,对优化器如何重写查询进行了一些深入研究..

以上是关于仅当过滤器值不是空字符串、空格或空值时过滤 MySQL 查询最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

当数组只有一个值时处理条件逻辑,即使它是一个空字符串?

工具类(过滤接口空值, value 或 空字符串) - iOS

js 过滤所有空字符串

用空值替换空字符串

excel过滤功能

JS判断数据类型以及数据过滤空值方法