【问题标题】:MySQL Query for values that are not null, where key is part of a stringMySQL 查询非空值,其中键是字符串的一部分
【发布时间】:2011-11-15 19:38:54
【问题描述】:

我的数据库设置如下(简化):

+----------+---------+----------+
|   path   |  val1   |   val2   |
+----------+---------+----------+
|    a/    |   two   |   cow    |
+----------+---------+----------+
|   a/b/   |   one   |   cat    |
+----------+---------+----------+
|  a/b/c   |   NULL  |   bat    |
+----------+---------+----------+

path是主键(实际使用中,更像Smith/John/Mark/Jr)。

PHP提供路径$mypath = "a/b/c";

有没有办法构建一个非空值的结果集,从path 是$mypath 一部分的所有行中进行选择。
也就是说,要搜索最精确匹配的键,找到其中所有非空值,如果有空值,则从第二个“最佳”键中获取它们。

例如。 Mysql 应该检查 a/b/c 并返回一个 val2 = "bat",但看到 val1 设置为 NULL。
因此它应该检查 a/b/ 并设置 val1 = "one";
最终结果应该是array('val1' => "one", 'val2' => "bat")

一位朋友告诉我有一种方法可以使用“LOCATE”,但我无法解决。
这可行吗?

知道 MySQL 的人可以帮我改写这个问题的标题,使其更具描述性吗?

谢谢。

【问题讨论】:

    标签: mysql


    【解决方案1】:

    路径的过滤器是否严格从左到右比较,并且没有中间匹配?似乎是这样,但我想确定一下。

    另外,我不确定您确定应该选择 III 的逻辑依据/规则是什么,但如果val2 是一个数字列,这里有一个示例:

    CREATE PROCEDURE `test`(IN phpPath VARCHAR(50))
    BEGIN
    
    SELECT A.val1, B.val2
    FROM 
     (SELECT val1 FROM `yourtablehere` 
     WHERE val1 IS NOT NULL AND instr(phpPath, path) = 1 
     ORDER BY length(path) DESC) A
     , (SELECT val2 FROM `yourtablehere` 
     WHERE val2 IS NOT NULL AND instr(phpPath, path) = 1 
     ORDER BY length(path) DESC) B
    LIMIT 1
    
    END
    

    您能否对此进行测试并告诉我们您是否获得了所需的结果? (同样,首先假设 val2 列是数字)

    我还建议您在代码中使用存储过程而不是嵌入式 SQL 命令,因此我也包含了 create 语法。 使用存储过程和适当的参数绑定可以帮助您对抗 SQL 注入。 请注意,SELECT 命令需要一个参数/变量来包含包含一行的基础。

    [edit] 我知道代码太不优雅了,但你能试试这个,看看它是否有效?尝试用不同的场景进行测试...

    【讨论】:

    • 准备好的语句参数可以防止注入,但程序不会;它们同样容易受到攻击,因为在提交查询时会发生注入,甚至在程序进入之前。
    • 我的意思是结合使用带有 bindValue/bindParam 的 PDO 准备语句——抱歉不清楚:-)
    • > 从左到右严格比较,没有中间匹配。我不明白你的意思? > 首先假设列 val2。抱歉,val2 不是数字。我没有意识到这会改变任何事情。我已经运行了上面的查询,结果是 Min(val1) => NULL, Max(val2) => NULL
    • 对不起!我将pathphpPath 参数切换为INSTR! :(请再试一次。你能告诉我你是怎么用III而不是其他人结束的吗?
    • 首先 - 非常感谢您的帮助。我不知道你是怎么理解这些东西的。其次,为了清楚起见,我更新了问题中的变量。第三,运行更正后的查询将返回 a/b/c 的完整行,而不是 val2=>bat。第三,解释一下——我想要 bat,因为那是 val2,其键与我的字符串最匹配,而不是一个,因为那是 val1,其键与我的字符串最匹配。假设两者都 NULL 是不可接受的。
    【解决方案2】:

    LOCATE(x,y) 返回xy 中的位置(或0,如果x 不是子字符串)。如果xy 的前缀,那么LOCATE(x,y) = 1 就是您要用作条件的。如果您不希望 'Smith/Jo' 匹配 'Smith/John/Mark/Jr',则需要在条件中添加一些内容以防止这种匹配,例如将 '/' 连接到路径列值,这将影响性能,因为 MySQL 将无法使用 path 列上的任何索引。至于找到最长的路径匹配(“最佳”是一个非常难以描述的词,通常应该避免使用),您可以使用排序来获得最长的第一个,LIMIT 忽略较短的结果。

    SELECT val2
      FROM tbl
      WHERE (path = ? OR LOCATE(CONCAT(path, '/'), ?) = 1)
        AND val1 IS NOT NULL
      ORDER BY STRLEN(path)
      LIMIT 1
    

    【讨论】:

    • 我在这个查询中得到一个空结果集。不知道我可能做错了什么
    • 我的错,我在简化时犯了一个错误。我已经更新了问题中的数据集。更正时[这已经很有帮助],我使用您的查询获得了最上面的行。但我不想要最上面的行 - 我想将非空结果合并在一起。有没有办法做到这一点。
    【解决方案3】:

    总结:
    仅使用 SQL 可能无法按要求执行。
    但是,SQL 可以从匹配的键中得到一个多维数组,而 PHP 可以将其缩减为一个数组。

    如果有人可以改进它以仅使用 SQL(如发布的问题中所示),将不胜感激。

    SQL:

    SELECT * FROM `mytable` WHERE `path` = '$mypath' 
    OR (LOCATE(`path`, '$mypath') = 1 AND RIGHT(`path`, 1) = '/') 
    ORDER by length(`page`) desc;
    

    PHP:

    $results = mysql_query(... sql ...);
    $pg = array();
    foreach(array_keys($results[0]) as $key)
        foreach($results as $result)
            if ($result[$key]){ $pg[$key] = $result[$key]; continue 2; }
    

    你可以在http://codepad.org/UnyEZJ6a看到这个实践

    注意事项:

    • locate(Outis 使用)和 instr(使用 Nonym)相同,只是参数的顺序颠倒了。
    • 在查询中使用right() 来确保路径仅在正斜杠上分割。
    • OR 中使用的括号在技术上是多余的,因为 AND 在 OR 之前进行评估。
    • 如果 sql 中有解决方案,我被告知它将使用 group_by 命令。

    希望这可以帮助其他人!

    【讨论】:

      猜你喜欢
      • 2013-11-22
      • 2014-02-22
      • 1970-01-01
      • 2021-06-16
      • 2021-12-03
      • 2022-01-15
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多