将PHP数组保存到MySQL?

95

如何将数据数组保存到单个mysql字段中?

在mysql表中查询该数组后,如何将其恢复为数组形式?

使用序列化(serialize)和反序列化(unserialize)是否是解决方案?

14个回答

92

没有一种好的方法可以将数组存储到单个字段中。

您需要检查关系数据并对模式进行适当的更改。有关此方法的参考,请参见下面的示例。

如果您必须将数组保存到单个字段中,则 serialize()unserialize() 函数可以解决问题。但是您不能对实际内容执行查询。

作为串行化函数的替代方案,还有 json_encode()json_decode()

考虑以下数组

$a = array(
    1 => array(
        'a' => 1,
        'b' => 2,
        'c' => 3
    ),
    2 => array(
        'a' => 1,
        'b' => 2,
        'c' => 3
    ),
);

为了将其保存在数据库中,您需要创建一个类似于此的表格

$c = mysql_connect($server, $username, $password);
mysql_select_db('test');
$r = mysql_query(
    'DROP TABLE IF EXISTS test');
$r = mysql_query(
    'CREATE TABLE test (
      id INTEGER UNSIGNED NOT NULL,
      a INTEGER UNSIGNED NOT NULL,
      b INTEGER UNSIGNED NOT NULL,
      c INTEGER UNSIGNED NOT NULL,
      PRIMARY KEY (id)
    )');

要处理记录,你可以执行以下查询(注意,这只是一个示例,请小心!)

function getTest() {
    $ret = array();
    $c = connect();
    $query = 'SELECT * FROM test';
    $r = mysql_query($query,$c);
    while ($o = mysql_fetch_array($r,MYSQL_ASSOC)) {
        $ret[array_shift($o)] = $o;
    }
    mysql_close($c);
    return $ret;
}
function putTest($t) {
    $c = connect();
    foreach ($t as $k => $v) {
        $query = "INSERT INTO test (id,".
                implode(',',array_keys($v)).
                ") VALUES ($k,".
                implode(',',$v).
            ")";
        $r = mysql_query($query,$c);
    }
    mysql_close($c);
}

putTest($a);
$b = getTest();

connect()函数返回一个mysql连接资源

function connect() {
    $c = mysql_connect($server, $username, $password);
    mysql_select_db('test');
    return $c;
}

27

一般情况下,序列化和反序列化是行之有效的方法。

不过如果你的数据比较简单,那么将其保存为逗号分隔的字符串可能更好,因为这样可以节省存储空间。例如,如果你知道你的数组只是一个数字列表,那么你应该使用implode/explode函数。这就是 1,2,3a:3:{i:0;i:1;i:1;i:2;i:2;i:3;} 之间的区别。

如果不是这种情况,那么序列化和反序列化都适用于所有情况。


24
既然您在这里阅读,我想趁此宣布,经过多年的经验积累,我已经发现这并不是正确的方法。被认可的答案是更为有效的方法。 - Matchu
4
既然你在这里,读者,我将告诉你,这取决于你的需求。如果你不需要按数组值查询并且需要非常快速的东西,你可以安全地将数据像这样存储在单个字段中(这正是问题明确提出的方式),加上一个主键,然后就可以开始了。如果你想知道应用程序是什么:将数据推送到每5分钟cron一次的队列中。另一个cron创建数组需要更多时间。队列获取计算的数据并将其传递给第三方API。没有更好的方法来实现它。 - Rid Iculous
1
@RidIculous 我有点困惑,你是在谈论你自己的个人应用程序还是与问题相关的应用程序? - Peter Lindqvist
1
虽然这与我的陈述前提无关,但前缀“如果你想知道应用程序是什么”应该能提供清晰度。 - Rid Iculous

12

只需使用 PHP 的 serialize 函数:

<?php
$myArray = array('1', '2');
$seralizedArray = serialize($myArray);
?>

但是,如果您使用的是像这样的简单数组,那么最好使用 implode 和 explode。请改用空白数组而不是 new。


11

序列化/反序列化数组以便在数据库中存储。

访问http://php.net/manual/en/function.serialize.php

来自PHP手册:

请在页面上查找“返回”

返回包含可以存储在任何地方的值的字节流表示的字符串。

请注意,这是一个二进制字符串,其中可能包含空字节,并且需要作为这样进行存储和处理。 例如,serialize()输出通常应存储在数据库中的BLOB字段中,而不是CHAR或TEXT字段中。

注意:如果要将HTML存储到Blob中,请确保对其进行base64编码,否则它可能会破坏序列化函数。

示例编码:

$YourSerializedData = base64_encode(serialize($theHTML));

$YourSerializedData 现在已经准备好存储到 blob 中。

从 blob 获取数据后,您需要进行 base64_decode 解码,然后反序列化。 解码示例:

$theHTML = unserialize(base64_decode($YourSerializedData));

9

我发现最好的方法是将数组保存为带有分隔符字符的数据字符串。

$array = array("value1", "value2", "value3", "...", "valuen");
$array_data = implode("array_separator", $array);

$query = "INSERT INTO my_tbl_name (id, array_data) VALUES(NULL,'" . $array_data . "');";

您可以使用简单的查询搜索存储在数组中的数据

$query = "SELECT * FROM my_tbl_name WHERE array_data LIKE '%value3%'";

使用explode()函数将"array_data"字符串转换为数组。
$array = explode("array_separator", $array_data);

请注意,这不适用于多维数组,请确保您的“array_separator”是唯一的,并且不存在于数组值中。
小心!如果您只是将表单数据放入数据库中,那么您将会陷入困境,因为表单数据不是SQL安全的!您必须使用mysql_real_escape_string处理表单值,或者如果您使用MySQLi,则使用mysqli::real_escape_string。如果值是整数或布尔值,请对它们进行强制转换(int)(boolean)。
$number = (int)$_POST['number'];
$checked = (boolean) $_POST['checked'];

$name = mysql_real_escape_string($db_pt, $_POST['name']);
$email = mysqli_obj->real_escape_string($_POST['email']);

在我的情况下,我觉得这是最好的选择。 - Imtiaz

6

序列化和反序列化在IT技术中非常常见。您还可以使用JSON,通过json_encode和json_decode来使用一种更少与PHP相关的格式。


6

如前所述 - 如果您不需要在数组中搜索数据,则可以使用序列化 - 但这仅适用于"php"。因此,我建议使用json_decode / json_encode,不仅能提高性能,也更易于阅读和可移植性(其他语言例如javascript可以处理json_encoded数据)。


3

嗯,我不知道为什么大家都建议序列化数组。

我认为,最好的方法是将其适配到您的数据库模式中。我不知道(您也没有给出任何线索)数组中实际语义含义,但通常有两种存储这样序列的方法。

create table mydata (
  id int not null auto_increment primary key,
  field1 int not null,
  field2 int not null,
  ...
  fieldN int not null
)

这样做可以将你的数组存储在单行中。
create table mydata (
    id int not null auto_increment primary key,
    ...
)

create table myotherdata (
    id int not null auto_increment primary key,
    mydata_id int not null,
    sequence int not null,
    data int not null
)

第一种方法的缺点显然是,如果你的数组中有许多项,那么与该表格一起工作就不太优雅。而且使用可变长度序列是不切实际的(当然可以,但也非常不优雅,只需使列可为空即可)。
对于第二种方法,您可以拥有任意长度的序列,但仅限于一种类型。当然,您可以将该类型设置为varchar或其他内容,并对数组的项进行序列化。虽然不是最好的选择,但肯定比序列化整个数组要好,对吧?
无论哪种方法,都具有能够访问序列中任意元素的明显优势,您无需担心序列化数组等丑陋的问题。
至于如何获取它,好吧,用查询获取相应的行/序列,然后使用循环.. 对吧?

1
我必须强烈反对 - 对于我来说,在关系型数据库中存储随机的、非结构化的数据(php“数组”实际上根本不是数组,对吧?)和未经分类的数据块似乎相当不合适。如果您的数组中可能有任何字符串,那么您将使用什么分隔符呢?这只会在许多不同的方面引起麻烦。在99.9%的情况下,还有另一种更自然的方法。我要大胆猜测一下,并建议提问者的情况不属于剩余的0.1%。 - shylent
2
实际上,有时将数组存储在数据库的字段中非常合适。例如元数据,如全局“变量”表,系统插件和模块可以将其各种设置存储在其中。存储任意“会话”数据的表可能最好使用保存键/值的关联数组的字段来实现。序列化和反序列化(这是正确答案)负责分隔符。查看一些成功且受欢迎的开源项目,如WordPress或Drupal - 您将看到有时以这种方式在数据库中存储数组。 - Scott Evernden
5
将一个数组存储在数据库字段中永远不是合适的做法。这不仅违反了第四范式、第三范式甚至第二范式,而是违反了第一范式。如果您甚至无法遵守第一范式,那么您的应用程序肯定存在严重问题。Drupal和WordPress也是如此;如果它们这样做,那肯定不是因为它们的应用程序是良好设计的。我曾在上一份工作中进行过这个争论,因为XCart在做这种无意义的事情,所以本来应该很容易的东西变得非常困难。 - Dexygen
@Justin,那么你不同意所有了解数据库的人,包括Codd和Date先生。 - Dexygen
@Justin 非常有趣,谢谢。我想我之前了解不够,过于教条化了。 - Dexygen
显示剩余9条评论

2
你可以将数组保存为JSON格式。
这里有关于JSON数据类型的文档:https://dev.mysql.com/doc/refman/5.7/en/json.html
我认为这是最好的解决方案,避免使用复杂的函数,让您的代码更易读。
希望对你有所帮助。

0

请查看implode函数,由于值在数组中,您想将数组的值放入mysql查询中,将值插入表中。

$query = "INSERT INto hardware (specifications) VALUES (".implode(",",$specifications).")";

如果数组中的值是文本值,您需要添加引号。
$query = "INSERT INto hardware (specifications) VALUES ("'.implode("','",$specifications)."')";

mysql_query($conn,$query);

另外,如果您不想要重复的值,请将“INto”切换为“IGNORE”,只有唯一的值才会插入到表中。

更新

警告 此扩展在PHP 5.5.0中已弃用,并在PHP 7.0.0中删除。应使用MySQLi或PDO_MySQL扩展。另请参见MySQL:选择API指南。此函数的替代方法包括:

mysqli_query

PDO :: query()


如果您有一个主键,在使用INSERT INTO时不会出现重复记录。如果您没有主键或索引,IGNORE语句则不会产生任何影响。 - Peter Lindqvist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接