【问题标题】:MySQL - Most efficient way to get the newest values of a joined tableMySQL - 获取连接表的最新值的最有效方法
【发布时间】:2016-02-13 23:30:04
【问题描述】:

考虑以下查询:

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 之前应用,我似乎无法添加 nodoid WHERE 语句的一部分,这将解决问题。

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  

【问题讨论】:

  • 您能否发布EXPLAIN SELECT 以及有关该表的一些统计信息,例如每个表中的索引和行数。
  • nodos 的行数约为 400,attributo_16 的行数约为 10000
  • 不确定我是否遗漏了什么——但你为什么在第二个查询中使用子选择。据我所知,只需在 nodeid 上加入两个表,然后使用 ORDER BY 和 LIMIT 1 就可以了。
  • 一个简单的连接会为每个 nodeid 返回 1000 行。我只需要那张桌子上最新的寄存器。
  • 我们可以看看 Giorgios 查询的解释吗

标签: mysql subquery limit derived


【解决方案1】:

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

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 是安全的。

【讨论】:

  • 我尝试了您的解决方案并且它有效,但结果实际上比我原来的查询慢。你认为我需要在桌子上创建一些键吗?
  • @JoeGalind 尝试在(nodoid, fecha) 上创建索引。
【解决方案2】:

假设 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

如果没有适当的索引,这个会很慢。但它在 (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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 2013-07-25
    • 1970-01-01
    • 2014-08-12
    • 2013-06-04
    • 2017-06-30
    • 1970-01-01
    相关资源
    最近更新 更多