MySQL - 获取连接表的最新值的最有效方法

时间:2016-02-13 23:30:04

标签: mysql subquery limit derived

考虑以下问题:

SELECT 
  nodos.nombre,
  (SELECT super FROM atributo_16 WHERE nodoid=nodos.nodoid ORDER BY fecha DESC,created_at LIMIT 1) AS descuento_navision_super_cif,
  (SELECT regular FROM atributo_16 WHERE nodoid=nodos.nodoid ORDER BY fecha DESC,created_at LIMIT 1) AS descuento_navision_regular_cif,
  (SELECT diesel FROM atributo_16 WHERE nodoid=nodos.nodoid ORDER BY fecha DESC,created_at LIMIT 1) AS descuento_navision_diesel_cif
FROM 
  nodos 
WHERE 
  nodos.nodotipoid=8;

这很好,但速度很慢。在此示例中,查询重复(3x)到同一个表并具有相同的WHERE。真正的查询有20种这样的子查询到不同的表。我想优化查询。

这是我尝试使用派生表加快速度的尝试之一。创建一个[fecha,created_at]索引,它提高了速度,但查询无法正常工作,因为查询的LIMIT 1部分在JOIN之前应用,我似乎无法添加WHERE语句的nodoid部分,可以解决问题。

SELECT 
  nodos.nombre,
  descuentos.super AS descuento_navision_super_cif,
  descuentos.regular AS descuento_navision_regular_cif,
  descuentos.diesel AS descuento_navision_diesel_cif
FROM 
  nodos 
  LEFT JOIN (SELECT nodoid, super, regular, diesel, ulsd 
    FROM atributo_16 ORDER BY fecha DESC,
    created_at LIMIT 1)descuentos ON descuentos.nodoid=nodos.nodoid
WHERE 
 nodos.nodotipoid=8

已更新 这是第一个查询的EXPLAIN表。

id  select_type         table        type  possible_keys  key         key_len  ref                             rows  Extra                        
1   PRIMARY             nodos        ref   nodotipoid     nodotipoid  4        const                           226                                
4   DEPENDENT SUBQUERY  atributo_16  ref   nodoid         nodoid      4        nodos.nodoid                    376   Using where; Using filesort  
3   DEPENDENT SUBQUERY  atributo_16  ref   nodoid         nodoid      4        nodos.nodoid                    376   Using where; Using filesort  
2   DEPENDENT SUBQUERY  atributo_16  ref   nodoid         nodoid      4        nodos.nodoid                    376   Using where; Using filesort  

2 个答案:

答案 0 :(得分:0)

尝试在派生表中使用变量来获取每组最大记录:

SELECT 
  nodos.nombre,
  super AS descuento_navision_super_cif,
  regular AS descuento_navision_regular_cif,
  diesel AS descuento_navision_diesel_cif
FROM 
  nodos AS t1 
LEFT JOIN (
   SELECT super, regular, diesel, nodoid, 
          @rn := IF (@id = nodoid, @rn + 1,
                    IF (@id := nodoid, 1, 1)) AS rn
   FROM atributo_16
   CROSS JOIN (SELECT @rn := 0,  @id := -1) AS vars
   ORDER BY nodoid, fecha DESC,created_at
) AS t2 ON t1.nodoid = t2.nodoid AND t2.rn = 1
WHERE 
  nodos.nodotipoid=8;

我已假设nodoid<>-1,因此使用此值初始化@id是安全的。

答案 1 :(得分:0)

假设 atributo_16.atributo16id 是主键 - 您可以将相关子选择放入where子句:

SELECT n.nombre, a.super, a.regular, a.diesel
FROM nodos n
LEFT JOIN atributo_16 a USING(nodoid)
WHERE n.nodotipoid = 8
  AND (
    a.atributo16id = (
      SELECT a1.atributo16id
      FROM atributo_16 a1
      WHERE a1.nodoid = n.nodoid
      ORDER BY a1.fecha DESC, a1.created_at
      LIMIT 1
    ) 
    OR a.nodoid IS NULL -- skip this line if using inner join
  )

(nodoid,fecha,created_at)上的索引可以提高性能。

更复杂的解决方案是搜索max(fecha),然后搜索结果min(created_at),然后将结果与最小/最大值上的物理表连接:

SELECT n.nombre, a.super, a.regular, a.diesel
FROM nodos n
LEFT JOIN (
    SELECT minc.nodoid, MIN(a.atributo16id) minpk
    FROM (
        SELECT maxf.nodoid, maxf.maxfecha, MIN(a.created_at) mincreated
        FROM (
            SELECT a.nodoid, MAX(a.fecha) maxfecha
            FROM atributo_16 a
            JOIN nodos n USING(nodoid)
            WHERE n.nodotipoid = 8
            GROUP BY a.nodoid
        ) maxf
        JOIN atributo_16 a
            ON  a.nodoid = maxf.nodoid
            AND a.fecha  = maxf.maxfecha
        GROUP BY maxf.nodoid, maxf.maxfecha
    ) minc
    JOIN atributo_16 a
        ON  a.nodoid     = minc.nodoid
        AND a.fecha      = minc.maxfecha
        AND a.created_at = minc.mincreated
    GROUP BY minc.nodoid, minc.maxfecha, minc.mincreated
) lastreg USING(nodoid)
LEFT JOIN atributo_16 a ON a.atributo16id = lastreg.minpk 
WHERE n.nodotipoid = 8

如果没有aproriate指数,这个会很慢。但它的指数很快(nodoid,fecha),并且索引可能会更快(nodoid,fecha,created_at)。

更新:这是另一个简短的解决方案,对您来说可能更直观,因为子选择基于您的原始查询。

SELECT
  n.nombre,
  a.super AS descuento_navision_super_cif,
  a.regular AS descuento_navision_regular_cif,
  a.diesel AS descuento_navision_diesel_cif
FROM (
  SELECT nodos.nombre,
    (SELECT atributo16id FROM atributo_16 WHERE nodoid=nodos.nodoid ORDER BY fecha DESC,created_at LIMIT 1) AS atributo16id
  FROM nodos
  WHERE nodos.nodotipoid=8
) n
LEFT JOIN atributo_16 a ON a.atributo16id = n.atributo16id