【问题标题】:Why does MySQL return rows that seemingly do not match the WHERE clause?为什么 MySQL 返回看似与 WHERE 子句不匹配的行?
【发布时间】:2013-03-20 21:22:55
【问题描述】:

假设用户输入

mysite.com/profile?identity=1
mysite.com/profile?identity=dinodsja
mysite.com/profile?identity=1a

获取价值

$identity = $_GET['identity']; // identity can be user_id or user_name

我有一个简单的选择查询:

SELECT * FROM lb_users WHERE (user_id = 'dinodsja' OR user_name = 'dinodsja') AND user_status = 1

而且效果很好。但问题是:

SELECT * FROM lb_users WHERE (user_id = '1a' OR user_name = '1a') AND user_status = 1

当我执行这个查询时,它也会返回结果不满足条件

表结构:

user_id     bigint(25)
user_name   varchar(50)     utf8_general_ci

**

-> Is this a MySQL Bug ? 
-> How can we avoid this ? 
-> What will be the query ?

**

【问题讨论】:

  • 可以发一下表结构吗
  • 您将苹果(数字)与橙子(字符串)进行比较。
  • 当您知道字段的类型是数字时,验证用户输入的问题是什么?您希望社区提供什么样的答案来为这个问题提供悬赏?

标签: mysql sql select


【解决方案1】:

我认为您不能复制主键和 ID,我对其进行了测试并得出了一个正在运行的数据..您是否设置了 user_id 及其属性,例如:

user_id bigint(50) auto_increment primary key

这不是 mysql 错误。

【讨论】:

    【解决方案2】:
    SELECT * FROM lb_users WHERE (user_id = '1a' OR user_name = '1a') AND user_status = 1
    

    如果将 '1a' 更改为 1a,则会得到 1 个结果:

    #1064 - 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的 '1a LIMIT 0, 30' 附近使用正确的语法

    这不是bug,看看http://dev.mysql.com/doc/refman/5.0/en/where-optimizations.html

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      在处理字符串时,我会使用 'LIKE' 而不是 '=' 来避免这种无声的类型转换疯狂。 LIKE 可以处理字符串,所以为什么不使用它。

      【讨论】:

        【解决方案4】:

        我记得很久以前遇到过类似的问题。

        首先是一些背景知识:这不是一个错误。它实际上是一个功能。好的,这可能会导致这种意外行为,但 MySQL 因此非常宽容 w.r.t.用户输入,各自的选择查询:

        mysql> SELECT 'a' = 'a ';
                -> 1
        mysql> SELECT 'A' = 'a';
                -> 1
        

        因此,implicit type conversion 的结果,例如 INTEGER 中的'1a' 是 1,但也是:

        mysql> SELECT 0 = 'x6';
                -> 1
        mysql> SELECT 1 = ' 1';
                -> 1
        mysql> SELECT 1 = ' 1a';
                -> 1
        

        此功能也在其他非静态类型语言中实现。例如,PHP 将其称为 type juggling。请参阅文档中的PHP String conversion rules 和此示例:

        <?php
          $foo = "0";                     // $foo is string (ASCII 48)
          $foo += 2;                      // $foo is now an integer (2)
          $foo = $foo + 1.3;              // $foo is now a float (3.3)
          $foo = 5 + "10 Little Piggies"; // $foo is integer (15)
          $foo = 5 + "10 Small Pigs";     // $foo is integer (15)
        ?>
        

        JavaScript:

        <script>
          document.write(parseInt("40 years") + "<br>");
        </script>
        
        => 40
        

        不过,您的问题的解决方案非常简单:只需将整数转换为 char 并进行比较:

        mysql> SELECT * FROM lb_users WHERE (CAST(user_id AS CHAR) = '1' OR user_name = '1')
                -> 1
        mysql> SELECT * FROM lb_users WHERE (CAST(user_id AS CHAR) = '1a' OR user_name = '1a')
                -> 0
        mysql> SELECT * FROM lb_users WHERE (CAST(user_id AS CHAR) = 'dinodsja' OR user_name = 'dinodsja')
                -> 1
        

        我做了一个fiddle给大家试试看:http://sqlfiddle.com/#!2/c2835/14/0

        希望对你有帮助,

        -汉尼斯

        【讨论】:

        • 我不同意。这一个错误,而不是一个功能。
        • 我不同意。这是以各种语言实现的功能。例如,在 PHP 中,这称为类型杂耍。对于语法突出显示,我将编辑我的答案并添加一个代码示例。我确实认为这些语言之间保持一致是非常好的,因为它们经常在相同的上下文中使用。
        • 这对于 PHP(或其他弱类型语言)可能是正确的,但 SQL 是一种强类型语言(如 Java),这种行为不仅完全出乎意料,而且还违反了明确规定的 SQL 标准在这种情况下必须抛出异常
        • @a_horse_with_no_name MySQL 开发人员有调用 bug 功能的习惯。我报告了分区修剪逻辑中的错误。我得到的答复是关于实现的一些解释,而不是接受它作为错误。简单地说,我的论点是——答案应该是 2+2=4,他们的反应是:代码是 2-2,所以 0 是正确的。没有解释为什么这样做 - 而不是 + 当这是这样一个查询所期望的。见:bugs.mysql.com/bug.php?id=28928
        • 是的,我想你是对的。由于我不是 MySQL 开发人员,因此我不会为此辩护。我只是认为它已记录在案,所以他们不认为它是一个错误,因此我 - 用户 - 必须应对它。我认为如果您可以引用标准中的相应段落,这将是讨论的完美结束:-) 但我确实喜欢将缺陷记录为特征的想法。 “我们的软件非常棒!它具有数千种功能,并附带百科全书作为文档”。 “老兄,这是一个计算器……”
        【解决方案5】:

        根据你之前的留言

        它是个人资料的用户输入。用户可以提供 user_id 或 user_name。 所以输入是有效的。但没有数据。 – DBK 3 月 30 日 6:42

        我建议测试它是否为整数,如果是整数则仅搜索用户 ID。对于不处理 STRING 到 INT 比较的 mySQL 来说,这实际上是一种解决方法,但它应该可以工作。

        declare @InputVar varchar(10)
        set @InputVar = '1a'
        
        SELECT *  
        FROM lb_users
        WHERE  
          (case when isnumeric(@InputVar) = 1 then 
            case when (user_id = @InputVar OR user_name = @InputVar) then 1 else 0 end
          else  
            case when user_name = @InputVar then 1 else 0 end
          end =1 )
        And 
          user_status = 1
        

        【讨论】:

          【解决方案6】:

          原因是user_ID 列的数据类型是整数。

          MySQL 默默地删除任何尾随 NON-Number以及任何后面的)值,这就是为什么1a 是等于1,因为a 将在值中被删除。

          【讨论】:

          • 好的。感谢你。那么我们怎样才能避免这种情况。查询将是什么?
          • @DBK 你可以处理PHP中的检查值。
          • 它是个人资料的用户输入。用户可以提供 user_id 或 user_name。所以输入是有效的。但没有数据。
          • 在我进入 PHP 大约 1 年后,我意识到了这一点(int 列删除了非数字值)。这是一件小事,但当我遇到类似情况时确实让我很困扰
          • 我认为这是 MySQL 中的一个错误。每个其他 DBMS 都正确拒绝数字和非数字字符串之间的比较。并且没有其他 DBMS 试图变得聪明并删除部分比较值以避免错误消息(有返回错误结果的风险)
          猜你喜欢
          • 2013-07-28
          • 1970-01-01
          • 1970-01-01
          • 2018-08-25
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 2021-08-03
          • 2016-04-30
          相关资源
          最近更新 更多