Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
复杂的MySQL查询偶尔会运行非终止查询_Mysql_Performance - Fatal编程技术网

复杂的MySQL查询偶尔会运行非终止查询

复杂的MySQL查询偶尔会运行非终止查询,mysql,performance,Mysql,Performance,每隔30分钟,我就会通过cron更新统计表,从PHP脚本运行一个相当复杂的SQL查询 通常情况下,更新工作正常,大约需要2-3分钟来处理大约40000条记录。通常,此查询运行无限长,长达数小时,没有结果。在PHPMyAdmin中查看MySQL进程,然后以50.000秒及以上的时间显示查询 这导致了一个问题,即该表不再整天更新。为什么会这样 查询很复杂,但不特殊: SELECT `a`.`id` AS `id`, `a`.`debitor`

每隔30分钟,我就会通过cron更新统计表,从PHP脚本运行一个相当复杂的SQL查询

通常情况下,更新工作正常,大约需要2-3分钟来处理大约40000条记录。通常,此查询运行无限长,长达数小时,没有结果。在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`
架构位置:

模式原型:

您可以尝试解释您的查询,以查看有关查询的更多详细信息

但我肯定会尽可能用JOIN和GROUP BY替换select部分中的子查询——select中的子查询必须为每一行计算。例如,在我看来,Menge和Schrott列似乎很容易替换。

您可以尝试解释您的查询,以查看有关查询的更多详细信息


但我肯定会尽可能用JOIN和GROUP BY替换select部分中的子查询——select中的子查询必须为每一行计算。例如,在我看来,Menge和Schrott列似乎很容易替换。

以下是我将尝试做的。。。通过提取子查询,我能够将3个元素包装到一个查询/连接结果中。无法应用于处理limit 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

以下是我将尝试做的。。。通过提取子查询,我能够将3个元素包装到一个查询/连接结果中。无法应用于处理limit 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

如果您的查询在大部分时间都有效,那么一些外力正在影响它。您还运行了哪些其他cron作业?运行查询时是否正在运行其他数据库查询?插入/查询是否包装在事务中

因此,我的建议是:

在PHP脚本中使用事务


尝试一种非默认锁定模式。MySQL的默认设置是可重复读取。如果脏读很好,请尝试“未提交读取”,或者如果不可接受,请尝试“更酸的已提交读取”

如果您的查询在大部分时间都有效,那么一些外力会影响它。您还运行了哪些其他cron作业?运行查询时是否正在运行其他数据库查询?插入/查询是否包装在事务中

因此,我的建议是:

在PHP脚本中使用事务


尝试一种非默认锁定模式。MySQL的默认设置是可重复读取。如果脏读很好,请尝试“未提交读取”,或者如果不可接受,请尝试“更酸的已提交读取”

我建议您强制执行解释中显示的所有索引。
有时mysql会认为没有索引更好,因此您的时间更长。

我建议您强制执行解释中显示的所有索引。
有时mysql会认为没有索引更好,因此您会得到更长的时间。

您可以通过在相同的字段上不使用as子句将行减半。您可以共享您的模式吗?另一个可能很重要的辅助信息:查询封装在INSERT语句中,该语句将此查询的结果插入到一个表中,该表与所选字段具有相同的模式,但有自己的索引。无限运行可能来自insert/在insert期间创建索引吗?您可以在相同的字段上不使用as子句将行切成两半。您可以共享您的模式吗?另一个可能很重要的辅助信息:查询封装在INSERT语句中,该语句将此查询的结果插入到一个表中,该表与所选字段具有相同的模式,但有自己的索引。无限运行可能来自插入/插入期间索引的创建吗?是的,也许你是对的。通过使用联接,查询的运行速度可能超过两分钟。但问题是,查询偶尔会无限运行,持续数小时/天,这与查询优化或用我的连接替换子查询无关。根据我的意见…-请参阅我更新的问题以了解解释输出。是的,也许你是对的。通过使用联接,查询的运行速度可能超过两分钟。但问题是,查询偶尔会无限运行,持续数小时/天,这与查询优化或用我的连接替换子查询无关。根据我的意见…-有关解释输出,请参阅我更新的问题。
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 
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