Ankündigung

Einklappen
Keine Ankündigung bisher.

Performance-Problem mit JOIN + OR

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Performance-Problem mit JOIN + OR

    Hi,
    ich brauche Hilfe bei der besseren Formulierung eines Queries. Im Moment ist das ganze absolut inperformant. Ich versuche folgendes:

    PHP-Code:
    SELECT 
        
    FROM a
        INNER JOIN b
            ON b
    .id a.id 
            
    OR a.id NOT IN(600,641,622
        
    INNER JOIN c
            ON c
    .id a.id 
            
    OR a.id NOT IN(600,641,622
        ... 
    Es handelt sich dabei um ein Query, welches Filter für einen Onlineshop darstellt und pro Filter ein neues INNER JOIN braucht. Leider wird das ganze ab dem dritten Filter absolut inperformant (Exponentiell zunehmende Ausführzeit). Ohne die OR-Kondition funktioniert alles sehr schnell. Ich brauche also eine alternative Formulierung wenn dies möglich ist. Leider bekomme ich das im Moment nicht hin




    Hier mal noch ein konkretes Beispiel, welches meinen Fall mit 3 Filtern zeigt:

    PHP-Code:
    SELECT DISTINCT 1 AS `status`, `e`.`entity_id`, `e`.`type_id`, `e`.`attribute_set_id`, `cat_index`.`position` AS `cat_index_position`, `e`.`name`, `e`.`description`, `e`.`price`, `e`.`small_image`, `e`.`tax_class_id`, `e`.`url_key`, `e`.`thumbnail`, `e`.`short_description`, `e`.`special_price`, `e`.`special_from_date`, `e`.`special_to_date`, `e`.`news_from_date`, `e`.`news_to_date`, `e`.`required_options`, `e`.`price_type`, `e`.`weight_type`, `e`.`price_view`, `e`.`shipment_type`, `e`.`image_label`, `e`.`small_image_label`, `e`.`thumbnail_label`, `e`.`links_purchased_separately`, `e`.`msrp_enabled`, `e`.`msrp_display_actual_price_type`, `e`.`msrp`, `e`.`links_exist`, `e`.`cf_marke`, `e`.`cf_marke_value`, `e`.`cf_marke_modell`, `e`.`cf_marke_modell_value`, `e`.`cf_baujahr`, `e`.`cf_baujahr_value`, `e`.`cf_marke_modell_baujahr_radio`, `e`.`cf_marke_modell_baujahr_radio_value`, `e`.`cf_ma_mo_ba_ra_sound`, `e`.`cf_ma_mo_ba_ra_sound_value`, `e`.`cf_ma_mo_ba_ra_pdc`, `e`.`cf_ma_mo_ba_ra_pdc_value`, `e`.`cf_ma_mo_ba_ra_so_pd_can`, `e`.`cf_ma_mo_ba_ra_so_pd_can_value`, `e`.`cf_marke_modell_baujahr_rfk`, `e`.`cf_marke_modell_baujahr_rfk_value`, `e`.`cf_marke_modell_baujahr_gps`, `e`.`cf_marke_modell_baujahr_gps_value`, `e`.`cf_marke_modell_baujahr_info`, `e`.`cf_marke_modell_baujahr_info_value`, `e`.`cf_marke_modell_baujahr_auxusb`, `e`.`cf_marke_modell_baujahr_auxusb_value`, price_index.price AS `indexed_price`, `price_index`.`price`, `price_index`.`final_price`, IF(`price_index`.`tier_price`, LEAST(`price_index`.`min_price`, `price_index`.`tier_price`), `price_index`.`min_price`) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_priceFROM `catalog_product_flat_2` AS `e`
     
    INNER JOIN `catalog_category_product_index` AS `cat_indexON cat_index.product_id=e.entity_id AND cat_index.store_id=AND cat_index.visibility IN(24) AND cat_index.category_id '418'
     
    INNER JOIN `catalog_product_index_price` AS `price_indexON price_index.entity_id e.entity_id AND price_index.website_id '1' AND price_index.customer_group_id 0
     INNER JOIN 
    `catalog_product_index_eav` AS `cf_marke_idxON cf_marke_idx.entity_id e.entity_id AND cf_marke_idx.attribute_id '1037' AND cf_marke_idx.store_id AND cf_marke_idx.value '453' OR (e.entity_id NOT IN(600) AND e.cf_baujahr is null AND e.cf_marke is null AND e.cf_marke_modell is null)
     
    INNER JOIN `catalog_product_index_eav` AS `cf_marke_modell_idxON cf_marke_modell_idx.entity_id e.entity_id AND cf_marke_modell_idx.attribute_id '1038' AND cf_marke_modell_idx.store_id AND cf_marke_modell_idx.value '496' OR (e.entity_id NOT IN(600) AND e.cf_baujahr is null AND e.cf_marke is null AND e.cf_marke_modell is null)
     
    INNER JOIN `catalog_product_index_eav` AS `cf_baujahr_idxON cf_baujahr_idx.entity_id e.entity_id AND cf_baujahr_idx.attribute_id '1039' AND cf_baujahr_idx.store_id AND cf_baujahr_idx.value '452' OR (e.entity_id NOT IN(600) AND e.cf_baujahr is null AND e.cf_marke is null AND e.cf_marke_modell is null
    Diese Abfrage dauer 13 Sekunden und es sollte möglich sein, auch bis zu 12 Filter zu verwenden. Bereits bei 4 Filtern wird das jedoch eine minutendauernde Abfrage.

  • #2
    In der ON Klausel wird die Beziehung zwischen Tabellen beschreiben. Was für eine Beziehung ergibt sich aus a.id NOT IN(600,641,622)? Das führt zu einen Kreuzprodukt zwischen alle Datensätze in a und b wo a.id nicht 600, 641 oder 622 ist. Das kann nicht gewollt sein...

    Kommentar


    • #3
      Ich habe versucht das Problem vereinfacht darzustellen. evtl. ist mir das nicht so gut gelungen.
      um das ganze noch anders zu veranschaulichen, habe ich hier mal etwas hochgeladen und auch eine mögliche Lösung versucht:
      nopaste
      das erste Query entspricht meiner realen IST-Situation. das zweite ist ein Lösungsansatz von mir. dabei bin ich mir jedoch im Moment nicht sicher, ob die beiden Queries äquivalent sind, ich kann aber sagen, dass die beiden die gleichen Ergebnismengen liefern.

      Kommentar


      • #4
        Falls meine Lösung eine gute ist, würde ich auch gern noch wissen, wie man dieses Query oder dieses Query entsprechend umformulieren kann.

        Kommentar


        • #5
          Für was für ein Problem soll das die Lösung sein? Was soll das OR bezwecken?

          Kommentar


          • #6
            Hier ist das implementiert.
            Es handelt sich hier um einen Magento-Shop, dessen herkömmliche Filterfunktion ich ändern will. Das ganze ist ein wenig schwierig zu erklären. Normaler Weise haben die Queries kein OR in der JOIN-Condition. Diese sind eben genau die Änderung, die ich machen will, um die Filter für meine Bedürfnisse anzupassen. Auf der angegebenen Seite kann man das ausprobieren, die Queries werden dann immer mit ausgegeben.

            Im Gegensatz zur normalen Filterfunktion sollen Produkte nur dann durch bestimmte Filter aus der Ergebnismenge herausgefiltert werden, wenn die den verwendeten Filtern entsprechenden Attribute im vom Produkt verwendeten Attributset Verwendung finden. Von diesen Attributsets gibt es 12. alle unterscheiden sich in gewisser Weise und es gibt auch Überschneidungen der Attributmengen.
            BSP:
            Normaler Weise wird kein Produkt mehr angezeigt, welches das Attribut "KFZ-Marke" nicht hat, sobald der Filter "KFZ-Marke" ausgewählt wird.

            Nun gibt es noch ein weiteres Kriterium, welches die herkömmliche Filterergebnismenge verändert:
            Alle Produkte, deren Attributset "Zübehör" heißt, sollen nicht angezeigt werden, sobald mindestens ein Filter ausgewählt ist (in der o.g. Seite betrifft das nur das Produkt "ML200 WIFI MIRRORLINK BOX")

            Ich hoffe ich konnte es ungefähr verständlich machen. Wie gesagt, richtig ist die verwendete Lösung, aber eben inperformant.

            Kommentar


            • #7
              Zitat von magenspueler Beitrag anzeigen
              Im Gegensatz zur normalen Filterfunktion sollen Produkte nur dann durch bestimmte Filter aus der Ergebnismenge herausgefiltert werden, wenn die den verwendeten Filtern entsprechenden Attribute im vom Produkt verwendeten Attributset Verwendung finden.
              Das macht man mit einem LEFT JOIN. (beim join auf die EAV Tabellen INNER gegen LEFT ersetzten und das komplette OR rausnehmen)

              Zitat von magenspueler Beitrag anzeigen
              Alle Produkte, deren Attributset "Zübehör" heißt, sollen nicht angezeigt werden, sobald mindestens ein Filter ausgewählt ist (in der o.g. Seite betrifft das nur das Produkt "ML200 WIFI MIRRORLINK BOX")
              Das ist dann vermutlich e.entity_id NOT IN(600)? Woher bekommst du die Ids, leist du die vorher aus? Wenn ja kannst du das in die WHERE Klausel packen.

              Kommentar


              • #8
                Zitat von erc Beitrag anzeigen
                Das macht man mit einem LEFT JOIN. (beim join auf die EAV Tabellen INNER gegen LEFT ersetzten und das komplette OR rausnehmen)
                Ich habe das mit den LEFT JOINs ausprobiert. Leider hat es nicht das gewünschte Ergebnis gebracht. In beiden Fällen, die ich, wie im Link zusehen ist, ausprobiert hab, werden alle acht Produkte angezeigt. Hattest du das anders gemeint?


                EDIT... Achso, das Zubehör muss ausgeschlossen werden. Stimmt! ...Aber welche Version meiner beiden ist denn die richtige?


                Zitat von erc Beitrag anzeigen
                Das ist dann vermutlich e.entity_id NOT IN(600)? Woher bekommst du die Ids, leist du die vorher aus? Wenn ja kannst du das in die WHERE Klausel packen.
                Ja genau, der Inhalt der Klammer nach IN wird dann größer, wenn die zuvor geholte Menge der auszuschließenden IDs größer ist.

                Kommentar


                • #9
                  Das ist also die Lösung?

                  Kommentar


                  • #10
                    Jap, ich habe aber was nicht bedacht.

                    PHP-Code:
                    AND cf_marke_idx.value '453' 
                    und co müssen raus aus der ON Klausel und mit ins WHERE mit IS NULL.

                    Muss dann so aussehen:
                    PHP-Code:
                    (cf_marke_idx.value '453' OR cf_marke_idx.value IS NULL) AND
                    (
                    cf_marke_modell_idx.value '...' OR cf_marke_modell_idx.value IS NULL
                    ... 

                    Kommentar


                    • #11
                      Dann wäre es jetzt so, wenn ich dich richtig verstehe.

                      die store_id und die attribute_id, die je in den conditions vorkommt, trifft das nicht? wohl nicht, weil dort nie NULL sein kann, oder?

                      Kommentar


                      • #12
                        Doch die können auch NULL sein. (http://en.wikipedia.org/wiki/Join_%2...eft_outer_join) Wenn diese Werte aber NULL sind ist auch value NULL.
                        Wenn dir was unklar ist kannst du den Query auch auseinander nehmen. Mach ein SELECT * und nimm die WHERE Klausel weg.

                        Kommentar


                        • #13
                          Ok, ich danke dir vielmals. Das war mir echt eine riesen Hilfe. Jetzt muss ich nur schauen, wie ich das Magento-Framework dazu überrede, das auch auf diese Weise zu machen.

                          Danke

                          Kommentar

                          Lädt...
                          X