2012-03-08 2 views
1

30 분마다 통계 테이블을 업데이트하는 cron을 통해 PHP 스크립트에서 다소 복잡한 SQL 쿼리를 실행하고 있습니다.복잡한 MySQL 쿼리가 가끔씩 실행되지 않습니다.

일반적으로 업데이트가 작동하며 약 40.000 개의 레코드가 약 2-3 분 동안 처리됩니다. Ocassionally이 쿼리는 아무런 결과없이 몇 시간까지 무한히 길게 실행됩니다. PHPMyAdmin에서 MySQL 프로세스를 열면 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 출력 :

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 
,617,

스키마 protokoll는 :

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 
+0

아마도 동일한 필드에서 as 절을 사용하지 않으면 선을 절반으로 줄일 수 있습니다. 스키마를 공유 할 수 있습니까? – rwilliams

+0

중요 할 수있는 또 다른 부가 정보 : 쿼리는이 쿼리의 결과를 선택한 필드와 동일한 구성표를 갖고 있지만 자체 인덱스가있는 단일 테이블에 삽입하는 INSERT 문에 캡슐화됩니다. 무한한 실행은 삽입/색인 작성 중에 발생합니다. – proximus

답변

1

당신은 쿼리에 대한 자세한 내용을 볼 수 EXPLAIN your_query을 시도 할 수 있습니다.

하지만 선택 섹션의 하위 쿼리를 가능한 한 JOIN 및 GROUP BY로 바꾸는 것이 좋습니다. 각 행에 대해 select의 하위 쿼리를 계산해야합니다. 예를 들어, 멘지 (Menge)와 슈롯 (Schrott) 열은 제게 바꿔주는 것처럼 보입니다.

+0

네, 아마도 당신 말이 맞아요. 쿼리는 JOIN을 사용하여 2 분보다 빨리 실행할 수 있습니다. 그러나 문제는 질의 최적화와 관련이 없거나 내 의견에 따르면 내 생각에 하위 쿼리를 JOIN으로 대체 할 수없는 몇 시간 동안 지속되는 질의에 대한 무한한 실행을 의미합니다 ... - EXPLAIN 출력에 대한 업데이트 된 질문 참조 . – proximus

2

다음은 내가 수행하려고하는 작업입니다. 하위 쿼리를 추출 할 때마다 단일 쿼리/결합 결과로 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

쿼리가 "대부분의 시간"에 작동하면 일부 외부 강제로 영향을받습니다. 어떤 "기타"크론 작업을 실행하고 있습니까? 쿼리가 실행 중일 때 다른 데이터베이스 쿼리가 실행되고 있습니까? 삽입/쿼리가 트랜잭션으로 래핑됩니까?

그래서 내 제안 :

당신의 PHP 스크립트에서 트랜잭션을 사용하여 ...

PHP + MySQL transactions examples가 기본이 아닌 잠금 모드 중 하나를 시도하십시오. MySQL의 디폴트는 REPEATABLE READ입니다. 더티 읽기가 좋으면 READ COMMUNICATION을 읽으십시오. 그렇지 않다면 ACID가 READ COMMITTED를 시도하십시오. http://www.itecsoftware.com/with-nolock-table-hint-equivalent-for-mysql

+0

MyISAM 테이블에서 작동합니까? – proximus

0

설명해 주신 모든 색인을 강하게 권합니다. 때때로 mysql은 더 좋은 인덱스가 없다고 생각할 수있다.

관련 문제