【问题标题】:MySQL (exploding/matching array)MySQL(爆炸/匹配数组)
【发布时间】:2011-03-23 13:56:42
【问题描述】:

问题1:

MySQL 表

标识 |数组
1 | 1,2,3
2 | 2
3 | 2,3
4 | 4,5,6

$_GET['id'] = 2;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");

在这一步中,我想遍历整个数组,看看它是否与$_GET['id']匹配,所以它应该输出:

ID:1,2,3

问题2:

MySQL 表

标识 |数组
1 | 4,5,6
2 | 3,4,7

$_GET['id'] = 4;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");

在这一步中,我想匹配数组中的第一个元素,所以它应该输出:

id:4

我只能考虑使用 PHP 来执行此操作,但如果可能的话,我宁愿在 MySQL 查询中执行所有这些操作。

$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
    $elements = explode(',', $b['array']);
    foreach($elements as $element)
    {
        if($element == $_GET['id'])
        {
            echo $b['id'].'<br />';
        }
    }
}

$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
    $array = $b['array'];

    if(in_array($_GET['id'], $array))
    {
        echo $b['id'].'<br />';
    }
}

那看起来很糟糕。

【问题讨论】:

  • 规范化你的数据库架构

标签: php mysql arrays explode


【解决方案1】:

第一个:

SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]

第二个:

SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]

解释:

  • '$_GET[id],%' 将匹配,如果数组以 $_GET[id] 开头
  • '%,$_GET[id],%' 将匹配,如果 $_GET[id] 在任意两个数组项之间
  • '%,$_GET[id]' 将匹配,如果数组以 $_GET[id] 结尾
  • array = '$_GET[id]' 匹配,如果数组只包含一项等于$_GET[id]

【讨论】:

  • '23,4' LIKE '3',它不是真的。 '3,4' 就像 '3' 是真的。
【解决方案2】:

已经提到您可以/应该以不同的方式构建数据库(请参阅http://en.wikipedia.org/wiki/Database_normalization)。但是……

FIND_IN_SET()

mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2

例如

<?php
$mysql = init();    
bar($mysql, 1);
bar($mysql, 2);
bar($mysql, 3);
bar($mysql, 4);


function bar($mysql, $x) {
  $sql_x = mysql_real_escape_string($x, $mysql);
  $result = mysql_query("SELECT id, foo FROM soTest WHERE FIND_IN_SET('$sql_x', foo)", $mysql) or die(mysql_error());

  echo "$x:\n";
  while( false!==($row=mysql_fetch_array($result, MYSQL_ASSOC)) ) {
    echo $row['id'], ' ', $row['foo'], "\n";
  }
  echo "----\n";
}

function init() {
  $mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
  mysql_select_db('test', $mysql) or die(mysql_error());
  mysql_query('CREATE TEMPORARY TABLE soTest (id int auto_increment, foo varchar(64), primary key(id))', $mysql) or die(__LINE__.' '.mysql_error());
  mysql_query("INSERT INTO soTest (foo) VALUES ('1,2,3'), ('2,4'), ('3'), ('2,3'), ('1,2')", $mysql) or die(__LINE__.' '.mysql_error());
  return $mysql;
}

打印

1:
1 1,2,3
5 1,2
----
2:
1 1,2,3
2 2,4
4 2,3
5 1,2
----
3:
1 1,2,3
3 3
4 2,3
----
4:
2 2,4
----

MySQL 不能使用索引来执行此搜索,即查询导致全表扫描,请参阅Optimizing Queries with EXPLAIN


编辑:
对于第二个问题,您只需将 WHERE 子句更改为
WHERE FIND_IN_SET('$sql_x', foo)=1

【讨论】:

    【解决方案3】:

    如果您无法规范化您的架构(这是最好的选择:

    SELECT * 
      FROM table 
     WHERE ','+array+',' LIKE '%,$_GET[id],%' 
    

    但是如果你需要通过 id 访问记录,那么你真的应该规范化

    【讨论】:

      【解决方案4】:

      我建议您将数据库带到first normal form,例如。 g.

      CREATE TABLE t_master (
          id INT PRIMARY KEY AUTO_INCREMENT
      );
      
      CREATE TABLE t_array (
          id INT PRIMARY KEY AUTO_INCREMENT,
          master_id INT NOT NULL,
          value INT,
          CONSTRAINT fk_array_master_id FOREIGN KEY (master_id) REFERENCES t_master (id)
      );
      

      然后您可以在t_master 中找到具有特定value 的记录

      $q = 'SELECT m.* ' .
          'FROM t_master AS m INNER JOIN t_array AS a ON a.master_id = m.id ' .
          "WHERE a.value = '" . mysql_real_escape_string($_GET['id'], $db) . "' " .
          'GROUP BY m.id';
      

      最重要的优势是,如果你有很多值,你可以添加一个索引来更快地找到它们:

      ALTER TABLE t_array ADD INDEX idx_value (value);
      

      一个不太明显但不是最后一个优点是您的查询变得更加合乎逻辑和结构化。

      【讨论】:

        【解决方案5】:

        您在 DB 中的数据结构并不是以您想要的方式进行查询的最佳选择。

        第一个问题:

        mysql_query("SELECT * FROM table WHERE array LIKE '%,$_GET[id],%' OR array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]'");
        

        第二个:

        mysql_query("SELECT id, SUBSTR(array, 1, POSITION(',' IN array) - 1) AS array FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]'");
        

        如您所见,这些查询并不漂亮,但它们会按照您的意愿行事。

        【讨论】:

        • 第一个问题,当值在字符串的开头时呢?它不匹配(之前没有 , )。另外不要忘记清理您的输入!!
        • 嘿,谢谢!为什么,否则你会如何构建它?我只是认为为某些任务保存数组会更容易。
        • @Blair:如果数字开始数组,或者是数组中的唯一元素,则查询匹配。 @user317005:您应该将每个数组值保存为单独的行,例如 create table table_name (id int, value int) 每个 id 会有多行,但查询要容易得多。
        • 对不起,不知什么原因我没有注意到 OR 的存在,所以我只看到了第一个 LIKE。 @user317005 正如 jmz 所说,您有效地存储了 MANY-MANY 关系(每个“项目”可以有许多“值”,每个“值”可以属于许多“项目”)。有很多关于如何做到这一点的文章。
        【解决方案6】:

        未经测试,但您应该可以使用:

        问题一:

        SELECT * FROM table WHERE array REGEXP '(^|,)?(,|$)';
        // Match either the start of the string, or a , then the query value, then either a , or the end of the string
        

        问题 2:

        SELECT * FROM table WHERE array REGEXP '^?(,|$)';
        // Match the start of the string, then the query value, then either a , or the end of the string
        

        其中? 替换为您的$_GET 值。 不知道它的性能。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-19
          • 2019-07-26
          • 2016-02-08
          • 1970-01-01
          相关资源
          最近更新 更多