Si je ne me trompe pas, il y a un champ appelé deleted_at.
S’il est supprimé, il devrait y avoir un horodatage.
S’il n’est pas supprimé, il est nul.
Peut-être chercher l’entrée qui est <> Null et la supprimer.
Si je ne me trompe pas, il y a un champ appelé deleted_at.
S’il est supprimé, il devrait y avoir un horodatage.
S’il n’est pas supprimé, il est nul.
Peut-être chercher l’entrée qui est <> Null et la supprimer.
Merci Gav, mais ceux que je vise ne sont pas marqués comme supprimés. En fait, leur Sujet a été supprimé, puis destroy_all a été exécuté.
J’ai fait quelques essais et j’ai remarqué qu’une partie de votre requête ne renvoyait pas la réponse dont vous avez besoin.
SELECT topic_id from posts ne renvoie pas un entier, mais une chaîne de caractères.
Cela pourrait expliquer pourquoi votre base de données contient encore des messages orphelins.
Je pense que vous voyez l’Explorateur de données convertir automatiquement l’entier en URL, ce qu’il fait lorsque le libellé est topic_id.
Lorsque j’exécute cette requête dans l’Explorateur de données, tous les messages que j’essaie d’identifier sont capturés (bien plus de 5000) :
SELECT id, topic_id
FROM posts
WHERE topic_id not in (select id from topics)
ORDER by id
Je fais clairement quelque chose de mal avec ma syntaxe Rails car c’est ce que j’obtiens :
[1] pry(main)=> Target = Post.where('topic_id not in (select id from topics)')
=> []
Quelqu’un peut-il me dire ce que je fais de mal ?
Eh bien, grâce à @pfaffman, j’ai identifié les publications pertinentes en utilisant ceci :
Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
J’obtiens ce résultat :
[1] pry(main)> Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”)
=> [#<Post:0x000055df30d4ee90 id: 150>,
#<Post:0x000055df2e538ff0 id: 51097>,
#<Post:0x000055df2e50ba28 id: 83>,
#<Post:0x000055df2e4ee8b0 id: 40636>,
#<Post:0x000055df2e4a92d8 id: 62562>,
#<Post:0x000055df2e4b7978 id: 13522>,
etc.
Cependant, je n’arrive pas à comprendre comment appliquer destroy_all à cette sélection.
Ceci pourrait aider (pour mon propre bénéfice) :
Des suggestions ?
Je pense que cela fonctionnera
posts=
Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
posts.destroy_all
Ou vous pourriez ajouter .destroy_all à votre find_by_sql
J’ai essayé cela. Les données semblent être retournées sous forme de tableau avec un identifiant de publication et un ID (voir Delete deleted-posts permanently in bulk? - #45 by nathank).
Voici l’erreur que j’obtiens lorsque j’ajoute .destroy_all ou que j’utilise posts= que vous suggérez :
[2] pry(main)> posts.destroy_all
NoMethodError: undefined methoddestroy_all' for #<Array:0x000055fe7bc7fc98> from (pry):3:inpry’
Oh. Alors peut-être voir si
p=posts.first
Est un post_id. Si c’est le cas, alors vous pouvez
x=Post.find(p)
x.destroy
Ensuite, vous pourriez parcourir ceux-ci.
Je pense que vous devriez encapsuler votre requête dans quelque chose pour en faire un tableau de posts plutôt que des identifiants de posts.
Merci Jay. Voici ce que j’obtiens :
[3] pry(main)> p=
[3] pry(main)* posts.first
=> #<Post:0x0000563a24cab908 id: 150>
[4] pry(main)> x=Post.find(p)
ArgumentError: You are passing an instance of ActiveRecord::Base tofind. Please pass the id of the object by calling.id.
from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/finder_methods.rb:467:in `find_one’
Maintenant, parlez-moi de ces choses d’encapsulation. C’est tellement énervant, parce que je sais que la commande Rails de @Sam ici devrait fonctionner, mais je pense que Rails a changé depuis :
Qu’est-ce que cela a fait ? Je n’arrive pas à imaginer que Rails ait changé.
Cela vous donne-t-il les publications que vous souhaitez supprimer ?
J’ai lu quelque part en ligne en cherchant comment convertir un tableau Active Record en une relation Active Record que les choses avaient changé entre Rails 3.x et Rails 4 et que la syntaxe devait être différente, mais cela m’a dépassé.
Cela semblait en récupérer quelques-unes lorsque je l’ai exécuté pour la première fois, et je les ai dûment détruites. Mais il n’y en avait pas beaucoup. Maintenant, il n’en récupère aucune, alors que le SQL en récupère des milliers lorsqu’il est exécuté dans Data Explorer.
Ce sont des publications orphelines, où le sujet a été détruit.
Pouvez-vous utiliser each{} pour parcourir les membres de ce tableau, en appelant destroy sur chaque publication individuellement ?
Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”).each { |p| p.destroy }
Eh bien, j’ai essayé. D’abord, il n’a pas aimé le SQL enveloppé de cette façon ni la syntaxe :
Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”).each { |p| p.destroy_all }
SyntaxError : inattenduin', attendu(’
…rom posts where topic_id not in (select id from topics)”)…
… ^~
SyntaxError : variable locale ou méthode inattendue, attendu fin d’entrée
…t in (select id from topics)”).each { |p| p.destroy_all }
Alors j’ai essayé une autre astuce en le divisant :
posts=Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
posts.each do |p|
p.destroy
end
Cela a semblé fonctionner correctement, mais l’ajout de p.destroy provoque ceci :
ActiveModel::MissingAttributeError : attribut manquant : user_id
from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activemodel-6.1.4.1/lib/active_model/attribute.rb:222:in `value’
J’ai essayé plusieurs façons de l’intégrer, mais j’ai abandonné. @sam, pouvez-vous aider ?
J’ai même essayé de convertir le SQL en AR via scuttle.io comme ceci :
Post.select([:id, :topic_id]).where(Topic.select(:id))
Hélas, j’obtiens cette erreur :
ArgumentError : type d’argument non pris en charge : #Topic::ActiveRecord_Relation:0x000055c67a7131d0 (Topic::ActiveRecord_Relation)
Quand j’ai copié-collé cette partie d’un message précédent, il semble que les guillemets aient été convertis en guillemets typographiques quelque part, je suppose que c’est la vraie erreur. Désolé pour ça.
find_by_sql est décrit comme retournant un objet avec les valeurs spécifiées dans la requête SQL, ce qui signifie probablement que vous récupérez un objet Post qui n’a que la propriété id définie, user_id et tout le reste est manquant.
... find_by_sql("select * ... s’en chargera. Il y a probablement un sous-ensemble de valeurs que vous pourriez sélectionner pour obtenir une destruction, plutôt que de tout sélectionner, mais je ne sais pas quel serait ce sous-ensemble.
Donc, le tout : (sans guillemets typographiques cette fois…)
Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }
Excellent, je suis ravi d’apprendre que cela a fonctionné pour vous. Donc, pour combiner les réponses, on peut utiliser ce qui suit dans la console Rails pour détruire tous les sujets qui ont été supprimés il y a plus de 90 jours, en répétant autant de fois que nécessaire s’il y a plus de 1000 sujets :
Topic.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all
Une fois cela terminé, on peut utiliser ce qui suit pour détruire tous les messages orphelins de sujets détruits :
Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }
Il convient de noter que les commandes ci-dessus ne détruiront pas les messages supprimés, seulement les sujets supprimés et leurs messages orphelins. Pour détruire également les messages supprimés de plus de 90 jours, utilisez ce qui suit, en répétant à nouveau si nécessaire :
Post.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all
P.S. Par curiosité, avez-vous essayé destroy_all sans limit(1000) et rencontré des problèmes, ou ne l’avez-vous pas essayé sans la limite ?
J’ai essayé sans la limite et quelque chose est devenu un peu fou - mais j’ai oublié les détails, désolé.
Pouvons-nous marquer votre publication comme solution ou utiliser celle de l’OP ?

Pas de soucis. Si vous n’aviez pas essayé, j’aurais ajouté une note indiquant que la limite pourrait ne pas être nécessaire, mais puisque vous avez eu des problèmes, je peux simplement la laisser telle quelle.