如何使用多个AND和OR优化此慢查询

时间:2015-11-02 16:28:40

标签: mysql query-optimization mysql-workbench

这是我的查询,

SELECT  transaktion.betrag_eur
FROM transaktion_buchung AS transaktion
WHERE transaktion.fk_kunde=303276 
  AND transaktion.konto='Cashback' 
  AND transaktion.storniert_am IS NULL 
  AND transaktion.freigegeben_am IS NULL  
  AND (transaktion.buchungstyp!='Zahlung' OR transaktion.buchungstyp IS NULL)

由于磁盘空间限制,此查询的密钥文件错误不正确。如何优化此查询?

此查询的

和EXPLAIN显示此内容,

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   PRIMARY <derived2>  ALL NULL    NULL    NULL    NULL    653631  Using where
2   DERIVED transaktion ALL NULL    NULL    NULL    NULL    428809  
2   DERIVED buchung ref fk_buchung_transaktion  fk_buchung_transaktion  5   card4students.transaktion.id    1   Using where; Not exists
2   DERIVED filiale eq_ref  PRIMARY PRIMARY 4   card4students.transaktion.fk_filiale    1   
3   UNION   buchung ref konto   konto   1       337602  Using where
3   UNION   transaktion eq_ref  PRIMARY PRIMARY 4   card4students.buchung.fk_transaktion    1   
3   UNION   filiale eq_ref  PRIMARY PRIMARY 4   card4students.transaktion.fk_filiale    1   
3   UNION   auszahlung  eq_ref  PRIMARY PRIMARY 4   card4students.buchung.fk_auszahlung 1   
NULL    UNION RESULT    <union2,3>  ALL NULL    NULL    NULL    NULL    NULL    

在这种情况下,transaktion_buchung是一个视图。

transaktion_buchung的架构:

CREATE 
    ALGORITHM = UNDEFINED 
    DEFINER = `db_card4students`@`%` 
    SQL SECURITY DEFINER VIEW `transaktion_buchung` AS
(
SELECT 
        `transaktion`.`fk_kunde` AS `fk_kunde`,
        `transaktion`.`id` AS `fk_transaktion`,
        `buchung`.`id` AS `fk_buchung`,
        NULL AS `auszahlung_gewuenscht`,
        `transaktion`.`betrag_eur` AS `betrag_eur`,
        `transaktion`.`nettoumsatz` AS `nettoumsatz`,
        `transaktion`.`mwst` AS `mwst`,
        NULL AS `fk_abrechnung`,
        `filiale`.`name` AS `filiale_name`,
        `transaktion`.`storniert_am` AS `storniert_am`,
        `transaktion`.`freigegeben_am` AS `freigegeben_am`,
        `buchung`.`buchungstyp` AS `buchungstyp`,
        `transaktion`.`konto` AS `konto`,
        0 AS `auszahlung_vorgenommen`,
        `transaktion`.`recordbirthdate` AS `recordbirthdate`
    FROM
        ((`transaktion`
        LEFT JOIN `buchung` ON ((`buchung`.`fk_transaktion` = `transaktion`.`id`)))
        LEFT JOIN `filiale` ON ((`filiale`.`id` = `transaktion`.`fk_filiale`)))
    WHERE
        ISNULL(`buchung`.`id`)
) UNION ALL (
SELECT 
        `buchung`.`fk_kunde` AS `fk_kunde`,
        `buchung`.`fk_transaktion` AS `fk_transaktion`,
        `buchung`.`id` AS `fk_buchung`,
        `buchung`.`auszahlung_gewuenscht` AS `auszahlung_gewuenscht`,
        `buchung`.`betrag_eur` AS `betrag_eur`,
        `transaktion`.`nettoumsatz` AS `nettoumsatz`,
        `transaktion`.`mwst` AS `mwst`,
        `auszahlung`.`fk_abrechnung` AS `fk_abrechnung`,
        `filiale`.`name` AS `filiale_name`,
        `transaktion`.`storniert_am` AS `storniert_am`,
        IF(ISNULL(`transaktion`.`id`),
            NOW(),
            `transaktion`.`freigegeben_am`) AS `freigegeben_am`,
        `buchung`.`buchungstyp` AS `buchungstyp`,
        `buchung`.`konto` AS `konto`,
        `buchung`.`auszahlung_vorgenommen` AS `auszahlung_vorgenommen`,
        IF((`transaktion`.`recordbirthdate` IS NOT NULL),
            `transaktion`.`recordbirthdate`,
            `buchung`.`recordbirthdate`) AS `recordbirthdate`
    FROM
        (((`buchung`
        LEFT JOIN `transaktion` ON ((`transaktion`.`id` = `buchung`.`fk_transaktion`)))
        LEFT JOIN `filiale` ON ((`filiale`.`id` = `transaktion`.`fk_filiale`)))
        LEFT JOIN `auszahlung` ON ((`auszahlung`.`id` = `buchung`.`fk_auszahlung`)))
    WHERE
        (`buchung`.`konto` = _UTF8'Cashback')
)

2 个答案:

答案 0 :(得分:0)

要优化此查询,您只需要一个复合索引:

create index idx_transaktion_buchung on
    transaktion_buchung(fk_kunde, konto, stonriert, freigegeben_am, buchungstyp, betrag_eur);

这是查询的覆盖索引,因此应该非常快。

答案 1 :(得分:0)

我怀疑MySQL没有非常有用地使用任何索引。它可能返回一个非常大的结果集(使用连接的索引),但随后扫描每个返回的行以检查您在WHERE子句中查找的值。

因此,在不使用视图的情况下编写查询可能是值得的。我想这样的事情: -

SELECT  transaktion.fk_kunde AS fk_kunde, 
    transaktion.id AS fk_transaktion,  
    buchung.id AS fk_buchung, 
    NULL AS auszahlung_gewuenscht,  
    transaktion.betrag_eur AS betrag_eur, 
    transaktion.nettoumsatz AS nettoumsatz,  
    transaktion.mwst AS mwst, 
    NULL AS fk_abrechnung,  
    filiale.name AS filiale_name,  
    transaktion.storniert_am AS storniert_am, 
    transaktion.freigegeben_am AS freigegeben_am,  
    buchung.buchungstyp AS buchungstyp,  
    transaktion.konto AS konto, 
    0 AS auszahlung_vorgenommen, 
    transaktion.recordbirthdate AS recordbirthdate 
FROM transaktion 
LEFT JOIN buchung ON buchung.fk_transaktion = transaktion.id
LEFT JOIN filiale ON filiale.id = transaktion.fk_filiale
WHERE ISNULL(buchung.id) 
AND transaktion.fk_kunde=303276 
AND transaktion.konto='Cashback' 
AND transaktion.storniert_am IS NULL 
AND transaktion.freigegeben_am IS NULL  
AND (transaktion.buchungstyp!='Zahlung' 
OR transaktion.buchungstyp IS NULL)
UNION ALL 
SELECT  buchung.fk_kunde AS fk_kunde,  
    buchung.fk_transaktion AS fk_transaktion, 
    buchung.id AS fk_buchung,  
    buchung.auszahlung_gewuenscht AS auszahlung_gewuenscht, 
    buchung.betrag_eur AS betrag_eur,  
    transaktion.nettoumsatz AS nettoumsatz, 
    transaktion.mwst AS mwst,  
    auszahlung.fk_abrechnung AS fk_abrechnung,  
    filiale.name AS filiale_name,  
    transaktion.storniert_am AS storniert_am, 
    IF(ISNULL(transaktion.id), NOW(),  transaktion.freigegeben_am) AS freigegeben_am, 
    buchung.buchungstyp AS buchungstyp,  
    buchung.konto AS konto, 
    buchung.auszahlung_vorgenommen AS auszahlung_vorgenommen, 
    IF((transaktion.recordbirthdate IS NOT NULL),  
    transaktion.recordbirthdate, buchung.recordbirthdate) AS recordbirthdate 
FROM buchung 
LEFT JOIN transaktion ON transaktion.id = buchung.fk_transaktion 
LEFT JOIN filiale ON filiale.id = transaktion.fk_filiale
LEFT JOIN auszahlung ON auszahlung.id = buchung.fk_auszahlung
WHERE buchung.konto = _UTF8'Cashback'
AND buchung.fk_kunde=303276 
AND transaktion.storniert_am IS NULL 
AND transaktion.freigegeben_am IS NULL  
AND transaktion.id IS NOT NULL 
AND transaktion.freigegeben_am IS NULL
AND (buchung.buchungstyp!='Zahlung' 
OR buchung.buchungstyp IS NULL)

你可能需要在buchung上覆盖fk_kunde和konto的复合索引。在transaktion上复合指数覆盖fk_kunde和konto。