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-ю нормальную форму, возможно, чистые реляционные базы данных не то, что вы хотите.