2016-02-13 115 views
1

考慮以下查詢的最新值:MySQL的 - 最有效的方式來獲得一個連接表

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

你可以發佈一個'EXPLAIN SELECT'和一些有關表的統計信息,如索引和每個行的數量。 –

+0

nodos的行數是〜400,atributo_16的行數是〜10000 – JoeGalind

+0

不確定我是否錯過了某些內容 - 但爲什麼在第二個查詢中使用子查詢。據我所知,你只要加入nodeid上的兩個表,然後使用ORDER BY和LIMIT 1即可。 –

回答

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

+0

我試過你的解決方案,它的工作原理,但結果實際上比我原來的查詢慢。你認爲我需要在桌面上創建一些密鑰嗎? – JoeGalind

+0

@JoeGalind嘗試在'(nodoid,fecha)'上創建一個索引。 –

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,出生日期,created_at)索引可以提高性能。

一個更復雜的解決方案是要搜索的最大(日期星),那麼查詢的結果爲分鐘(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 
相關問題