【问题标题】:Why SQL queries hang on MySQL while MSSQL is OK为什么 SQL 查询挂在 MySQL 上而 MSSQL 正常
【发布时间】:2021-11-05 09:16:31
【问题描述】:

我正在处理一个系统报告,它将检索具有截止日期条件的项目的最后购买成本并检索最大(日期)“最新购买”,当最新购买有多个成本时,它将检索成本较高。此外,它正在按位置和项目分组,请参阅下面的详细信息:

查询:

SELECT t1.location, t1.item, MAX(t1.cost) AS cost, 
       t1.date 
  FROM MyTable AS t1 
     WHERE t1.date = (SELECT MAX(t2.date) 
                        FROM MyTable AS t2 
                             WHERE t1.location = t2.location 
                             AND t1.item = t2.item 
                             AND t1.cost <> 0 
                             AND t2.date <= '2021/10/31') 
 GROUP BY t1.location, t1.item, t1.date;

MyTable 数据:

LOCATION ITEM COST DATE
Loc1 Item#1 100.00 02/01/2021
Loc1 Item#1 105.00 02/15/2021
Loc1 Item#1 103.00 02/19/2021
Loc2 Item#1 107.00 02/01/2021
Loc2 Item#1 105.00 02/13/2021
Loc2 Item#1 100.00 02/13/2021

预期结果:

LOCATION ITEM COST DATE
Loc1 Item#1 103.00 02/19/2021
Loc2 Item#1 105.00 02/13/2021

有人可以帮我解释一下为什么查询在 MSSQL 上正常,而在 MySQL 上却挂起并且执行和检索数据的时间太长???

另外请注意,MyTable 上的 ROWS/DATA 数量在 MSSQL 和 MySQL 上是相同的,大约 500K ROWS。我试图找到一些人说查询具有不相关子查询的问题,这就是为什么查询效率低下的原因。有人可以帮我重新编码查询以使其高效并在 MySQL 上运行吗???请帮忙!

我还尝试在具有 1K ROWS 的表上的 MySQL 上执行它,似乎查询正常。

我希望你能帮助我,我非常需要它。在此先感谢:)

【问题讨论】:

    标签: mysql subquery correlated-subquery mssql-jdbc maxdate


    【解决方案1】:

    假设你的表结构是这样的:

    CREATE TABLE `mytable` (
      `Location` varchar(50) DEFAULT NULL,
      `item` varchar(50) DEFAULT NULL,
      `cost` decimal(14,2) DEFAULT NULL,
      `date` date DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    

    如您所见,表格上没有设置索引。我在本地数据库中创建了一个假表并插入了 500K 行数据。对于您的查询,我在它运行了 10 分钟后停止了,因为老实说,这足以说明查询效率低下。然后我改成JOIN的方式,所以修改后的查询是这样的:

    SELECT t1.location, t1.item, MAX(t1.cost) AS cost, 
           t1.date 
      FROM MyTable AS t1 
         JOIN (SELECT MAX(t2.date) t2d,  t2.location, t2.item
                            FROM MyTable AS t2 
                                 WHERE t2.date <= '2021/10/31'
                                 GROUP BY  t2.location, t2.item) ref
       ON t1.date = ref.t2d 
       AND t1.location = ref.location 
       AND t1.item = ref.item 
     WHERE t1.cost <> 0 
     GROUP BY t1.location, t1.item, t1.date;
    

    仍然使用与上面相同的表结构(没有索引)和 500K 的数据,这个查询返回我:

    1 queries executed, 1 success, 0 errors, 0 warnings
    
    303 row(s) affected
    
    Execution Time : 0.531 sec
    Transfer Time  : 0 sec
    Total Time     : 0.532 sec
    

    在不到 1 秒的时间内,总共 500K 数据中的 303 行。但是,与总的 500K 相比,303 行只是一点点,对吧?因此,我已将表中的大部分 date 列全部更新到 '2021/10/31' 之前并再次测试查询,结果如下:

    1 queries executed, 1 success, 0 errors, 0 warnings
    
    416507 row(s) affected
    
    Execution Time : 16.413 sec
    Transfer Time  : 0.208 sec
    Total Time     : 16.621 sec
    

    查询在大约 16 秒内返回 500K 中的 416,507 条数据。并不是很快,但请记住这是在没有索引的表上。让我们用索引试试这个。现在,我不打算对列中的一列进行索引,然后也一一进行测试,而是一次性为每一列分配其自己的索引并进行测试。添加索引的语法:

    ALTER TABLE mytable   
      ADD INDEX (Location),
      ADD INDEX (item),
      ADD INDEX (cost),
      ADD INDEX (date)
    

    以及创建索引后的查询结果:

    1 queries executed, 1 success, 0 errors, 0 warnings
    
    416507 row(s) affected
    
    Execution Time : 7.753 sec
    Transfer Time  : 0.220 sec
    Total Time     : 7.973 sec
    

    返回时间是在列上建立索引之前的一半。查询的执行计划如下所示:

    id select_type table type possible_keys key key_len ref rows Extra
    1 PRIMARY ALL NULL NULL NULL NULL 248559 Using where; Using temporary; Using filesort
    1 PRIMARY t1 ref Location,item,cost,date Location 53 ref.location 1 Using where
    2 DERIVED t2 ALL Location,item,date NULL NULL NULL 497119 Using where; Using temporary; Using filesort

    最后,让我们回到您的原始查询,看看索引对其性能的影响有多大。索引到位后,您的原始查询将返回:

    1 queries executed, 1 success, 0 errors, 0 warnings
    
    Query: SELECT t1.location, t1.item, MAX(t1.cost) AS cost, 
           t1.date 
      FROM MyTable AS t1 
         WHERE t1.date = (SELECT MAX(t2.date) 
                            FROM MyTable AS t2 
                                 WHERE t1.location = t2.location 
                                 AND t1.item = t2.item 
                                 AND t1.cost <> 0 
                                 AND t2.date <= '2021/10/31') 
     GROUP BY t1.location, t1.item, t1.date;
      
    416507 row(s) affected
    
    Execution Time : 9.840 sec
    Transfer Time  : 0.202 sec
    Total Time     : 10.042 sec
    

    以及执行计划:

    id select_type table type possible_keys key key_len ref rows Extra
    1 PRIMARY t1 ALL NULL NULL NULL NULL 497119 Using where; Using temporary; Using filesort
    2 DEPENDENT SUBQUERY t2 ref Location,item,date Location 53 test.t1.Location 1 Using where

    作为结论,如果您的表上没有索引,您的原始查询肯定需要永远运行,而将其转换为 JOIN 会显着减少持续时间。设置索引后,JOIN 查询比您的原始查询快 2-3 秒,但最重要的是要注意,您的原始查询甚至难以在表有索引之前返回仅 303 行的数据结果。您还没有发布您的表结构,我确实发布了一条(现已删除)评论要求它但是在自己进行测试之后,我很确定您当前的表没有任何索引。

    P/S:这个测试数据对于小提琴来说太大了,所以我在 Pastebin 中有测试代码。您可以在本地服务器上对此进行测试。插入数据语法仅兼容 MySQL v8+ 和 MariaDB 10.2.2 及以上版本。

    https://pastebin.com/GdXhQ5mw

    【讨论】:

      猜你喜欢
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 2013-07-09
      • 1970-01-01
      • 2012-03-22
      相关资源
      最近更新 更多