如何简化包含许多内部选择的选择查询并提高PostgreSQL中的性能

时间:2014-08-14 06:54:46

标签: sql performance postgresql

以下是我的select query

  SELECT 
   gtab04.Productid,
   gtab04.Product,
   gtab04.SaleUnit ,
   gtab04.Packing,
   gtab04.ConvFact,
   gtab04.PTR,
   gtab04.MRP, 
   gtab04.PRate,
   gtab04.PTR1,
   gtab04.PTR2,
   gtab04.Location,
   0 As ManufId,
   gtab07.PatentId,
   gtab07.Patent,
   gtab07.PatentCd,
   gtab15.TaxId,
   gtab15.TaxName, 
   gtab15.TaxType,
   gtab15.TaxRate, 
   gtab15.TxOMRP,
   ''::text As Manufacture,
CASE WHEN EXISTS(
      SELECT gtab10.Productid FROM gtab10 INNER JOIN gtab09 ON gtab09.TranId = 
      gtab10.TranId WHERE  gtab10.Productid = gtab04.Productid AND      
      gtab10.BatchId = gtab05.BatchId AND gtab09.acyrid = 7 limit 1) THEN 1 ELSE 0 END   
      AS 
   StkEntered,
      (SELECT SUM(gtab10.qty) FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId =  
      gtab09.TranId WHERE gtab10.Productid = gtab04.Productid AND    
      gtab10.BatchId = gtab05.BatchId AND gtab09.vrid = 6 AND gtab09.acyrid = 7) 
      +
      (SELECT SUM(gtab10.qty) FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId =  
      gtab09.TranId  WHERE gtab10.Productid = gtab04.Productid AND   
      gtab10.BatchId = gtab05.BatchId AND gtab09.vrid in (10,11,23,42,35) AND      
      gtab09.trdate < Cast('2014-06-01' AS timestamp) AND gtab09.acyrid = 7) 
   AS 
   OpeningInQty
      (SELECT SUM(gtab10.qty)FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId =    
      gtab09.TranId  WHERE gtab04.Productid = gtab10.Productid AND     
      gtab10.BatchId = gtab05.BatchId AND gtab09.vrid in( 12,32,33,44 ,45 ,46, 47 ,48 ,     
      49,18 , 34 ,25,27 ,15,26,24 , 43 ,36) AND gtab09.trdate < Cast('2014-06-01'   AS    
      timestamp) AND gtab09.acyrid = 7) AS 
   OpeningOutQty,
   0 AS PurchQty, 
   0 AS SRetQty, 
   0 AS PerInQty, 
   0 AS SaleQty, 
   0 AS StockInQty,
   0 AS StockOutQty, 
   0 AS SaleAmt, 
   0 AS DamageQty, 
   0 AS PRetQty, 
   0 AS PerOutQty,
      (SELECT SUM(gtab10.qty) FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId =     
      gtab09.TranId WHERE gtab04.Productid = gtab10.Productid AND     
      gtab10.BatchId = gtab05.BatchId AND gtab09.vrid in (12 ,32 ,33 ,44 ,45 ,46 ,47,48    
      ,49) AND (gtab09.trdate BETWEEN '2014-05-01' AND '2014-05-31')
      ) AS 
   PrMthSaleQty, 
      (SELECT (SUM(gtab10.qty * gtab10.ptr) ) FROM gtab10 INNER JOIN gtab09 ON   
      gtab10.TranId = gtab09.TranId WHERE gtab04.Productid = gtab10.Productid   AND    
      gtab10.BatchId = gtab05.BatchId AND gtab09.vrid in( 12, 32 , 33 , 44 ,45 ,46 , 47    
      ,48 , 49) AND (FreeOrRpl = 0 OR FreeOrRpl = 2) AND (gtab09.trdate BETWEEN  '2014-   
      05-01' AND '2014-05-31')
      ) AS 
   PrMthSaleAmt, 
   gtab04.LandCost,
   gtab05.PTR AS BatchPTR,
      (case when (
      gtab05.Fqty -  gtab05.FIQty)>0 then (gtab05.Fqty -  gtab05.FIQty) else 0 end) as 
   IssdFreeQty, 
   gtab05.MRP AS BatchMRP, 
   gtab05.PRate AS BatchPRate, 
   gtab04.StdPack,  
      (date_part('day',  (Select Min(Expiry) From gtab05 WHERE  gtab05.Productid =    
      gtab04.Productid And gtab05.Qty > gtab05.IQty)-Cast('2014-06-20' AS    
      timestamp))) AS 
   ExpDys, 
      (SELECT  gtab09.TrDate FROM gtab09 INNER JOIN gtab10 ON gtab09.TranId  
      =gtab10.TranId   
      where gtab09.VrId in (12,32,33,44,45,46, 47,48,49) And   
      gtab10.ProductId =gtab04.ProductId order by TrDate desc limit 1) As 
   LastSaleDate ,
   GTAB05.BatchId, 
   0 AS FreeSaleQty, 
   0 AS ReplSaleQty 
FROM
   gtab04 INNER JOIN  gtab15 ON gtab04.TaxId = gtab15.TaxId  LEFT JOIN gtab05 ON  
   gtab04.Productid = gtab05.Productid   INNER JOIN gtab07 ON gtab07.Patentid =      
   gtab04.Patentid  WHERE (gtab04.Masked = False AND gtab04.Banned = false)  AND  
   gtab04.patentid in  (321, 313 , 267 , 431) ORDER BY Patent, gtab04.Product


EXPLAIN ANALYZE
正如您在上面的选择中看到的,我使用了很多内部选择,而将这些内部选择添加到主< em> select 整体性能会降低,需要花费很多时间才能执行。 我认为(不确定)我的选择查询计划
请帮助改进Query

修改


Sample data(.backup) and query plan

2 个答案:

答案 0 :(得分:2)

优化

  1. 在单一选择中获取所有聚合计算 - 按查询分组。
  2. 用左连接替换子选择
  3. 基于外部条件的内部查询的附加限制。
  4. 这样做:

    with gtab4_5 as (
        select 
            gtab04.Productid, gtab05.BatchId
        FROM
           gtab04 
        LEFT JOIN gtab05 
            ON gtab04.Productid = gtab05.Productid  
        WHERE (gtab04.Masked = False AND gtab04.Banned = false)  
            AND  gtab04.patentid in  (321, 313 , 267 , 431) 
    )
      SELECT 
       gtab04.Productid,
       gtab04.Product,
       gtab04.SaleUnit ,
       gtab04.Packing,
       gtab04.ConvFact,
       gtab04.PTR,
       gtab04.MRP, 
       gtab04.PRate,
       gtab04.PTR1,
       gtab04.PTR2,
       gtab04.Location,
       0 As ManufId,
       gtab07.PatentId,
       gtab07.Patent,
       gtab07.PatentCd,
       gtab15.TaxId,
       gtab15.TaxName, 
       gtab15.TaxType,
       gtab15.TaxRate, 
       gtab15.TxOMRP,
       ''::text As Manufacture,
       stats_ags.StkEntered AS StkEntered,
       stats_ags.OpeningInQty as OpeningInQty,
       stats_ags.OpeningOutQty as OpeningOutQty,
       0 AS PurchQty, 
       0 AS SRetQty, 
       0 AS PerInQty, 
       0 AS SaleQty, 
       0 AS StockInQty,
       0 AS StockOutQty, 
       0 AS SaleAmt, 
       0 AS DamageQty, 
       0 AS PRetQty, 
       0 AS PerOutQty,
       stats_ags.PrMthSaleQty as PrMthSaleQty,
       stats_ags.PrMthSaleAmt as PrMthSaleAmt,
       gtab04.LandCost,
       gtab05.PTR AS BatchPTR,
          (case when (
          gtab05.Fqty -  gtab05.FIQty)>0 then (gtab05.Fqty -  gtab05.FIQty) else 0 end) as 
       IssdFreeQty, 
       gtab05.MRP AS BatchMRP, 
       gtab05.PRate AS BatchPRate, 
       gtab04.StdPack,  
          (date_part('day',  (Select Min(Expiry) From gtab05 WHERE  gtab05.Productid =    
          gtab04.Productid And gtab05.Qty > gtab05.IQty)-Cast('2014-06-20' AS    
          timestamp))) AS 
       ExpDys, 
       stats_ags.LastSaleDate as LastSaleDate ,
       GTAB05.BatchId, 
       0 AS FreeSaleQty, 
       0 AS ReplSaleQty 
    FROM
       gtab04 
    INNER JOIN  gtab15 
        ON gtab04.TaxId = gtab15.TaxId  
    LEFT JOIN gtab05 
        ON gtab04.Productid = gtab05.Productid   
    INNER JOIN gtab07 
        ON gtab07.Patentid = gtab04.Patentid  
    left join (
            SELECT gtab10_9.Productid, gtab10_9.BatchId, 
                max( case 
                    when gtab10_9.acyrid = 7 
                    then gtab10_9.Productid else null 
                end) as StkEntered,
                SUM( case 
                    when gtab10_9.acyrid = 7 and ( 
                        gtab10_9.vrid = 6 
                    or (
                        gtab10_9.vrid in (10,11,23,42,35) 
                        AND gtab10_9.trdate < Cast('2014-06-01' AS timestamp) 
                    ) ) 
                    then gtab10_9.qty else 0 
                end) as OpeningInQty,
                SUM( case 
                    when gtab10_9.acyrid = 7 
                        and gtab10_9.vrid in( 12,32,33,44 ,45 ,46, 47 ,48 , 49,18 , 34 ,25,27 ,15,26,24 , 43 ,36) 
                        AND gtab10_9.trdate < Cast('2014-06-01'   AS timestamp)                 
                    then gtab10_9.qty else 0 
                end) as OpeningOutQty,
                SUM( case 
                    when gtab10_9.acyrid = 7 
                        and gtab10_9.vrid in(12 ,32 ,33 ,44 ,45 ,46 ,47,48,49) 
                        AND gtab10_9.trdate BETWEEN '2014-05-01' AND '2014-05-31'               
                    then gtab10_9.qty else 0 
                end) as PrMthSaleQty,
                SUM( case 
                    when gtab10_9.acyrid = 7 
                        and gtab10_9.vrid in( 12, 32 , 33 , 44 ,45 ,46 , 47,48 , 49) 
                        and (gtab10_9.FreeOrRpl = 0 OR gtab10_9.FreeOrRpl = 2) 
                        AND gtab10_9.trdate BETWEEN '2014-05-01' AND '2014-05-31'               
                    then gtab10_9.qty * gtab10_9.ptr else 0 
                end) as PrMthSaleAmt,
                MAX( case 
                    when gtab10_9.acyrid = 7 
                        and gtab10_9.VrId in (12,32,33,44,45,46, 47,48,49)
                        and (gtab10_9.FreeOrRpl = 0 OR gtab10_9.FreeOrRpl = 2) 
                    then gtab10_9.TrDate else null 
                end) as LastSaleDate
            FROM (
                SELECT gtab10.*, gtab09.*
                FROM gtab10 
                INNER JOIN gtab09 ON gtab09.TranId = gtab10.TranId 
                inner join gtab4_5 on   gtab4_5.Productid = gtab10.Productid and gtab4_5.BatchId = gtab10.BatchId
            ) gtab10_9 
            group by gtab10_9.Productid, gtab10_9.BatchId
        ) stats_ags
        on stats_ags.Productid = gtab04.Productid 
        and stats_ags.BatchId = gtab05.BatchId
    WHERE (gtab04.Masked = False AND gtab04.Banned = false)  
        AND  gtab04.patentid in  (321, 313 , 267 , 431) 
    ORDER BY Patent, gtab04.Product
    

    我对此查询的结果:

    Sort  (cost=82928.96..82931.84 rows=1152 width=306) (actual time=447.433..450.191 rows=2421 loops=1)
      Sort Key: gtab07.patent, gtab04.product
      Sort Method: external merge  Disk: 680kB
    

    与原始版本对比:

    Sort  (cost=2796544.62..2796547.50 rows=1152 width=278) (actual time=47865.883..47868.570 rows=2421 loops=1)
      Sort Key: gtab07.patent, gtab04.product
      Sort Method: external merge  Disk: 680kB
    

    排序和行数的大小仍与...相同。

    基于样本数据的测量PG 9.3

答案 1 :(得分:1)

以下是优化此查询的一些建议,因为我看到它没有运行它。

  • OpeningInQty可以用OR条件计算,只有1选择 需要
  • 可以使用MAX功能选择LastSaleDate,订购更多 昂贵
  • 在计算查询后,应该对整个查询进行排序, 将整个事物移到子查询中
  • 也许您可以在gtab10 INNER JOIN gtab09上使用左连接和子查询 并通过vrid对它们进行分组。您可以将其重复用于外部结果 然后查询。仅通过FreeOrRpl过滤或执行此案例 外部条款。

这是我的SQL版本,如果您与我分享QUERY PLAN,我将不胜感激。

SELECT * FROM (
SELECT 
    gtab04.Productid,
    gtab04.Product,
    gtab04.SaleUnit ,
    gtab04.Packing,
    gtab04.ConvFact,
    gtab04.PTR,
    gtab04.MRP, 
    gtab04.PRate,
    gtab04.PTR1,
    gtab04.PTR2,
    gtab04.Location,
    0 As ManufId,
    gtab07.PatentId,
    gtab07.Patent,
    gtab07.PatentCd,
    gtab15.TaxId,
    gtab15.TaxName, 
    gtab15.TaxType,
    gtab15.TaxRate, 
    gtab15.TxOMRP,
    ''::text As Manufacture,
    CASE WHEN EXISTS(
      SELECT gtab10.Productid 
        FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId = gtab09.TranId
        WHERE  gtab10.Productid = gtab04.Productid AND gtab10.BatchId = gtab05.BatchId 
            AND gtab09.acyrid = 7 limit 1) THEN 1 ELSE 0 END   
  AS 
    StkEntered,
      (SELECT SUM(gtab10.qty) 
        FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId = gtab09.TranId 
        WHERE gtab10.Productid = gtab04.Productid AND gtab10.BatchId = gtab05.BatchId 
            AND (gtab09.vrid = 6 
                OR (gtab09.vrid in (10, 11, 23, 42, 35) AND gtab09.trdate < Cast('2014-06-01' AS timestamp))
            AND gtab09.acyrid = 7)  
  AS 
  OpeningInQty
      (SELECT SUM(gtab10.qty)
        FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId = gtab09.TranId  
        WHERE gtab10.Productid = gtab04.Productid AND gtab10.BatchId = gtab05.BatchId 
            AND gtab09.vrid in ( 12, 32, 33, 44, 45, 46, 47, 48, 49, 18, 34, 25, 27, 15, 26, 24, 43, 36) 
                AND gtab09.trdate < Cast('2014-06-01' AS timestamp) AND gtab09.acyrid = 7) 
    AS 
    OpeningOutQty,
    0 AS PurchQty, 
    0 AS SRetQty, 
    0 AS PerInQty, 
    0 AS SaleQty, 
    0 AS StockInQty,
    0 AS StockOutQty, 
    0 AS SaleAmt, 
    0 AS DamageQty, 
    0 AS PRetQty, 
    0 AS PerOutQty,
      (SELECT SUM(gtab10.qty) 
        FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId = gtab09.TranId 
        WHERE gtab10.Productid = gtab04.Productid AND gtab10.BatchId = gtab05.BatchId 
                AND gtab09.vrid in (12, 32, 33, 44, 45, 46, 47, 48, 49) 
                AND (gtab09.trdate BETWEEN '2014-05-01' AND '2014-05-31')
      ) 
    AS 
    PrMthSaleQty, 
      (SELECT SUM(gtab10.qty * gtab10.ptr) 
        FROM gtab10 INNER JOIN gtab09 ON gtab10.TranId = gtab09.TranId 
        WHERE gtab10.Productid = gtab04.Productid  
            AND gtab10.BatchId = gtab05.BatchId 
            AND gtab09.vrid in (12, 32, 33, 44, 45, 46, 47, 48, 49) 
            AND (gtab09.trdate BETWEEN  '2014-05-01' AND '2014-05-31') 
            AND (FreeOrRpl IN (0, 2))
      ) 
    AS 
    PrMthSaleAmt, 
    gtab04.LandCost,
    gtab05.PTR AS BatchPTR,
        (case when (
        gtab05.Fqty - gtab05.FIQty)>0 then (gtab05.Fqty -  gtab05.FIQty) else 0 end) as 
    IssdFreeQty, 
    gtab05.MRP AS BatchMRP, 
    gtab05.PRate AS BatchPRate, 
    gtab04.StdPack,  
    (date_part('day', (Select Min(Expiry) From gtab05 
    WHERE  gtab05.Productid = gtab04.Productid And gtab05.Qty > gtab05.IQty)-Cast('2014-06-20' AS    
        timestamp))) 
    AS 
    ExpDys, 
        (SELECT  MAX(gtab09.TrDate) FROM gtab09 INNER JOIN gtab10 ON gtab09.TranId  = gtab10.TranId   
        where gtab09.VrId in (12,32,33,44,45,46, 47,48,49) And gtab10.ProductId = gtab04.ProductId) 
  AS 
    LastSaleDate ,
    GTAB05.BatchId, 
    0 AS FreeSaleQty, 
    0 AS ReplSaleQty 
FROM gtab04 
    LEFT JOIN gtab05 ON gtab04.Productid = gtab05.Productid AND gtab04.patentid in (321, 313, 267, 431)
    INNER JOIN  gtab15 ON gtab04.TaxId = gtab15.TaxId      
    INNER JOIN gtab07 ON gtab04.Patentid = gtab07.Patentid   
WHERE (gtab04.Masked = False AND gtab04.Banned = false)  
) v
ORDER BY v.Patent, v.Product