web-dev-qa-db-fra.com

Recherche de données sérialisées, à l'aide d'un enregistrement actif

J'essaie de faire une simple requête sur une colonne sérialisée, comment faites-vous cela?

serialize :mycode, Array


1.9.3p125 :026 > MyModel.find(104).mycode
  MyModel Load (0.6ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`id` = 104 LIMIT 1
 => [43565, 43402] 
1.9.3p125 :027 > MyModel.find_all_by_mycode("[43402]")
  MyModel Load (0.7ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` = '[43402]'
 => [] 
1.9.3p125 :028 > MyModel.find_all_by_mycode(43402)
  MyModel Load (1.2ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` = 43402
 => [] 
1.9.3p125 :029 > MyModel.find_all_by_mycode([43565, 43402])
  MyModel Load (1.1ms)  SELECT `mymodels`.* FROM `mymodels` WHERE `mymodels`.`mycode` IN (43565, 43402)
 => [] 
46
JZ.

Fondamentalement, vous ne pouvez pas. L'inconvénient de #serialize est que vous contournez les abstractions natives de votre base de données. Vous êtes à peu près limité au chargement et à la sauvegarde des données.

Cela dit, un très bon moyen de ralentir votre application vers une analyse pourrait être:

MyModel.all.select { |m| m.mycode.include? 43402 }

Morale de l'histoire: n'utilisez pas #serialize pour les données sur lesquelles vous devez interroger.

37
noodl

C'est juste une astuce pour ne pas ralentir votre application. Vous devez utiliser .to_yaml.

résultat exact:

MyModel.where("mycode = ?", [43565, 43402].to_yaml)
#=> [#<MyModel id:...]

Testé uniquement pour MySQL.

71
rks

Le tableau sérialisé est stocké dans la base de données de manière particulière, par exemple:

[1, 2, 3, 4]
in
1\n 2\n 3\n etc

d'où la requête serait

MyModel.where("mycode like ?", "% 2\n%")

mettre de l'espace entre % et 2.

31
jbmyid

La réponse de Noodl est juste, mais pas entièrement correcte.

Cela dépend vraiment de l'adaptateur de base de données/ORM que vous utilisez: par exemple, PostgreSQL peut maintenant stocker et rechercher des hachages/json - consultez hstore. Je me souviens avoir lu que l'adaptateur ActiveRecord pour PostgreSQl le gère désormais correctement. Et si vous utilisez mongoid ou quelque chose comme ça - alors vous utilisez des données non structurées (c'est-à-dire json) partout au niveau de la base de données.

Cependant, si vous utilisez une base de données qui ne peut pas vraiment gérer les hachages - comme la combinaison MySQL/ActiveRecord - alors la seule raison pour laquelle vous utiliseriez un champ sérialisé est pour certaines données que vous pouvez créer/écrire dans un processus d'arrière-plan et afficher/sortir à la demande - les deux seules utilisations que j'ai trouvées dans mon expérience sont certains rapports (comme un champ de statistiques sur un modèle de produit - où j'ai besoin de stocker des moyennes et des médianes pour un produit), et des options utilisateur (comme leur couleur de modèle préférée -Je vraiment pas besoin d'interroger à ce sujet) - cependant les informations des utilisateurs - comme leur abonnement à une liste de diffusion - doivent être consultables pour les envois d'e-mails.

PostgreSQL hstore Exemple ActiveRecord:

MyModel.where("mycode @> 'KEY=>\"#{VALUE}\"'")

UPDATE Depuis 2017, MariaDB et MySQL prennent en charge les types de champs JSON.

17
konung

Bonnes nouvelles! Si vous utilisez PostgreSQL avec hstore (ce qui est super facile avec Rails 4), vous pouvez maintenant rechercher totalement les données sérialisées. This est un guide pratique, et - ici est la documentation syntaxique de PG.

Dans mon cas, j'ai un dictionnaire stocké sous forme de hachage dans une colonne hstore appelée amenities. Je souhaite rechercher quelques équipements interrogés d'une valeur de 1 dans le hachage, je fais juste

House.where("amenities @> 'wifi => 1' AND amenities @> 'pool => 1'")

Hourra pour les améliorations!

8
NealJMD

Vous pouvez interroger la colonne sérialisée avec une instruction sql LIKE.

 MyModel.where("mycode LIKE '%?%'", 43402)

C'est plus rapide que d'utiliser include?, Cependant, vous ne pouvez pas utiliser un tableau comme paramètre.

7
Kyle C

Il y a un article de blog de FriendFeed de 2009 qui décrit comment utiliser les données sérialisées dans MySQL.

Ce que vous pouvez faire, c'est créer des tables qui fonctionnent comme des index pour toutes les données que vous souhaitez rechercher.

Créer un modèle qui contient les valeurs/champs consultables

Dans votre exemple, les modèles ressembleraient à ceci:

class MyModel < ApplicationRecord
  # id, name, other fields...
  serialize :mycode, Array
end

class Item < ApplicationRecord
  # id, value...
  belongs_to :my_model
end

Création d'une table "index" pour les champs consultables

Lorsque vous enregistrez MyModel, vous pouvez faire quelque chose comme ceci pour créer l'index:

Item.where(my_model: self).destroy
self.mycode.each do |mycode_item|
  Item.create(my_model: self, value: mycode_item)
end

Interrogation et recherche

Ensuite, lorsque vous souhaitez interroger et rechercher, faites simplement:

Item.where(value: [43565, 43402]).all.map(&:my_model)
Item.where(value: 43402).all.map(&:my_model)

Vous pouvez ajouter une méthode à MyModel pour simplifier:

def find_by_mycode(value_or_values)
  Item.where(value: value_or_values).all.map(&my_model)
end

MyModel.find_by_mycode([43565, 43402])
MyModel.find_by_mycode(43402)

Pour accélérer les choses, vous souhaiterez créer un index SQL pour cette table.

3
Rudolf Olah