Forum: Rails Germany Rails Datenbank Anfragen performant gestalten

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-24 14:38
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.
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2008-11-24 17:31
(Received via mailing list)
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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-24 20:34
Michael Schuerig 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)"
0719268936a0d71e8dcf7c21810510bf?d=identicon&s=25 Manuel Wiedenmann (Guest)
on 2008-11-24 20:51
(Received via mailing list)
_______________________________________________
rubyonrails-ug mailing list
rubyonrails-ug@headflash.com
http://mailman.headflash.com/mailman/listinfo/rubyonrails-ug
0719268936a0d71e8dcf7c21810510bf?d=identicon&s=25 Manuel Wiedenmann (Guest)
on 2008-11-24 21:01
(Received via mailing list)
_______________________________________________
rubyonrails-ug mailing list
rubyonrails-ug@headflash.com
http://mailman.headflash.com/mailman/listinfo/rubyonrails-ug
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-26 00:00
Manuel Wiedenmann wrote:
> _______________________________________________
> rubyonrails-ug mailing list
> rubyonrails-ug@headflash.com
> 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....
F9d29367490e69ae58826cdcf3e35b2b?d=identicon&s=25 Holger Hänisch (ortwin)
on 2008-11-28 15:50
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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-29 17:17
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.
F3c47e083c860b7ba044ea71bf8512ca?d=identicon&s=25 Alex (Guest)
on 2008-11-29 17:25
(Received via mailing list)
Hi, probier mal:

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


-alex
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-29 17:49
Alex wrote:
> Hi, probier mal:
>
> @term = Term.find_all_by_bezeichnung(term).collect(&:id)
>
>
> -alex

Das funktioniert einwandfrei, ich danke dir vielmals alex :)
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2008-11-29 18:17
(Received via mailing list)
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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-29 18:54
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')))
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2008-11-29 19:35
(Received via mailing list)
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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-29 20:04
Michael Schuerig 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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-11-29 21:01
Matthias Sch. wrote:
> Michael Schuerig 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?
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-12-08 17:55
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
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2008-12-08 20:45
(Received via mailing list)
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
A2a31c6a16e6014feecffbfbe0ae62a1?d=identicon&s=25 Matthias Sch. (mathao)
on 2008-12-08 22:11
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
This topic is locked and can not be replied to.