Mysql select query в сериализованном массиве

Я сохраняю список элементов в сериализованном массиве в поле в моей базе данных (я использую PHP/MySQL).

Я хочу получить запрос, который выберет все записи, содержащие определенный из этих элементов, который находится в массиве.

Что-то вроде этого:

select * from table WHERE (an item in my array) = '$n'

Надеюсь, это имеет смысл.

Будем очень благодарны за любые идеи.

Спасибо

Ответы

Ответ 1

Итак, вы хотите использовать MySQL для поиска в массиве PHP, который был сериализован с помощью команды serialize и сохранен в поле базы данных? Моя первая реакция была бы: OMG. Моя вторая реакция была бы: почему? Разумное дело - либо:

  • Извлеките массив в PHP, несериализируйте его и выполните поиск в нем
  • Забудьте о хранении данных в MySQL как сериализованных и сохраните его как обычную таблицу и проиндексируйте ее для быстрого поиска

Я бы выбрал второй вариант, но я не знаю вашего контекста.

Конечно, если вы действительно этого захотите, вы можете попробовать что-то с помощью SUBSTRING или другой функции MySQL и попытаться манипулировать этим полем, но я не понимаю, зачем вам это нужно. Это громоздко, и это было бы ненужным уродливым взломом. С другой стороны, это головоломка, и люди здесь любят головоломки, поэтому, если вы действительно захотите опубликовать содержимое своего поля, и мы можем дать ему шанс.

Ответ 2

Как говорит GWW в комментариях, если вам нужно запросить такие вещи таким образом, вам действительно стоит подумать о том, чтобы хранить эти данные как нечто отличное от большой ole-строки (что и является вашим сериализованным массивом).

Если это невозможно (или вы просто ленивы), вы можете использовать тот факт, что сериализованный массив является просто большой строкой и вычисляет предложение LIKE для поиска совпадающих записей. То, как PHP сериализует данные, довольно легко понять (подсказка: эти цифры указывают длину вещей).

Теперь, если ваш сериализованный массив довольно сложный, это быстро сломается. Но если это плоский массив, вы должны это сделать.

Конечно, вы будете использовать LIKE '%...%', поэтому вы не получите никакой помощи от каких-либо указаний, и производительность будет очень плохой.

Вот почему люди предлагают вам хранить эти данные в некотором нормализованном порядке, если вам нужно запросить "внутри".

Ответ 3

Если у вас есть контроль над моделью данных, набивка сериализованных данных в базе данных будет укусить вас в конечном счете почти всегда. Однако, как правило, у одного нет контроля над моделью данных, например, при работе с определенными системами управления контентом с открытым исходным кодом. Drupal хранит много сериализованных данных в столбцах корзины вместо правильной модели. Например, ubercart имеет столбец "данные" для всех своих заказов. Добавленные модули должны прикреплять данные к основному объекту заказа, поэтому из удобства они привязывают его к сериализованному блобу. Как третий участник этого, мне все еще нужен способ получить некоторые данные, заполненные там, чтобы ответить на некоторые вопросы.

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*[email protected]";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, а внешние два очищают остаток.

Этот сложный хакер - это не то, что вы хотите в коде, который работает более одного раза, а больше инструмент для вывода данных из таблицы без необходимости прибегать к написанию php script.

Обратите внимание, что это можно упростить, просто

SELECT uo.*,
SUBSTRING_INDEX(
  SUBSTRING_INDEX( uo.data, '";}' , 1 ),
'"',-1) 
AS `old order id`
FROM `uc_orders` AS `uo`
HAVING `old order id` = 25

но это будет работать только в этом конкретном случае (значение, которое я хочу, находится в конце блока данных)

Ответ 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), '%');

Ответ 5

Вы можете сделать это следующим образом:

SELECT * FROM table_name WHERE some_field REGEXP '.*"item_key";s:[0-9]+:"item_value".*'

Но в любом случае вам следует рассмотреть возможность хранения этих данных в отдельной таблице.

Ответ 6

Хорошо, у меня была такая же проблема, и, по-видимому, это кусок пирога, но, возможно, ему нужно больше тестов.

Просто используйте инструкцию IN, но поместите поле как массив! Пример:

SELECT id, title, page FROM pages WHERE 2 IN (child_of)

~ где '2' - это значение, которое я ищу внутри поля 'child_of', который является сериализованным массивом.

Этот сериализованный массив был необходим, потому что я не могу дублировать записи только для того, чтобы хранить то, что они были дочерними.

Приветствия

Ответ 7

Select * from table where table_field like '%"enter_your_value"%'

Ответ 8

Работа с сериализованными данными php, очевидно, довольно уродлива, но у меня есть одно линейное сочетание функций MySQL, которые помогают разобраться в этом:

select REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(searchColumn, 'fieldNameToExtract', -1), ';', 2), ':', -1), '"', '') AS extractedFieldName
from tableName as t 
having extractedFieldName = 'expressionFilter';

Надеюсь, что это может помочь!

Ответ 9

Возможно, вы ищете инструкцию SQL IN.

http://www.w3schools.com/sql/sql_in.asp

Однако вам придется сначала разбить свой массив. Вы не можете просто передать массив в MySQL и ожидать, что он будет знать, что с ним делать. Для этого вы можете попытаться выполнить сериализацию с PHP взорваться.

http://php.net/manual/en/function.explode.php

Ответ 10

select * from postmeta where meta_key = 'your_key'  and meta_value REGEXP  ('6')

Ответ 11

foreach( $result as $value ) {
   $hour = unserialize( $value->meta_value );
   if( $hour['date'] < $data['from'] ) {
     $sum = $sum + $hour['hours'];
   }
 }

Ответ 12

Попробуйте переключиться на postgresql или mongodb. Ваше выражение повреждает 1-ю нормальную форму, возможно, чистые реляционные базы данных не то, что вы хотите.