Сложный MySQL Query иногда запускается без ограничений - PullRequest
1 голос
/ 08 марта 2012

Каждые 30 минут я выполняю довольно сложный запрос SQL из скрипта PHP через cron, обновляя статистическую таблицу.

Обычно обновление работает и занимает около 2-3 минут, обрабатывая около 40 000 записей. Иногда этот запрос выполняется бесконечно долго, до нескольких часов без результата. После просмотра процессов MySQL в PHPMyAdmin отображается запрос со временем 50 000 секунд и более.

Это приводит к тому, что таблица больше не обновляется весь день. Почему это происходит?

Запрос сложный, но не специальный:

SELECT `a`.`id`
       AS
       `id`,
       `a`.`debitor`
       AS `debitor`,
       `a`.`wnummer`
       AS `wnummer`,
       `a`.`konto`
       AS `konto`,
       `a`.`datum`
       AS `datum`,
       `a`.`name`
       AS `name`,
       `a`.`name2`
       AS `name2`,
       `a`.`land`
       AS `land`,
       `a`.`plz`
       AS `plz`,
       `a`.`ort`
       AS `ort`,
       `a`.`str`
       AS `str`,
       `a`.`beschichterdatum`
       AS `beschichterdatum`,
       `a`.`werkstattdatum`
       AS `werkstattdatum`,
       `a`.`plandatum`
       AS `plandatum`,
       `a`.`kommision`
       AS `kommision`,
       `a`.`status`
       AS `status`,
       (SELECT `protokoll`.`ts`
        FROM   `protokoll`
        WHERE  ( `protokoll`.`auftrag_id` = `a`.`id` )
               AND ( `protokoll`.`status_neu` = `a`.`status` )
        ORDER  BY `protokoll`.`ts` DESC
        LIMIT  1)
       AS `status_ts`,
       `a`.`tourname`
       AS `tourname`,
       `a`.`anlage`
       AS `anlage`,
       `a`.`user`
       AS `user`,
       `a`.`vertreter`
       AS `vertreter`,
       `a`.`druckmodus`
       AS `druckmodus`,
       (SELECT GROUP_CONCAT(DISTINCT `position`.`maschine` ORDER BY
               `position`.`maschine` ASC SEPARATOR ', ') AS
               `maschine`
        FROM   `position`
        WHERE  ( `position`.`auftrag_id` = `a`.`id` )
               AND ( `position`.`status` = 20 )
               AND ( Length(`position`.`maschine`) > 0 ))
       AS
       `maschine`,
       (SELECT GROUP_CONCAT(DISTINCT `position`.`beschichter` ORDER
               BY
               `position`.`beschichter` ASC SEPARATOR ', ') AS
               `beschichter`
        FROM   `position`
        WHERE  ( `position`.`auftrag_id` = `a`.`id` )
               AND ( `position`.`status` = 50 )
               AND ( Length(`position`.`beschichter`) > 0 ))
       AS
       `beschichter`,
       (SELECT DISTINCT `position`.`rueckstandinfo` AS
                        `rueckstandinfo`
        FROM   `position`
        WHERE  ( `position`.`auftrag_id` = `a`.`id` )
        ORDER  BY IF(( Length(`position`.`rueckstandinfo`) > 0 ), 1,
                  0) DESC
                  ,
                  COUNT(*) DESC
        LIMIT  1)
       AS `rueckstandinfo`,
       (SELECT COUNT(*)
        FROM   `position`
        WHERE  ( `position`.`auftrag_id` = `a`.`id` ))
       AS `menge`,
       (SELECT COUNT(*)
        FROM   `position`
        WHERE  ( `position`.`auftrag_id` = `a`.`id` )
               AND ( `position`.`schrott` = 1 ))
       AS `schrott`,
       (SELECT SUM(`position`.`menge` -
                   `position`.`schrott`)
        FROM   `position`
        WHERE  ( ( `position`.`auftrag_id` = `a`.`id` )
                 AND ( ( CASE
                           WHEN ( `position`.`status` < 41 ) THEN (
                           To_days(`a`.`werkstattdatum`) - To_days(NOW()) )
                           WHEN ( `position`.`status` < 66 ) THEN (
                           To_days(`a`.`beschichterdatum`) - To_days(NOW()) )
                           WHEN ( `position`.`status` < 100 ) THEN (
                           To_days(`a`.`plandatum`) - To_days(NOW()) )
                         END ) < 0 ) ))
       AS `rueckstaendig`,
       ( CASE
           WHEN ( `a`.`status` < 41 ) THEN ( To_days(`a`.`werkstattdatum`) -
                                             To_days(NOW()) )
           WHEN ( `a`.`status` < 66 ) THEN (
           To_days(`a`.`beschichterdatum`) - To_days(
           NOW()) )
           WHEN ( `a`.`status` < 100 ) THEN (
           To_days(`a`.`plandatum`) - To_days(NOW())
                                            )
         END )
       AS `kalendertage`
FROM   `auftrag` `a`

В таблицах установлен движок MyISAM.

ОБЪЯСНИТЕ вывод:

EXPLAIN output

Схема auftrag:

CREATE TABLE `auftrag`
  (
     `id`                  INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Auftrag',
     `debitor`             VARCHAR(255) DEFAULT NULL COMMENT 'Kunde',
     `wnummer`             VARCHAR(255) DEFAULT NULL COMMENT 'W-Nr.',
     `konto`               VARCHAR(255) DEFAULT NULL COMMENT 'Konto',
     `vertreter`           INT(11) NOT NULL DEFAULT '0' COMMENT 'Vertreter',
     `preisliste`          INT(11) NOT NULL DEFAULT '0' COMMENT 'Preisliste',
     `datum`               DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
     COMMENT
     'Auftragseingang',
     `name`                VARCHAR(255) DEFAULT NULL COMMENT 'Name',
     `name2`               VARCHAR(255) DEFAULT NULL COMMENT 'Name 2',
     `land`                VARCHAR(255) DEFAULT NULL COMMENT 'Land',
     `plz`                 VARCHAR(255) DEFAULT NULL COMMENT 'PLZ',
     `ort`                 VARCHAR(255) DEFAULT NULL COMMENT 'Ort',
     `str`                 VARCHAR(255) DEFAULT NULL COMMENT 'Strasse',
     `str2`                VARCHAR(255) DEFAULT NULL COMMENT 'Strasse 2',
     `beschichterdatum`    DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
     COMMENT
     'Beschichtung',
     `werkstattdatum`      DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
     COMMENT
     'Werkstatt',
     `plandatum`           DATE NOT NULL DEFAULT '0000-00-00' COMMENT 'Versand',
     `kiste`               INT(1) NOT NULL DEFAULT '0' COMMENT
     'Anzahl Schleifkisten',
     `anlage`              DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
     COMMENT
     'Auftragsanlage',
     `user`                INT(11) NOT NULL DEFAULT '0' COMMENT 'Benutzer',
     `menge_sofortschrott` INT(11) NOT NULL DEFAULT '0',
     `menge_gesamt`        INT(11) NOT NULL DEFAULT '0' COMMENT
     'Menge Lieferschein',
     `menge_realvomkunden` INT(11) NOT NULL DEFAULT '0' COMMENT 'Menge erhalten'
     ,
     `menge_beschrift`     INT(11) NOT NULL DEFAULT '0' COMMENT
     'Menge Beschriftung',
     `bemerkung`           TEXT COMMENT 'Bemerkung',
     `rueckstandinfo`      VARCHAR(255) DEFAULT NULL COMMENT 'Info Rueckstand',
     `bem_c206`            TEXT,
     `bem_c207`            TEXT,
     `bem_c208`            TEXT,
     `bem_c209`            TEXT,
     `kommision`           VARCHAR(255) DEFAULT NULL COMMENT 'Kommission',
     `status`              INT(11) NOT NULL DEFAULT '0' COMMENT 'Status Auftrag'
     ,
     `belegart`            VARCHAR(255) NOT NULL DEFAULT 'Auftrag' COMMENT
     'Belegart',
     `emailapkd`           VARCHAR(255) DEFAULT NULL COMMENT
     'eMail Arbeitsplan Kunde',
     `emailapvt`           VARCHAR(255) DEFAULT NULL COMMENT
     'eMail Arbeitsplan Vertreter',
     `rech_name1`          VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung Name',
     `rech_name2`          VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung Name 2',
     `rech_land`           VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung Land',
     `rech_plz`            VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung PLZ',
     `rech_ort`            VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung Ort',
     `rech_str`            VARCHAR(255) DEFAULT NULL COMMENT 'Rechnung Strasse',
     `rech_str2`           VARCHAR(255) DEFAULT NULL COMMENT
     'Rechnung Strasse 2',
     `gesendet_an`         TEXT COMMENT 'Arbeitsplan gesendet',
     `angebotmail`         DATETIME DEFAULT NULL COMMENT 'Angebot Intern',
     `requestangebot`      SMALLINT(1) NOT NULL DEFAULT '0' COMMENT
     'Angebot',
     `gutschein`           CHAR(1) NOT NULL DEFAULT 'N' COMMENT 'Gutschein',
     `tourname`            VARCHAR(255) DEFAULT NULL COMMENT 'Tourenname Pickup'
     ,
     `druckmodus`          VARCHAR(255) NOT NULL DEFAULT '5/12/""/DL/""'
     COMMENT
     'Schl,Beschicht,Teillief,DL-ZS',
     `fremdkonto`          VARCHAR(255) DEFAULT NULL COMMENT 'Fremdkonto',
     `versandart`          VARCHAR(255) NOT NULL DEFAULT '9' COMMENT
     'Versandart',
     `c201`                VARCHAR(255) DEFAULT NULL,
     `ansprechpartner`     VARCHAR(255) DEFAULT NULL COMMENT 'AP',
     PRIMARY KEY (`id`),
     KEY `status` (`status`),
     KEY `tourname` (`tourname`),
     KEY `wnummer` (`wnummer`),
     KEY `debitor` (`debitor`),
     KEY `user` (`user`)
  )
ENGINE=myisam
AUTO_INCREMENT=91809
DEFAULT CHARSET=utf8 

Положение схемы:

CREATE TABLE `position`
  (
     `id`             INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Position',
     `auftrag_id`     INT(11) NOT NULL DEFAULT '0' COMMENT 'Auftrag',
     `artikel_id`     VARCHAR(30) NOT NULL COMMENT 'Artikel',
     `menge`          DECIMAL(6, 0) NOT NULL DEFAULT '0' COMMENT 'Menge',
     `listpreis`      DECIMAL(10, 2) NOT NULL DEFAULT '0.00' COMMENT
     'Listenpreis',
     `rabatt`         DECIMAL(6, 2) NOT NULL DEFAULT '0.00' COMMENT
     'Rabatt Prozent',
     `preis`          DECIMAL(13, 2) NOT NULL DEFAULT '0.00' COMMENT 'Preis',
     `wl_rab`         DECIMAL(6, 2) DEFAULT NULL COMMENT 'Rabatt',
     `wl_preis`       DECIMAL(13, 2) DEFAULT NULL COMMENT 'Preis',
     `schrott`        DECIMAL(6, 0) NOT NULL DEFAULT '0' COMMENT 'Schrott',
     `maschine`       VARCHAR(5) DEFAULT NULL COMMENT 'Maschine',
     `beschichter`    VARCHAR(5) DEFAULT NULL COMMENT 'Beschichter',
     `beschichtung`   VARCHAR(30) DEFAULT NULL COMMENT 'Beschichtung',
     `sk_kond`        VARCHAR(2) DEFAULT NULL COMMENT 'Schneidkanten NB/VB',
     `status`         SMALLINT(6) NOT NULL DEFAULT '15' COMMENT 'Status',
     `infotext`       TEXT COMMENT 'Infotext',
     `artikeltext`    TEXT COMMENT 'Artikeltext',
     `schleiftext`    TEXT COMMENT 'Schleifanweisung',
     `name_en`        VARCHAR(255) DEFAULT NULL COMMENT 'Artikeltext EN',
     `name_it`        VARCHAR(255) DEFAULT NULL COMMENT 'Artikeltext IT',
     `name_fr`        VARCHAR(255) DEFAULT NULL COMMENT 'Artikeltext FR',
     `c214`           VARCHAR(200) DEFAULT NULL,
     `enddatum`       DATETIME DEFAULT NULL,
     `reklamation`    CHAR(1) NOT NULL DEFAULT 'N' COMMENT 'Reklamation',
     `werkzeugtyp`    ENUM('Standard', 'LohntNichtInfo', 'Sonderwerkzeug',
     'Kundenwerkzeug') NOT NULL DEFAULT 'Standard' COMMENT 'Werkzeugtyp',
     `durchmesser`    VARCHAR(50) DEFAULT NULL COMMENT 'Durchmesser',
     `rueckstandinfo` VARCHAR(255) DEFAULT NULL COMMENT 'Rueckstandinfo',
     PRIMARY KEY (`id`),
     KEY `status` (`status`),
     KEY `auftrag_id` (`auftrag_id`),
     KEY `artikel_id` (`artikel_id`),
     KEY `maschine` (`maschine`),
     KEY `beschichter` (`beschichter`),
     KEY `rueckstandinfo` (`rueckstandinfo`),
     KEY `enddatum` (`enddatum`)
  )
ENGINE=myisam
AUTO_INCREMENT=2518917
DEFAULT CHARSET=utf8 

Схема протоколла:

CREATE TABLE `protokoll`
  (
     `id`         INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
     `usr_id`     INT(11) NOT NULL COMMENT 'Benutzer',
     `auftrag_id` INT(11) NOT NULL COMMENT 'Auftrag',
     `pos_id`     INT(11) NOT NULL COMMENT 'Position',
     `artikel_id` VARCHAR(255) NOT NULL COMMENT 'Artikel',
     `status_alt` SMALLINT(6) DEFAULT NULL COMMENT 'Status ALT',
     `status_neu` SMALLINT(6) DEFAULT NULL COMMENT 'status NEU',
     `info`       VARCHAR(255) DEFAULT NULL COMMENT 'Infotext',
     `ts`         DATETIME NOT NULL COMMENT 'Zeitstempel',
     PRIMARY KEY (`id`),
     KEY `ts` (`ts`),
     KEY `auftrag_id_2` (`auftrag_id`, `artikel_id`, `status_neu`)
  )
ENGINE=myisam
AUTO_INCREMENT=361183
DEFAULT CHARSET=utf8 

Ответы [ 4 ]

2 голосов
/ 08 марта 2012

Вот что я бы попытался сделать ... согласно извлечению подзапросов, я могу получить 3 элемента, обернутых в один результат запроса / соединения.Не удалось применить к вашим экземплярам с лимитом 1, поэтому оставьте их как встроенные выборки.

SELECT 
      a.id,
      a.debitor,
      a.wnummer,
      a.konto,
      a.datum,
      a.`name`,
      a.name2,
      a.land,
      a.plz,
      a.ort,
      a.`str`,
      a.beschichterdatum,
      a.werkstattdatum,
      a.plandatum,
      a.kommision,
      a.`status`,
      ( SELECT protokoll.ts
           FROM   protokoll
           WHERE  ( protokoll.auftrag_id = a.id )
              AND ( protokoll.status_neu = a.`status` )
           ORDER  BY protokoll.ts DESC
           LIMIT  1) AS status_ts,
      a.tourname,
      a.anlage,
      a.`user`,
      a.vertreter,
      a.druckmodus,
      JoinMaschine.maschine,
      JoinBeschichter.beschichter,
      MengeSchrottRueck.menge,
      MengeSchrottRueck.schrott,
      MengeSchrottRueck.rueckstaendig,

      ( SELECT DISTINCT `position`.rueckstandinfo AS rueckstandinfo
           FROM   `position`
           WHERE  ( `position`.auftrag_id = a.id )
           ORDER BY 
              IF(( Length(`position`.rueckstandinfo) > 0 ), 1, 0) DESC,
              COUNT(*) DESC
           LIMIT  1 ) AS rueckstandinfo,
       ( CASE WHEN ( a.`status` < 41 ) 
                 THEN ( To_days(a.werkstattdatum) - To_days(NOW()) )
              WHEN ( a.`status` < 66 ) 
                 THEN ( To_days(a.beschichterdatum) - To_days( NOW()) )
              WHEN ( a.`status` < 100 ) 
                 THEN (To_days(a.plandatum) - To_days(NOW()) )
         END ) AS kalendertage
FROM   
   auftrag a
      JOIN ( SELECT p.auftrag_id,
                    GROUP_CONCAT(DISTINCT p.maschine 
                                 ORDER BY p.maschine ASC SEPARATOR ', ') AS maschine
               from `position` p
               WHERE p.`status` = 20
                 AND Length(p.maschine) > 0 
               group by p.auftrag_id ) as JoinMaschine
         ON a.ID = JoinMaschine.auftrag_id

      JOIN ( SELECT p.auftrag_id,
                    GROUP_CONCAT(DISTINCT p.beschichter 
                                 ORDER BY p.beschichter ASC SEPARATOR ', ') AS beschichter
                FROM `position` p
               WHERE  ( `position`.auftrag_id = a.id )
                 AND ( `position`.`status` = 50 )
                 AND ( Length(`position`.beschichter) > 0 )
               GROUP BY p.auftrag_id ) AS JoinBeschichter
         ON a.id = JoinBeschichter.auftrag_id

      JOIN ( SELECT p.auftrag_id,
                    COUNT(*) as Menge,
                    SUM( IF( p.schrott = 1, 1, 0 )) as schrott,
                    SUM(p.menge - p.schrott * 
                          IF( ( p.`status` < 41 AND To_days(a.werkstattdatum) - To_days(NOW()) < 0)
                           OR ( p.`status` < 66 AND To_days(a.beschichterdatum) - To_days(NOW()) < 0 )
                           OR ( p.`status` < 100 AND To_days(a.plandatum) - To_days(NOW()) < 0 ), 1, 0 )
                       ) AS rueckstaendig
                FROM `position` p
                group by p.auftrag_id ) as MengeSchrottRueck
         ON a.id = MengeSchrottRueck.auftrag_id
1 голос
/ 09 марта 2012

Если ваш запрос работает «большую часть времени», то на него влияет какая-то внешняя сила. Какие "другие" рабочие места cron у вас есть? Другие запросы к базе данных выполняются, когда ваш запрос выполняется? Ваш запрос вставлен в транзакцию?

Итак, мои предложения:

Использовать транзакцию в вашем скрипте PHP ... Примеры транзакций PHP + MySQL

Попробуйте один из нестандартных режимов блокировки. По умолчанию MySQL является REPEATABLE READ. Если грязное чтение нормально, попробуйте READ UNCOMMITTED, или если это неприемлемо, попробуйте ACID'y READ COMMITTED. http://www.itecsoftware.com/with-nolock-table-hint-equivalent-for-mysql

1 голос
/ 08 марта 2012

Вы можете попробовать EXPLAIN your_query, чтобы увидеть более подробную информацию о запросе.

Но я бы определенно заменил подзапросы в секции select на JOIN и GROUP BY, где это возможно - подзапрос в select должен быть рассчитан для каждой строки.Колонки Менге и Шротта, мне кажется, легко заменить, например.

0 голосов
/ 10 марта 2012

Я рекомендую вам СИЛЬНО все индексы, показанные в объяснении. Иногда mysql может подумать, что нет лучшего индекса, в результате вы получаете больше времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...