序列化数组中的mysql选择查询
Posted
技术标签:
【中文标题】序列化数组中的mysql选择查询【英文标题】:mysql select query within a serialized array 【发布时间】:2011-05-06 05:02:25 【问题描述】:我将项目列表存储在我的数据库字段中的序列化数组中(我使用的是 php/mysql)。
我想要一个查询,它将选择包含数组中这些项目中特定一项的所有记录。
类似这样的:
select * from table WHERE (an item in my array) = '$n'
希望这是有道理的。
任何想法都将不胜感激。
谢谢
【问题讨论】:
除非您的项目有一种非常独特的识别方式,否则您最好将序列化数据存储为表格或其他内容。 哪种数组?整数?字符串?混合?你能提供一些样本记录吗? 永远不要存储您要搜索的序列化数据... Select * from table where table_field like '%"enter_your_value"%' 【参考方案1】:正如 GWW 在 cmets 中所说,如果您需要以这种方式查询事物,您真的应该考虑将此数据存储为 big-ole-string 以外的其他东西(这是您的序列化数组)。
如果这不可能(或者你只是懒惰),你可以利用序列化数组只是一个大字符串这一事实,并找出一个 LIKE 子句来查找匹配的记录。 PHP 序列化数据的方式很容易弄清楚(提示:这些数字表示事物的长度)。
现在,如果您的序列化数组相当复杂,它会很快崩溃。但是如果是平面数组,应该可以的。
当然,您将使用 LIKE '%...%',因此您不会从任何指标中获得帮助,而且性能会很差。
这就是为什么人们建议您以某种规范化的方式存储该数据,如果您需要在“内部”进行查询。
【讨论】:
【参考方案2】:您可能正在寻找 SQL IN 语句。
http://www.w3schools.com/sql/sql_in.asp
不过,您必须先将数组拆分一下。您不能只是将数组交给 MySQL 并期望它知道如何处理它。为此,您可以尝试使用 PHP 的 explode 将其序列化。
http://php.net/manual/en/function.explode.php
【讨论】:
【参考方案3】:所以你的意思是使用 MySQL 在一个 PHP 数组中搜索,该数组已经用 serialize 命令序列化并存储在数据库字段中?我的第一反应是:天啊。我的第二反应是:为什么? 明智的是:
-
将数组检索到 PHP 中,反序列化并在其中搜索
忘记将数据存储在 MySQL 中作为序列化并将其存储为常规表并对其进行索引以进行快速搜索
我会选择第二个选项,但我不知道你的上下文。
当然,如果您真的想这样做,您可以尝试使用SUBSTRING
或其他 MySQL 函数并尝试操作该字段,但我不明白您为什么要这样做。这很麻烦,而且这将是一个不必要的丑陋黑客。另一方面,这是一个谜题,这里的人们往往喜欢谜题,所以如果你真的想要然后发布你的领域的内容,我们可以试一试。
【讨论】:
是的,看起来我最好的选择是忘记数组并将其存储在自己的表中。谢谢! > “天啊。我的第二反应是:为什么?明智的做法是:” 多么居高临下。通常,在使用 Drupal 和 WordPress 等 CMS 系统时,您无法控制其他一些愚蠢的程序员存储数据的格式。 OMG 评论有一些优点。在数组是数据库键的序列化数组的情况下,将序列化代码写入数据库表是有问题的。这个 SO question 就是这个问题的一个很好的例子。序列化一个数据库键数组,并将它们存储在数据库中,这将在以后对某些类型的查找产生性能损失。 虽然我同意数据不应该以这种方式存储这一事实,正如@MikeSchinkel 提到的那样,但我们有时无法选择不同的方式。 您可能会搜索存储在 dbase 中的数组/序列化文本字段中的一组“id”,以查看过去交易中是否使用了“id”值。同意一个新表将起作用,除非 OP 正在为 Wordpress 开发其中这将被视为元数据并且需要进入后元表并完成以避免大量新行。【参考方案4】:您如何序列化您正在搜索的值?
$sql = sprintf("select * from tbl WHERE serialized_col like '%%%s%%'", serialize($n));
或
$sql = sprintf("select * from tbl WHERE serialized_col like '%s%s%s'", '%', serialize($n), '%');
【讨论】:
+1 用于提供更好的方法来将转义值放置到位,对于那些可能认为这可以保护他们免受 sql 注入的人 remember you must still escape your value 否则魔鬼会抓到你。例如,sprintf("select * from tbl WHERE serialized_col like '%%%s%%'", $mysqli->real_escape_string(serialize($n)));
【参考方案5】:
你可以这样做:
SELECT * FROM table_name WHERE some_field REGEXP '.*"item_key";s:[0-9]+:"item_value".*'
但无论如何,您应该考虑将该数据存储在单独的表中。
【讨论】:
有效!没有意识到你可以在 mysql 中进行正则表达式搜索。显然很慢,但是很棒!请注意,serialize 不会转义特殊字符值,因此 item_value 需要替换为 db 仅转义字符串。【参考方案6】:如果您可以控制数据模型,从长远来看,将序列化数据填充到数据库中几乎总是会给您带来麻烦。然而,一个通常无法控制数据模型,例如在使用某些开源内容管理系统时。 Drupal 会在垃圾箱列中粘贴 很多 序列化数据来代替适当的模型。例如,ubercart 的所有订单都有一个“数据”列。贡献的模块需要将数据附加到主订单实体,因此为了方便他们将其附加到序列化的 blob 上。作为第三方,我仍然需要一种方法来获取其中的一些数据以回答一些问题。
a:4:s:7:"cc_data";s:112:"6"CrIPY2IsMS1?blpMkwRj[XwCosb]gl<Dw_L(,Tq[xE)~(!$C"9Wn]bKYlAnS[Kv[&Cq$xN-Jkr1qq<z](td]ve+Xi!G0x:.O-"=yy*2KP0@z";s:7:"cc_txns";a:1:s:10:"references";a:1:i:0;a:2:s:4:"card";s:4:"3092";s:7:"created";i:1296325512;s:13:"recurring_fee";b:1;s:12:"old_order_id";s:2:"25";
看到那个'old_order_id'了吗?这就是我需要找出这个经常性订单来自哪里的关键,但由于不是每个人都使用经常性订单模块,因此没有合适的位置将其存储在数据库中,因此模块开发人员选择将其填充到垃圾箱表中。
我的解决方案是使用一些有针对性的 SUBSTRING_INDEX 来剔除无关紧要的数据,直到我将结果字符串雕刻成我想要的数据宝石。 然后我添加一个 HAVING 子句来查找所有匹配项,如下所示:
SELECT uo.*,
SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX( uo.data, 'old_order_id' , -1 ),
'";', 1),
'"',-1)
AS `old order id`
FROM `uc_orders AS `uo`
HAVING `old order id` = 25
最里面的 SUBSTRING_INDEX 为我提供了 old_order_id 之后的所有内容,而外面的两个则清理了其余部分。
这种复杂的hackery并不是你想要的代码运行多次的东西,它更多的是一种无需编写php脚本就可以从表中获取数据的工具。 p>
请注意,这可以简化为仅
SELECT uo.*,
SUBSTRING_INDEX(
SUBSTRING_INDEX( uo.data, '";' , 1 ),
'"',-1)
AS `old order id`
FROM `uc_orders` AS `uo`
HAVING `old order id` = 25
但这仅适用于这种特定情况(我想要的值位于数据 blob 的末尾)
【讨论】:
> “但是,通常人们无法控制数据模型,例如在使用某些开源内容管理系统时。”对于这里所有其他告诫人们不要以关系形式使用日期的人。别人的罪,你是改不了的! 这比以前的 cmets 更有帮助......就像你说的,Drupal/Ubercart 对此感到内疚,所以告诉人们他们应该重建房子,而他们所要求的只是如何改变灯泡并不完全有帮助【参考方案7】:嗯,我也遇到了同样的问题,显然这是小菜一碟,但可能需要更多测试。
只需使用 IN 语句,但将字段本身作为数组! 示例:
SELECT id, title, page FROM pages WHERE 2 IN (child_of)
~ 其中“2”是我在字段“child_of”中寻找的值,该字段是一个序列化数组。
这个序列化数组是必要的,因为我不能复制记录只是为了存储它们的子 ID。
干杯
【讨论】:
如果您要搜索的值不太可能意外地存在于其他任何地方,这将起作用。例如'2' 在序列化数组中搜索将是一件可怕的事情,因为 php 将其数据值的大小放入输出字符串中,因此您会匹配很多很多结果。即在我的情况下,我正在搜索 25。它遇到了许多错误匹配。【参考方案8】:select * from postmeta where meta_key = 'your_key' and meta_value REGEXP ('6')
【讨论】:
【参考方案9】:Select * from table where table_field like '%"enter_your_value"%'
【讨论】:
这里有很多不好的答案,但据我所知,这是最好的方法。因为序列化的数组在存储的值周围加上引号,这将确保即使您正在寻找数字 1 之类的东西,您也不会从元数据中得到误报,例如 a:2:i:0;s: 1:"2";i:1;s:1:"7"; 因为 s:1 不在引号中。这也比在 PHP 中解析过滤器要快得多,并且比尝试根据接受的答案建议的位置挑选子字符串更真实。【参考方案10】:foreach( $result as $value )
$hour = unserialize( $value->meta_value );
if( $hour['date'] < $data['from'] )
$sum = $sum + $hour['hours'];
【讨论】:
虽然此代码 sn-p 可能是解决方案,但 including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。【参考方案11】:使用 php 序列化数据显然非常难看,但我有一个 MySQL 函数的线性组合,可以帮助解决这个问题:
select REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(searchColumn, 'fieldNameToExtract', -1), ';', 2), ':', -1), '"', '') AS extractedFieldName
from tableName as t
having extractedFieldName = 'expressionFilter';
希望这能有所帮助!
【讨论】:
这真的很有帮助,谢谢;我面临着将 Wordpress 网站迁移到 Craft 的问题,并且许多重要数据都嵌入到 wp_postmeta 表的序列化数组中。【参考方案12】:如果我在日志表中有 attribute_dump 字段,并且其中一行的值有
a:69:s:9:"status_id";s:1:"2";s:2:"id";s:5:"10215"
如果我想获取 status_id 等于 2 的所有行,那么查询将是
SELECT * FROM log WHERE attribute_dump REGEXP '.*"status_id";s:[0-9]+:"2".*'
【讨论】:
【参考方案13】:上面有一个很好的 REGEX 答案,但它假定 key
和 value
实现。如果您的序列化数组中只有 values
,这对我有用:
仅值
SELECT * FROM table WHERE your_field_here REGEXP '.*;s:[0-9]+:"your_value_here".*'
关键和价值
SELECT * FROM table WHERE your_field_here REGEXP '.*"array_key_here";s:[0-9]+:"your_value_here".*'
【讨论】:
【参考方案14】:为了方便使用:
column_field_name LIKE %VALUE_TO_BE_SEARCHED_FOR%
在 MySQL 查询中
【讨论】:
以上是关于序列化数组中的mysql选择查询的主要内容,如果未能解决你的问题,请参考以下文章
WP_查询 |数组序列化形式的 meta_query 参数元值
HiveQL:如何编写查询以根据嵌套的 JSON 数组值选择和过滤记录