it-swarm.com.de

Auswählen mehrerer Zeilen in einer Abfrage mit mehreren Bedingungen

Ich kann mehrere Zeilen mit einer Bedingung auswählen, indem ich Folgendes verwende:

SELECT `Col`, `Col2` FROM `Table` WHERE `Col3` IN (?, ?, ?, ?, ?);
# This selects 5 rows

Wie kann dies getan werden, wenn mehrere Bedingungen vorliegen (alle Ganzzahlen sind gleich Operationen)?

Es gibt drei Bedingungen, anhand derer die Abfrage prüfen muss, und alle drei bilden den zusammengesetzten Primärschlüssel.

Eine einzelne Abfrage wählt aus 10 bis 100 Zeilen aus (meistens jedoch nur 10) - sie muss in Bezug auf die Leistung schnell sein. Aus diesem Grund ist die Verwendung mehrerer Abfragen keine gute Idee.

Das CREATE TABLE Aussage ist:

CREATE TABLE `Table Name` (
  `X` smallint(6) unsigned NOT NULL,
  `Y` smallint(6) unsigned NOT NULL,
  `Z` smallint(6) unsigned NOT NULL,
  `Data` varchar(2048) NOT NULL DEFAULT '',
  PRIMARY KEY (`X`,`Y`,`Z`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Bei mehreren Abfragen kann dies folgendermaßen erfolgen:

SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
# This selects 3 rows but I don't want to make 3 calls to the database server for that
7
J. Doe

Sie haben 2 grundlegende syntaktische Optionen, um dies in einer Abfrage zu tun, und 2 Optionen, ob die Werte in der Abfrage gesendet oder zuerst in eine Tabelle geladen werden sollen:

  • das normale AND/OR (Klammern sind hier redundant, aber es ist gut, sie mit OR zu verwenden, nur für den Fall, dass das WHERE komplizierter wird):

    WHERE (  ( x = ? AND y = ? AND z = ? )
          OR ( x = ? AND y = ? AND z = ? )
          ... 
          OR ( x = ? AND y = ? AND z = ? )
          )
    
  • compact IN mit "Zeilenkonstruktor":

    WHERE (x,y,z) IN ((?,?,?), (?,?,?), ...)
    
  • laden Sie die Triplets in eine (temporäre) Tabelle und JOIN:

    CREATE (TEMPORARY) TABLE tmp_table 
    --- ;
    
    INSERT INTO tmp_table (x,y,z)
    VALUES (?,?,?), (?,?,?), ... ;
    
    SELECT t.*
    FROM my_table AS t
      JOIN tmp_table AS tmp
      ON  ( t.x = tmp.x AND t.y = tmp.y AND t.z = tmp.z )
     ;
    
  • oder:

      ON  (t.x, t.y, t.z) = (tmp.x, tmp.y, tmp.z) 
    

Die ersten beiden Optionen sind gleichwertig, können sich jedoch je nach Version in der Effizienz unterscheiden. Diese Syntax von IN mit Tupeln (Zeilenkonstruktoren) verwendet Indizes in älteren Versionen nicht am effektivsten. In 5.7 identifiziert der Optimierer die beiden Syntaxen als äquivalent (siehe MySQL-Dokumente: Row Constructor Expression Optimization ). Test!

Die andere Möglichkeit, eine Tabelle zu verwenden, ist möglicherweise besser, wenn Sie eine Reihe von Parametern/Triplets abfragen möchten. Sie können auch die (temporäre) Tabelle indizieren. Test!

Der grundlegende Rat ist also, in Ihrer Version/Konfiguration/Einrichtung zu testen, wobei die Tabellen ähnliche Größen wie die vorhergesagten Größen und eine unterschiedliche Anzahl von haben Parameter.


Zwei weitere Möglichkeiten, die es ebenfalls wert sein könnten, getestet zu werden:

  • Das Einfache UNION ALL. Da wir nur mehrere identische Abfragen ausführen möchten, bei denen sich nur die Parameter unterscheiden. Ein Nachteil ist, dass die Abfrage sehr lang wird und ungeschickt aussieht, wenn die grundlegende Abfrage komplex ist und Sie viele Drillinge überprüfen müssen:

    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) UNION ALL
    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) UNION ALL
    ---
    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) ;
    
  • Verwenden einer abgeleiteten Tabelle (mit UNION ALL) in der Variante JOIN. Dies kann eine Optimierung verwenden (die in 5.5 oder 5.6 hinzugefügt wurde), die eine abgeleitete Tabelle materialisieren und indizieren kann:

    SELECT t.*
    FROM my_table AS t
      JOIN 
          ( SELECT ? AS x, ? AS y, ? AS z  UNION ALL
            SELECT ?, ?, ?  UNION ALL
            ---
            SELECT ?, ?, ?
          )
        AS tmp
      ON  ( t.x = tmp.x AND t.y = tmp.y AND t.z = tmp.z )
     ;
    
11
ypercubeᵀᴹ

Ein Hinweis für MariaDB. Zeilenkonstruktor-Ausdrucksoptimierung FUNKTIONIERT NICHT! Meine Version ist 10.1.24 und ich sehe es auch nicht in den 10.2 Notizen erwähnt! Ich habe es nicht für ein PRIMARY getestet, aber ich habe es für UNIQUE und normalen INDEX getestet.

Wenn Sie AND/OR verwenden und die Abfragelänge hoch wird (bei ca. 1200 Bedingungen - keine exakte Zahl), wählt der Abfrageanalysator nicht den richtigen Index aus, sodass Sie Ihrer QUERY eine FORCE INDEX-Klausel hinzufügen müssen.

Lange Fragen - schlechte Analyse. Der Zeilenkonstruktor hätte den Tag gerettet.

1
Alex S