web-dev-qa-db-ja.com

シリアル化された配列内のmysql selectクエリ

データベースのフィールド内のシリアル化された配列にアイテムのリストを格納しています(PHP/MySQLを使用しています)。

配列内にあるこれらのアイテムの特定の1つを含むすべてのレコードを選択するクエリが必要です。

このようなもの:

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

うまくいけば、それは理にかなっています。

任意のアイデアをいただければ幸いです。

ありがとう

17
Daelan

MySQLを使用してPHP serialize コマンドでシリアル化され、データベースフィールドに格納されている配列)を検索するつもりですか?最初の反応は次のとおりです:OMG 。2番目の反応は次のとおりです:なぜですか賢明すべきことは次のいずれかです。

  1. 配列をPHPに取得し、シリアル化を解除して検索します
  2. MySQLにデータをシリアル化して保存することを忘れ、通常のテーブルとして保存し、高速検索のためにインデックスを作成する

2番目のオプションを選択しますが、あなたのコンテキストがわかりません。

もちろん、本当にやりたいのであれば、SUBSTRINGまたは別のMySQL関数で何かを試し、フィールドを操作することもできますが、なぜそうしたいのかわかりません。それは面倒であり、不必要な醜いハックになるでしょう。一方、それはパズルであり、ここの人々はパズルを好む傾向があるので、-本当にしたい場合次に、フィールドのコンテンツを投稿してください。

15
thomaspaulb

GWWがコメントで述べているように、この方法でクエリを実行する必要がある場合は、このデータをビッグOLE文字列(シリアル化された配列)以外のものとして格納することを検討する必要があります。

それが不可能な場合(または怠惰な場合)、シリアル化された配列が単なるビッグOLE文字列であるという事実を利用し、LIKE句を見つけて一致するレコードを見つけることができます。 PHPデータをシリアライズする方法は簡単に理解できます(ヒント:これらの数値は物の長さを示します)。

シリアル化された配列がかなり複雑な場合、これはすぐに壊れます。しかし、それがフラットアレイであれば、それを実行できるはずです。

もちろん、LIKE '%...%'を使用するので、どのインデックスからも助けが得られず、パフォーマンスが非常に低下します。

これが、「内部」でクエリを実行する必要がある場合に、データを正規化された方法で保存することを勧めている理由です。

24
timdev

データモデルを制御している場合、データベースにシリアル化されたデータを詰め込むと、ほぼいつでも、長期的にはあなたを悩ませるでしょう。ただし、多くの場合、1つのしないは、たとえば特定のオープンソースのコンテンツ管理システムを使用している場合など、データモデルを制御します。 Drupalは、適切なモデルの代わりに、ごみ箱の列にシリアル化されたデータのlotを貼り付けます。たとえば、ubercartには、すべての注文の 'data'列があります。モジュールはメイン注文エンティティにデータを添付する必要があるため、便宜上、シリアル化された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を過ぎたものをすべて提供し、外側の2つは残りをクリーンアップします。

この複雑なハッカーは、複数回実行するコードでは望んでいるものではなく、phpスクリプトを作成することなく、テーブルからデータを取得するためのツールです。

このcouldは、

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の最後にあります)

14
Mixologic

あなたはこのようにそれを行うことができます:

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

ただし、とにかく、そのデータを別のテーブルに格納することを検討する必要があります。

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), '%');
4
ryanlahue

まあ、私は同じ問題がありました、そしてそれは明らかに簡単なことですが、おそらくもっとテストが必要です。

INステートメントを使用するだけで、フィールド自体を配列として配置します。例:

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

〜ここで、「2」は、シリアル化された配列である「child_of」フィールド内で探している値です。

このシリアル化された配列が必要だったのは、子であるIDを格納するためだけにレコードを複製することはできないためです。

乾杯

3
Alan M. Maziero

ログテーブルにattribute_dumpフィールドがあり、その行の1つの値に

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".*'
1
Jeyakumar T
Select * from table where table_field like '%"enter_your_value"%'
0
shafi

PHPのシリアル化されたデータの操作は明らかに醜いですが、これを整理するのに役立つMySQL関数の1つのライナーミックスを用意しています。

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

これが役立つことを願っています!

0
Jeremy Jumeau