如何将SQL从旧的Informix样式转换为ANSI样式?

时间:2015-02-12 18:40:18

标签: sql informix

我有一些用Informix风格的SQL编写的查询。具体而言,此查询选择客户订单中的项目。 (我稍微简化了表格结构,但我保留了有问题的部分。)

SELECT ordi.line_no, ordi.item_code, ordi.desc, ordi.price,
    shpi.location, shpi.status, shpi.ship_code,
    box.box_no, box.tracking_no, shpc.ship_co, mfr.mfr_name,
    sum(shpi.ship_qty), sum(shpi.net_cost)
FROM order_items ordi, ship_items shpi, OUTER ship_boxes box,
    shipping_companies shpc, OUTER (inventory invt, brand, manufacturer mfr)
WHERE ordi.order_id = ?
    AND shpi.order_id = ordi.order_id AND shpi.line_no = ordi.line_no
    AND box.order_id = ordi.order_id AND box.box_no = shpi.box_no
    AND shp.shipper_code = shpi.shipper_code
    AND invt.item_code = ordi.item_code
        AND brand.brand_no = invt.brand_no
        AND mfr.mfr_code = brand.mfr_code
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
ORDER BY ordi.line_no ASC;

(库存由OUTER加入的原因是因为某个类别的商品存储在不同的库存表中.ship_box上的OUTER用于尚未包装的商品。)

我用标准的ANSI风格的JOIN改写了它。这是我得到的:

SELECT ordi.line_no, ordi.item_code, ordi.desc, ordi.price, shpi.location,
    shpi.status, shpi.ship_code, box.box_no, box.tracking_no, shpc.ship_co,
    mfr.mfr_name, sum(shpi.ship_qty), sum(shpi.net_cost)
FROM order_items ordi
    JOIN ship_items shpi ON shpi.order_id = ordi.order_id
        AND shpi.line_no = ordi.line_no
    LEFT JOIN ship_boxes box ON box.order_id = ordi.order_id
        AND box.box_no = shpi.box_no
    JOIN shipping_companies shpc ON shpc.shipper_code = shpi.shipper_code
    LEFT JOIN (inventory invt
        JOIN brand ON brand.brand_no = invt.brand_no
        JOIN manufacturer mfr ON mfr.mfr_code = brand.mfr_code
        ) ON invt.item_code = ordi.item_code
WHERE ordi.order_id = ?
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
ORDER BY ordi.line_no ASC;

结果集完全相同,但性能影响接近2个数量级。对于包含50个项目的订单,第一个查询大约需要50毫秒,而第二个查询大约需要5秒。运行Explain对第一个查询的成本为25,而对第二个查询的成本为14403。我能够确定库存的复杂连接的差异:Informix风格的查询将其执行为3 INDEX PATH / NESTED LOOP JOIN,每个成本为1; ANSI JOIN的执行时间为SEQUENTIAL SCAN,此时费用为383,加起来超过14K。

似乎ANSI JOIN适用于整个广告资源/品牌/制造商表,然后LEFT JOIN对订单商品进行处理。 Informix OUTER (...)能够处理我要求的那个表的小选择(顺序中的项目)。

我做错了什么?有没有办法编写ANSI样式的查询,不会给我带来性能影响?如果必须,我会回到Informix风格的JOIN,但我真的希望还有另一种方式。

谢谢。

编辑: 以下是SET EXPLAIN的结果:

  1. 原始查询:估计费用:18
  2. 我的重写(显式JOIN):估计费用:15629
  3. @HartCO的建议(unbundle库存部分):预计成本:18(但数据是否相同?为什么不像OUTER inventory, brand, manufacturer?)

2 个答案:

答案 0 :(得分:2)

您需要拆分Inventory加入部分并将其更改为LEFT JOIN

SELECT ordi.line_no     , ordi.item_code     , ordi.DESC        , ordi.price
     , shpi.location    , shpi.STATUS        , shpi.ship_code   , box.box_no
     , box.tracking_no  , shpc.ship_co       , mfr.mfr_name     
     , sum(shpi.ship_qty)
     , sum(shpi.net_cost)
FROM order_items ordi
    JOIN ship_items shpi ON  shpi.order_id = ordi.order_id   
        AND shpi.line_no = ordi.line_no
    LEFT JOIN ship_boxes box ON box.order_id = ordi.order_id
        AND box.box_no = shpi.box_no
    LEFT JOIN shipping_companies shpc ON shpc.shipper_code = box.shipper_code
    LEFT JOIN inventory invt ON invt.item_code = ordi.item_code
    LEFT JOIN brand ON brand.brand_no = invt.brand_no
    LEFT JOIN manufacturer mfr ON mfr.mfr_code = brand.mfr_code    
WHERE ordi.order_id = ?
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
ORDER BY ordi.line_no ASC;

注意:我只有一个要测试的SQL Server实例,但我发现执行计划有很大差异,我的查询显示执行一次Nested Loops (Left Outer Join),而你的查询显示Nested Loops (Inner Join)执行了3次。当然看起来像是罪魁祸首。

您的LEFT JOIN ship_boxes实际上是INNER JOIN,因为您使用JOIN shipping_companies加入该表。如果上述查询的结果不符合要求,则应将LEFT JOIN更改为JOIN

答案 1 :(得分:0)

我对原始查询的分解很接近但有一些显着差异。

首先,shipping_company绝对是一个内部联接。这是有道理的,因为这似乎是至少已发送给托运人的物品的读数。托运人可能尚未将所有内容加载到框中,因此从ship_boxes开始向下是外部联接。

一个没有意义的外部联接是inventory。库存中没有的物品是否已发送给托运人?也许我正在读这个关系错了,但在此期间,我把它改成了内部 - 也就是在连接链中跟随它的brandmanufacturer。这使得ship_boxes成为唯一幸存的外部联接。

另一件令人好奇的事情是ship_boxesship_itemsorder_items的双重关系。这会将整个盒子锁定为一个订单。如果整个订单都是一副扑克牌,那么那个盒子里就会浪费很多空间。假设一个盒子可以很容易地包含多个订单,我就消除了这个连接。现在,我意识到“ship_box”并不一定是整个集装箱。它可以是一个纸箱,其大小只是为了符合订单或订单的一部分。这没什么区别。连接到该框的order_id可以来自ship_items。在order_id中有一个重复的ship_boxes字段是一个不必要的冗余,据我所知,它在执行计划中没有任何区别。

我的最终查询,使用SQL Server:

select  ordi.line_no, ordi.item_code, ordi.item_desc, ordi.price,
        shpi.location, shpi.status, shpi.ship_code,
        box.box_no, box.tracking_no, shpc.ship_co, mfr.mfr_name,
        sum(shpi.ship_qty), sum(shpi.net_cost)
from    order_items ordi
join    ship_items shpi
    on  shpi.order_id = ordi.order_id
    and shpi.line_no = ordi.line_no
left join ship_boxes box
    on  box.box_no = shpi.box_no --AND box.order_id = ordi.order_id
join    shipping_companies shpc
    on  shpc.shipper_code = shpi.shipper_code
join    inventory invt
    on  invt.item_code = ordi.item_code
join    brand
    on  brand.brand_no = invt.brand_no
join    manufacturer mfr
    on  mfr.mfr_code = brand.mfr_code
where ordi.order_id = 1
group by ordi.line_no, ordi.item_code, ordi.item_desc, ordi.price,
        shpi.location, shpi.status, shpi.ship_code,
        box.box_no, box.tracking_no, shpc.ship_co, mfr.mfr_name
order by ordi.line_no;

我创建了表并加载了一些测试数据。结果集是正确的,执行计划看起来很简单,正如我所期望的那样。

现在,如果我对库存的假设是错误的,那么将该链更改回外部联接实际上并不会改变执行计划。