Rails Datenbank Anfragen performant gestalten

Hallo zusammen,

als erstes möchte ich allen danken, die sich hier ein wenig Zeit nehmen,
um mir zu helfen.

Ich habe folgendes Schema :

class Term < ActiveRecord::Base
has_many :sims
end

class Sim < ActiveRecord::Base
belongs_to :term1, :class_name => “Term”
belongs_to :term2, :class_name => “Term”
end

Ich denke die Migrations sind hierbei eher unwichtig. Jedenfalls möchte
ich folgende Abfragen durchführen :

t = [“NewYork”,“Boston”,“US”]
@term = Array.new
@sub_terms = Array.new

t.each do |term|
  @termi = Term.find_by_bezeichnung(term)
  @term << @termi.id
end

puts "############################################"

@term.each do |termid|
  li = Sim.find(:all, :conditions => {:term1_id => termid}).each do

|li|
@sub_terms << li.term2.name
end
end
puts @sub_terms.uniq

Wie ihr seht speichere ich meine Ergebnisse wieder als Array ab, ka ob
das so sinnvoll ist oder nicht. Frage ist nun kann ich das irgendwie
performanter gestalten? Bis jetzt ist das ja zweimal find und unzählige
each do Blöcke.

Dann würde ich noch gerne wissen ob man in der Anfrage Beispielsweise
wenn ich in der Tabelle Sims eine Spalte value (int Wert) habe, nach
diesem Wert die Erbebnisse sortieren kann und dann nur die ersten 10
Ergebnisse ausgebe. In SQL gibt es ja sowas wie ORDER_BY value DESC
LIMIT 10 oder so ähnlich.

On Monday 24 November 2008, Ma Sch wrote:

Wie ihr seht speichere ich meine Ergebnisse wieder als Array ab, ka
ob das so sinnvoll ist oder nicht. Frage ist nun kann ich das
irgendwie performanter gestalten? Bis jetzt ist das ja zweimal find
und unzählige each do Blöcke.

Versuch’s mal mit

Sim.find(:all, :conditions => [“term1_id IN ?”, @term])

Michael

Michael S. wrote:

Sim.find(:all, :conditions => [“term1_id IN ?”, @term])

Ok danke für deine Antwort,

an welcher Stelle soll das denn hin oder was soll damit ersetzt werden?

Lösch ich den ganzen Block mit li = Sim.find und ersetze das mit deinem
Vorschlag bekomme ich beispielsweise einen Syntax Fehler :

“Mysql::Error: #42000You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near ‘1,2)’ at line 1: SELECT * FROM sims WHERE
(term1_id IN 1,2)”


rubyonrails-ug mailing list
[email protected]
http://mailman.headflash.com/mailman/listinfo/rubyonrails-ug


rubyonrails-ug mailing list
[email protected]
http://mailman.headflash.com/mailman/listinfo/rubyonrails-ug

Manuel W. wrote:


rubyonrails-ug mailing list
[email protected]
http://mailman.headflash.com/mailman/listinfo/rubyonrails-ug

Hmm was damit wohl gemeint war oO? Ich nehme mal an hier weiß niemand
mehr wie man die oberen Anfragen verbessern könnte. Ich habe jedenfalls
bis auf Performance alles hinbekommen, sprich Sortierung und co…

Matthias Sch. wrote:

t.each do |term|
  @termi = Term.find_by_bezeichnung(term)
  @term << @termi.id
end

t.each do |term|
@termi = Term.find_by_bezeichnung(term)
@term << @termi.id
end

kannst du ersetzen durch:

@term = Term.find_by_bezeichnung(term).collect(&:id)

in @term hast du dann eine Array mit den Term.ids.

Zum zweiten Teil:

Sim.find(:all, :order => ‘value’, :limit => 10)

ortwin

Hallo Holger,

danke für deine Antwort, den zweiten Teil habe ich hinbekommen. Leider
funktioniert das noch nicht so ganz mit

@term = Term.find_by_bezeichnung(term).collect(&:id)

Ich nehme mal an in term soll das array von oben namens t kommen, wo
auch die ganzen Terme drinstecken. Ansonsten kennt er term nicht, wenn
ich das umbenenne bekomme ich folgenden Fehler bei t = [“apple”] :

undefined method `collect’ for #<Term id: 2025, bezeichnung: “apple”>

Die ID stimmt jedenfalls mit der ID aus der Datenbanktabelle überein.

Alex wrote:

Hi, probier mal:

@term = Term.find_all_by_bezeichnung(term).collect(&:id)

-alex

Das funktioniert einwandfrei, ich danke dir vielmals alex :slight_smile:

On Saturday 29 November 2008, Matthias Sch. wrote:

@term = Term.find_[all_]by_bezeichnung(term).collect(&:id)

Ich sag’s mal ganz platt: Du bist auf dem Holzweg und solltest, bevor du
irgendetwas anderes tust, lernen, was eine Relationale Datenbank ist
und wie man sie sinnvoll verwendet.

Ich habe gerade nochmal auf deine ursprüngliche Frage in diesem Thread
geschaut und daraus wird klar, dass du versuchst, etwas in Ruby zu
machen, was Sache der DB ist und problemlos in einer einzelnen Query,
bzw. einem #find mit entsprechenden Parametern, erreicht werden kann.
Nämlich – ungetestet – so:

Sim.find(:all,
:joins => :term1,
:conditions => [“terms.bezeichnung IN (?)”,
[“NewYork”,“Boston”,“US”]])

Seit Rails 2.2 geht vermutlich auch

Sim.find(:all,
:joins => :term1,
:conditions => {
:terms => { :bezeichnung => [“NewYork”,“Boston”,“US”] }})

Michael

Hallo Michael,

danke für deine Antwort, leider funktioniert das :

Sim.find(:all,
:joins => :term1,
:conditions => [“terms.bezeichnung IN (?)”,
[“apple”]])

nicht. Falls dich der Fehler interessieren sollte :

Mysql::Error: #42S22Unknown column ‘sims.term_id’ in ‘on clause’: SELECT
sims.* FROM sims INNER JOIN terms ON terms.id = sims.term_id
WHERE (sims.id = ‘— :term1\n’ AND (terms.bezeichnung IN
(‘apple’)))

Hi, probier mal:

@term = Term.find_all_by_bezeichnung(term).collect(&:id)

-alex

On Saturday 29 November 2008, Matthias Sch. wrote:

nicht. Falls dich der Fehler interessieren sollte :

Mysql::Error: #42S22Unknown column ‘sims.term_id’ in ‘on clause’:
SELECT sims.* FROM sims INNER JOIN terms ON terms.id =
sims.term_id WHERE (sims.id = ‘— :term1\n’ AND
(terms.bezeichnung IN (‘apple’)))

Versuch’s statt dessen mal mit :include statt :joins.

Da Sim zwei Assoziationen (term1 und term2) auf die Term hat, macht
ActiveRecord hier offenbar etwas, was ich auch auf den zweiten Blick
noch verwunderlich finde. Ich kann im Augenblick nicht beurteilen, ob
ich es nur nicht verstehe, oder ob es ein Fehler ist. Vielleicht kann
das jemand anders aufklären.

Wenn das alles nicht hilft, nimm die SQL-Keule, obwohl die nicht
erforderlich sein sollte

:joins => ‘INNER JOIN terms ON sims.term1_id = terms.id’

Wozu der ganze Aufwand? Nun, weil es Sache der DB ist, die Daten zu
speichern und so zurückzugeben, wie die Anwendung sie benötigt. Die DB
(sogar MySQL) ist gut darin, Daten zu filtern und zu verknüpfen und tut
das erheblich schneller als es in Ruby möglich wäre.

Michael

Michael S. wrote:

:joins => ‘INNER JOIN terms ON sims.term1_id = terms.id’

Die SQL-Keule liefert erstmal keinen Fehler, ich würde noch gerne
wissen, wie ich die Ergebnisse ausgeben kann. Obiges Schema funktioniert
leider nicht mehr und ich weiß auch nicht wie ich es sonst machen
könnte. Gibt es da was besseres?

:include hat leider auch nicht funktioniert.

Grüße Matthias

Matthias Sch. wrote:

Michael S. wrote:

:joins => ‘INNER JOIN terms ON sims.term1_id = terms.id’

Die SQL-Keule liefert erstmal keinen Fehler, ich würde noch gerne
wissen, wie ich die Ergebnisse ausgeben kann. Obiges Schema funktioniert
leider nicht mehr und ich weiß auch nicht wie ich es sonst machen
könnte. Gibt es da was besseres?

:include hat leider auch nicht funktioniert.

Grüße Matthias

Muß wohl irgendwo nen Fehler gehabt haben, jedenfalls funktioniert das
nun folgendermaßen :

li = Sim.find(:all,
:joins => ‘INNER JOIN terms ON sims.term1_id =
terms.id’,
:conditions => [“terms.bezeichnung IN (?)”,[“apple”]],
:order => ‘similarity DESC’,
:limit => 10).each do |element|
puts element.term2.bezeichnung
end

Ist die Schleife noch in Orndnung am Ende, um die Bezeichnungen von
term2 ausgeben zu lassen oder gibt es was besseres?

Hallo zusammen,

ich habe die Anfragen etwas umgestaltet in :

li = Term.find(:all, #Oberbegriffe
:joins => ‘INNER JOIN sims ON sims.term1_id =
terms.id INNER JOIN terms terms2 ON sims.term2_id = terms2.id’,
:conditions => [“terms2.bezeichnung IN (?)”,tag],
:order => ‘sims.similarity DESC’,
:limit => 5).each do |element|
@super_terms << element.bezeichnung
end
return @super_terms

und in die andere Richtung :

la = Term.find(:all, #Unterbegriffe
:joins => ‘INNER JOIN sims ON sims.term2_id =
terms.id INNER JOIN terms terms2 ON sims.term1_id = terms2.id’,
:conditions => [“terms2.bezeichnung IN (?)”,tag],
:order => ‘similarity DESC’,
:limit => 15).each do |element|
@sub_terms << element.bezeichnung
end
return @sub_term

Das teste ich lokal im Moment. Der LapTop ist ziemlich alt, hat wenig
Arbeitsspeicher usw… deswegen kam mir das sehr langsam vor (auf meinem
DesktopRechner geht das in 1,4 Sekunden, auf dem laptop breche ich
vorher ab). Daher habe ich in der Tabelle sims über php myadmin eine
Indexierung drüberlaufen lassen auf term1_id und term2_id. Die obere
Anfrage hat sich verbessert auf 0.03 sec von ca. 2.5, die zweite Anfrage
jedoch wurde noch langsamer, von 7 sec auf 13 und ähnlicheres. Woran
kann das liegen und wie bekomm ich das performanter hin?

Danke für die Antworten

Grüße M.Sch

On Monday 08 December 2008, Matthias Sch. wrote:

:conditions => ["terms2.bezeichnung IN (?)",tag],

return @sub_term

Ersetze das #each durch ein #map, das ist in Ruby viel idiomatischer und
erspart auch das ohnehin überflüssige return.

Woran kann das liegen und wie bekomm ich das
performanter hin?

Hast du einzelne Methoden in script/console aufgerufen oder Requests an
die Anwendung getestet? In beiden Fällen, schau im development.log,
welche Anfragen an die Datenbank geschickt werden als Folge einer
solchen Aktion. Wenn du das SQL aus dem Log ausschneidest, kannst du es
auch auf der mysql-Kommandozeile ausführen. Dort bekommst du mit

EXPLAIN SELECT …

eine Erklärung, wie MySQL die Anfrage ausführt.

Michael

Also ich bekomm als Ergebnis bei meiner ProblemAnfrage :

SQL-Befehl: EXPLAIN SELECT terms. * FROM terms INNER JOIN sims ON
sims.term2_id = terms.id INNER JOIN terms terms2 ON sims.term1_id =
terms2.id WHERE ( terms2.bezeichnung IN ( ‘deutschland’ ) ) ORDER BY
sims.similarity DESC LIMIT 15 ;

folgendes (3 Zeilen) :

id :1
select_type :SIMPLE
table :terms
type :ALL
possible_keys :PRIMARY
key :NULL
key_len :NULL
ref :NULL
rows :92790
Extra: Using temporary; Using filesort

id :1
select_type :SIMPLE
table :sims
type :ref
possible_keys :term1_id,term2_id
key :term2_id
key_len :5
ref :mytag_development.terms.id
rows :3
Extra: Using where

id :1
select_type :SIMPLE
table :terms2
type : eq_ref
possible_keys :PRIMARY
key :PRIMARY
key_len :4
ref :mytag_development.sims.term1_id
rows :1
Extra :Using where

Die andere Anfrage liefert mir hingegen (3 Zeilen) :

SQL-Befehl: EXPLAIN SELECT terms. * FROM terms INNER JOIN sims ON
sims.term1_id = terms.id INNER JOIN terms terms2 ON sims.term2_id =
terms2.id WHERE ( terms2.bezeichnung IN ( ‘deutschland’ ) ) ORDER BY
sims.similarity DESC LIMIT 5 ;

id :1
select_type :SIMPLE
table :terms2
type :ALL
possible_keys :PRIMARY
key :NULL
key_len :NULL
ref :NULL
rows :92790
Extra:Using where; Using temporary; Using filesort

id :1
select_type :SIMPLE
table :sims
type :ref
possible_keys :term1_id,term2_id
key :term2_id
key_len :5
ref :mytag_development.terms2.id
rows :3
Extra:Using where

id :1
select_type :SIMPLE
table :terms
type :eq_ref
possible_keys :PRIMARY
key :PRIMARY
key_len :4
ref :mytag_development.sims.term1_id
rows :1
Extra:Using where

Ob das gut oder schlecht ist, kann ich leider nicht sagen, da ich
zuwenig Ahnung habe, aber vielleicht kannst du mir ein wenig was
erklären Michael.

Danke nochmal für deine Zeit

Grüße M.Sch