2016-03-02 16 views
5

Ho una tabella di oltre 5 milioni di righe. Quando eseguo una query di selezione, sono necessari circa 20 secondi.MYSQL - query di selezione indice e ottimizzazione

SELECT CompUID,Weburl FROM `CompanyTable` WHERE (Alias1='match1' AND Alias2='match2')OR Alias3='match3' OR Alias4='match4' 

Ecco la struttura della tabella:

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Alias1` varchar(150) DEFAULT NULL, 
    `Alias2` varchar(150) DEFAULT NULL, 
    `Alias3` varchar(150) DEFAULT NULL, 
    `Alias4` varchar(150) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    PRIMARY KEY (`CompUID`), 
    KEY `Alias` (`Alias1`,`Alias2`,`Alias3`,`Alias4`) 
) ENGINE=InnoDB AUTO_INCREMENT=5457968 DEFAULT CHARSET=latin1 

Qui è la a spiegare da quella query:

--------+------------------------------------------------------------------------------------------------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+ 
| 1 | SIMPLE  | CompanyTable | ALL |  Alias  | NULL | NULL  | NULL | 5255929 | Using where | 
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+ 

ho usato l'indice composito Alias (Alias1, Alias2, Alias3, Alias4) . Ma credo che non sia il migliore. Per favore suggeriscimi la giusta indicizzazione per questa ricerca di ricerca selezionata.

+1

L'OR = alias3 'match3' 'O Alias4 = 'match4'' è ciò che sta costringendo la scansione completa della tabella. Questa clausola è essenzialmente non indicizzata. Per ottimizzare questa query, è necessario aggiungere un indice su Alias3 e Alias4. – drew010

+0

È possibile controllare il rendimento della query fornendo un suggerimento simile a questo: 'SELEZIONA COMPUID, Weburl FROM 'CompanyTable' usa indice (Alias) ...'. Questo fa la differenza nelle velocità? – zedfoxus

+0

numero di condivisioni della seguente query: "selezionare il conteggio (*) da" CompanyTable "dove Alias1 =" match1 "AND Alias2 =" match2 ";" e "selezionare il conteggio (*) da" CompanyTable "dove Alias1 =" match3 "" e "select count (*) da" CompanyTable "dove Alias1 =" match4 ". –

risposta

3

Per il motore di query per utilizzare una colonna in un indice composito, le colonne a sinistra devono essere soddisfatte prima di. Vale a dire, le colonne devono essere utilizzate come restrizioni che riducono le righe candidate come leggere da sinistra a destra devono essere.

Le clausole alias3 (o alias4) violano questa regola come dice "Non mi interessa quali siano le parti di sinistra (alias1 o alias2 (o alias3)), perché non sono dipendente da esse".

Viene quindi richiesta una scansione completa della tabella per verificare se esistono valori di alias3 (o alias4) che soddisfano le condizioni.

I potenzialmente indici utili in questa condizione sarebbe:

  • INDEX (alias1, alias2): alias1 E alias2 copre questo indice composito
  • INDEX (alias3)
  • INDEX (alias4)

Le statistiche effettive and plan selection richiedono ulteriori indagini, ma almeno ora il pianificatore di query ha qualcosa su cui lavorare h.


Detto questo - e non sono sicuro quale sia il ruolo di un "alias" è - può essere utile per normalizzare il tavolo. Quanto segue cambia leggermente la semantica poiché rilascia la "posizione alias" (che potrebbe essere aggiunta nuovamente) e dovrebbe essere verificata per la correttezza semantica.

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT 
,`CompanyName` varchar(200) DEFAULT NULL 
,PRIMARY KEY (`CompUID`) 
) 

-- (This establishes a unique alias-per-company, which may be incorrect.) 
CREATE TABLE `CompaniesAliases` (
    `CompUID` int(11) NOT NULL 
,`Alias` varchar(150) NOT NULL 
    -- Both CompUID and Alias appear in 'first' positions: 
    -- CompUID for Join, Alias for filter 
,PRIMARY KEY (`CompUID`, `Alias`) 
,KEY (`Alias`) 
-- Alternative, which may change plan selection by eliminating options: 
-- ,PRIMARY KEY (`Alias`, `CompUID`) -- and no single KEY/index on Alias or CompUID 
,FOREIGN KEY(CompUID) REFERENCES CompanyMaster(CompUID) 
) 

Può quindi essere interrogato circa simile a quello originale, essendo differente in quanto non si cura che "Alias" al quale valore:

-- AND constructed by joins (could also use GROUP BY .. HAVING COUNT) 
SELECT c.CompUID FROM `CompanyTable` c 
JOIN `CompaniesAliases` ac1 
ON ac1.CompUID = c.CompUID AND Alias = 'match1' 
JOIN `CompaniesAliases` ac2 
ON ac2.CompUID = c.CompUID AND Alias = 'match2' 

-- OR constructed by union(s) 
UNION 
SELECT c.CompUID FROM `CompanyTable` c 
JOIN `CompaniesAliases` ac1 
ON ac1.CompUID = c.CompUID AND (Alias = 'match3' OR Alias = 'match4') 

mi aspetterei un tale query essere implementato in modo efficiente in SQL Server - YMMV con MySQL.

0

Vorrei suggerire la seguente soluzione, creare una tabella con un campo complex_alias_field. Aumenta un po 'i tuoi dati e i tuoi dati sono ora ridondanti, ma penso che sia una soluzione semplice e immediata.

1.Crea tabella

CREATE TABLE `CompanyMaster` (
`CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Alias1` varchar(150) DEFAULT NULL, 
    `Alias2` varchar(150) DEFAULT NULL, 
    `Alias3` varchar(150) DEFAULT NULL, 
    `Alias4` varchar(150) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    `ComplexAliasQuery` BOOLEAN DEFAULT FALSE, 
    PRIMARY KEY (`CompUID`), 
    KEY `Alias` (`Alias1`,`Alias2`,`Alias3`,`Alias4`), 
    KEY `AliasQuery` (`ComplexAliasQuery`) 
) ENGINE=InnoDB AUTO_INCREMENT=5457968 DEFAULT CHARSET=latin1; 

2. Riempire il nuovo campo ComplexAliasQuery

UPDATE CompanyMaster set ComplexAliasQuery = TRUE WHERE (Alias1='match1' AND Alias2='match2')OR Alias3='match3' OR Alias4='match4'; 

3. Per aggiornare uno dei campi Alias1, alias2, alias3, Alias4

per l'aggiornamento solo riempire anche ComplexAliasQuery. È possibile farlo con un Trigger http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html o nel codice, se non è possibile utilizzare un trigger, perché si sta eseguendo un cluster.

4. Il tuo Query semplice è alla fine

SELECT CompUID,Weburl FROM `CompanyMaster` WHERE ComplexAliasQuery IS TRUE; 

con colpire l'indice

+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 
| id | select_type | table   | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 
| 1 | SIMPLE  | CompanyMaster | ALL | NULL   | NULL | NULL | NULL | 1 | Using where | 
+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 

un'altra soluzione

Se non piace il campo nella tabella CompanyMaster, puoi esternalizzarlo in una nuova tabella e lo ha chiamato IndexAliasCompanyMaster e quindi basta unirsi a questa tabella.

0

Nessuna delle precedenti. Ridisegna lo schema.

Se i 4 alias sono solo sinonimi di un'azienda, fare non splay un array di essi nella tabella, spostarli in un altro tavolo. (User2864740 ottenuto a metà strada là, io sto dicendo di andare fino in fondo.)

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    PRIMARY KEY (`CompUID`), 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

CREATE TABLE `CompaniesAliases` (
    `CompUID` int(11) NOT NULL, 
    `Alias` varchar(150) NOT NULL, 
    PRIMARY KEY (Alias) -- Assuming no two companies can have the same Alias 
    KEY (CompUID) 
) ENGINE=InnoDB; 

(Si dovrebbe convertire tutti i tavoli a InnoDB.)

Ora, la query originale diventa

SELECT CompUID, Weburl 
    FROM `CompanyTable` 
    JOIN CompaniesAliases USING(CompUID) 
    WHERE Alias IN ('match1', 'match2', 'match3', 'match4'); 

e funzionerà molto più velocemente.

Se avete bisogno di mostrare il nome della società e dei suoi alias, considerare

SELECT CompanyName, 
     GROUP_CONCAT(Alias) AS 'Also known as' 
    FROM `CompanyTable` 
    JOIN CompaniesAliases USING(CompUID) 
    WHERE ... 
    GROUP BY CompUID;