기억이 맞다면 deleted_at이라는 필드가 있습니다.
삭제된 경우라면 날짜와 시간 스탬프가 있어야 합니다.
삭제되지 않은 경우라면 null입니다.
null이 아닌 항목을 찾아 삭제하는 방법을 살펴보시는 것이 좋습니다.
기억이 맞다면 deleted_at이라는 필드가 있습니다.
삭제된 경우라면 날짜와 시간 스탬프가 있어야 합니다.
삭제되지 않은 경우라면 null입니다.
null이 아닌 항목을 찾아 삭제하는 방법을 살펴보시는 것이 좋습니다.
Gav, 감사합니다. 하지만 제가 대상으로 삼고 있는 항목들은 삭제 처리가 되어 있지 않습니다. 오히려 해당 항목들의 Topic이 삭제된 후 destroy_all이 호출된 상태입니다.
Data Explorer가 label이 topic_id인 경우 정수를 자동으로 URL로 변환하고 있기 때문에, 그런 현상을 보고 계신 것 같습니다.
Data Explorer에서 이 쿼리를 실행하면, 식별하려는 모든 게시글이 모두 조회됩니다(5,000개 이상):
SELECT id, topic_id
FROM posts
WHERE topic_id not in (select id from topics)
ORDER by id
Rails 구문을 잘못 작성하고 있는 것 같아, 다음과 같은 결과가 나옵니다:
[1] pry(main)> Target = Post.where('topic_id not in (select id from topics)')
=> []
누구든 제가 무엇을 잘못하고 있는지 알려주실 수 있을까요?
음, @pfaffman 덕분에 다음 코드를 사용하여 관련 게시물을 식별했습니다:
Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
다음과 같은 출력을 얻습니다:
[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
그러나 이 선택된 항목에 destroy_all을 적용하는 방법을 알 수 없습니다.
(제 이해를 돕기 위해) 다음 링크가 도움이 될 수 있습니다:
어떤 제안이 있을까요?
이렇게 하면 될 것 같습니다
posts=
Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
posts.destroy_all
아니면 find_by_sql에 .destroy_all을 추가할 수도 있습니다
그 방법을 시도해 봤습니다. 데이터는 어떤 게시식 식별자와 ID를 가진 배열로 반환되는 것 같습니다 (Delete deleted-posts permanently in bulk? - #45 by nathank 참고).
.destroy_all을 추가하거나 제안해 주신 posts=를 사용할 때 발생하는 오류입니다:
[2] pry(main)> posts.destroy_all
NoMethodError: undefined methoddestroy_all' for #<Array:0x000055fe7bc7fc98> from (pry):3:inpry’
아. 그렇다면
p=posts.first
이 post_id인지 확인해 보세요. 맞다면 다음과 같이 하면 됩니다:
x=Post.find(p)
x.destroy
그 후 이 항목들을 반복문으로 처리할 수 있습니다.
쿼리를 무엇으로 감싸야 post id 배열이 아닌 post 배열을 얻을 수 있는지 확인해야 할 것 같습니다.
Jay, 감사합니다. 이렇게 나옵니다:
[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’
자, 이 'wrap’이라는 것에 대해 알려주세요. @Sam의 이 rails 명령어는 작동해야 한다고 생각하는데, 그때 이후로 Rails가 변경된 것 같아 정말 짜증납니다:
그게 뭘 한 거예요? 레일스가 바뀌었을 리가 없다고 생각해요.
이 쿼리로 삭제하려는 게시글들을 가져올 수 있나요?
Active Record 배열을 Active Record Relation으로 변환하는 방법에 대해 온라인에서 검색하던 중, Rails 3.x와 Rails 4 사이에서 변경 사항이 있었고 문법도 달라져야 한다는 내용을 어딘가에서 읽었습니다. 하지만 제게는 너무 어려워서 이해가 되지 않았습니다.
처음 실행했을 때 몇 개를 가져오긴 해서, 당연히 destroy_all을 실행했습니다. 하지만 그 수가 많지 않았습니다. 지금은 전혀 가져오지 못하는 반면, Data Explorer에서 SQL을 실행하면 수천 개의 레코드가 조회됩니다.
이것들은 Topic이 Destroy_all로 삭제된 후 남게 된 고아(Orphaned) Posts입니다.
each{}를 사용하여 해당 배열의 멤버를 반복하면서 각 게시글에 대해 개별적으로 destroy를 호출할 수 있나요?
Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”).each { |p| p.destroy }
음, 시도해 봤습니다. 처음에는 그렇게 감싸놓은 SQL도, 문법도 인정하지 않았습니다:
Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”).each { |p| p.destroy_all }
SyntaxError: unexpected `in’, expecting ‘(’
…rom posts where topic_id not in (select id from topics)”)…
… ^~
SyntaxError: unexpected local variable or method, expecting end-of-input
…t in (select id from topics)”).each { |p| p.destroy_all }
그래서 나누어 처리하는 또 다른 어설픈 방법을 시도해 봤습니다:
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
이것은 잘 실행되는 것 같았지만, p.destroy를 추가하면 다음과 같은 오류가 발생합니다:
ActiveModel::MissingAttributeError: missing attribute: 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’
여러 방법으로 그 값을 넣으려 해 보았지만 결국 포기했습니다. @sam, 도와주실 수 있나요?
다음과 같은 내용으로 SQL을 AR로 변환해 보려고 scuttle.io를 시도해 보았습니다:
Post.select([:id, :topic_id]).where(Topic.select(:id))
불행히도 다음과 같은 오류가 발생합니다:
ArgumentError: Unsupported argument type: #Topic::ActiveRecord_Relation:0x000055c67a7131d0 (Topic::ActiveRecord_Relation)
이전 메시지 일부에서 그 부분을 복사해서 붙여넣었는데, 어딘가에서 따옴표가 곡선 따옴표(curly quotes)로 변환된 것 같습니다. 아마 그게 실제 오류일 것 같습니다. 죄송합니다.
find_by_sql은 SQL 쿼리에 지정된 값을 가진 객체를 반환한다고 설명되어 있는데, 이는 id 속성만 설정된 Post 객체를 반환받고, user_id와 그 외 모든 값이 누락되어 있다는 것을 의미합니다.
... find_by_sql("select * ...는 이 문제를 해결해 줄 것입니다. 모든 것을 선택하는 대신 destroy를 수행할 수 있는 값의 부분 집합을 선택할 수도 있을 것 같지만, 그 부분 집합이 무엇인지는 제가 알지 못합니다.
그래서 전체 코드는 다음과 같습니다: (이번에는 곡선 따옴표 없이…)
Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }
좋습니다, 도움이 되었다니 기쁩니다. 답변을 통합하기 위해, 90일 이상 삭제된 모든 주제를 삭제하려면 다음 명령을 Rails 콘솔에서 사용할 수 있습니다. 주제가 1000개 이상이라면 필요한 만큼 반복하세요:
Topic.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all
이 작업이 완료된 후, 삭제된 주제에서 고아가 된 모든 게시물을 삭제하려면 다음을 사용할 수 있습니다:
Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }
위 명령들은 삭제된 게시물은 삭제하지 않으며, 삭제된 주제와 그 고아 게시물만 삭제한다는 점에 유의해야 합니다. 90일 이상 삭제된 게시물도 함께 삭제하려면 다음을 사용하세요. 마찬가지로 필요한 만큼 반복하세요:
Post.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all
참고. 참고로, limit(1000) 없이 destroy_all을 시도해 보셨나요? 문제가 있었나요, 아니면 제한 없이 시도해 보지 않으셨나요?
limit 없이 시도해 보았는데 뭔가 좀 이상하게 동작하더라고요 - 하지만 자세한 내용은 기억이 나지 않네요. 죄송합니다.
질문자님의 게시물을 해결책으로 표시하거나 OP의 내용을 사용해도 될까요?

걱정하지 마세요. 만약 시도해 보지 않으셨다면, 한도 설정이 필요하지 않을 수도 있다는 주석을 달았을 텐데, 문제가 있었으니 그대로 두겠습니다.