MySQL实体框架将查询包含到Order By的子选择中

时间:2016-04-05 18:58:53

标签: mysql sql asp.net-mvc entity-framework

我们在MVC 5应用程序中为Entityframework 6支持MSSQL和MySQL。现在,我遇到的问题是当使用MySQL连接器和LINQ时,具有INNER JOIN和ORDER BY的查询将导致查询进入子选择并且ORDER BY应用于外部。这会对性能产生重大影响。使用MSSQL连接器时不会发生这种情况。这是一个例子:

SELECT 
    `Project3`.*
FROM
    (SELECT 
        `Extent1`.*,
            `Extent2`.`Name_First`
    FROM
        `ResultRecord` AS `Extent1`
    LEFT OUTER JOIN `ResultInputEntity` AS `Extent2` ON `Extent1`.`Id` = `Extent2`.`Id`
    WHERE
        `Extent1`.`DateCreated` <= '4/4/2016 6:29:59 PM'
            AND `Extent1`.`DateCreated` >= '12/31/2015 6:30:00 PM'
            AND 0000 = `Extent1`.`CustomerId`
            AND (`Extent1`.`InUseById` IS NULL OR 0000 = `Extent1`.`InUseById` OR `Extent1`.`LockExpiration` < '4/4/2016 6:29:59 PM')
            AND `Extent1`.`DivisionId` IN (0000)
            AND `Extent1`.`IsDeleted` != 1
            AND EXISTS( SELECT 
                1 AS `C1`
            FROM
                `ResultInputEntityIdentification` AS `Extent3`
            WHERE
                `Extent1`.`Id` = `Extent3`.`InputEntity_Id`
                    AND 0 = `Extent3`.`Type`
                    AND '0000' = `Extent3`.`Number`
                    AND NOT (`Extent3`.`Number` IS NULL)
                    OR LENGTH(`Extent3`.`Number`) = 0)
            AND EXISTS( SELECT 
                1 AS `C1`
            FROM
                `ResultRecordAssignment` AS `Extent4`
            WHERE
                1 = `Extent4`.`AssignmentType`
                    AND `Extent4`.`AssignmentId` = 0000
                    OR 2 = `Extent4`.`AssignmentType`
                    AND `Extent4`.`AssignmentId` = 0000
                    AND `Extent4`.`ResultRecordId` = `Extent1`.`Id`)) AS `Project3`
ORDER BY `Project3`.`DateCreated` ASC , `Project3`.`Name_First` ASC , `Project3`.`Id` ASC
LIMIT 0 , 25

这个查询在与几百万行对抗时简单地超时。这是上述查询的解释:

    | id | select_type        | table   | type | possible_keys                                                                                                                                                        | key                        | key_len | ref        | rows     | extra                                        |
    |  1 | PRIMARY            | Extent1 | ref  | IX_ResultRecord_CustomerId,IX_ResultRecord_DateCreated,IX_ResultRecord_IsDeleted,IX_ResultRecord_InUseById,IX_ResultRecord_LockExpiration,IX_ResultRecord_DivisionId | IX_ResultRecord_CustomerId | 4       | const      | 1        | Using where; Using temporary; Using filesort |
    |  1 | PRIMARY            | Extent2 | ref  | PRIMARY                                                                                                                                                              | PRIMARY                    | 8       | Extent1.Id | 1        |                                              |
    |  4 | DEPENDENT SUBQUERY | Extent4 | ref  | IX_RA_AT,IX_RA_A_ID,IX_RA_RR_ID                                                                                                                                      | IX_RA_A_ID                 | 5       | const      | 1        | Using where                                  |
    |  3 | DEPENDENT SUBQUERY | Extent3 | ALL  | IX_InputEntity_Id,IX_InputEntityIdentification_Type,IX_InputEntityIdentification_Number                                                                              |                            |         |            | 14341877 | Using where 

现在,因为它会在MSSQL中生成,或者我们只是将子选择删除到ORDER BY,所以改进是戏剧性的!

SELECT 
    `Extent1`.*, 
    `Extent2`.`Name_First`
FROM
    `ResultRecord` AS `Extent1`
    LEFT OUTER JOIN `ResultInputEntity` AS `Extent2` ON `Extent1`.`Id` = `Extent2`.`Id`
WHERE
    `Extent1`.`DateCreated` <= '4/4/2016 6:29:59 PM'
        AND `Extent1`.`DateCreated` >= '12/31/2015 6:30:00 PM'
        AND 0000 = `Extent1`.`CustomerId`
        AND (`Extent1`.`InUseById` IS NULL
        OR 0000 = `Extent1`.`InUseById`
        OR `Extent1`.`LockExpiration` < '4/4/2016 6:29:59 PM')
        AND `Extent1`.`DivisionId` IN (0000)
        AND `Extent1`.`IsDeleted` != 1
        AND EXISTS( SELECT 
            1 AS `C1`
        FROM
            `ResultInputEntityIdentification` AS `Extent3`
        WHERE
            `Extent1`.`Id` = `Extent3`.`InputEntity_Id`
                AND 9 = `Extent3`.`Type`
                AND '0000' = `Extent3`.`Number`
                AND NOT (`Extent3`.`Number` IS NULL)
                OR LENGTH(`Extent3`.`Number`) = 0)
        AND EXISTS( SELECT 
            1 AS `C1`
        FROM
            `ResultRecordAssignment` AS `Extent4`
        WHERE
            1 = `Extent4`.`AssignmentType`
                AND `Extent4`.`AssignmentId` = 0000
                OR 2 = `Extent4`.`AssignmentType`
                AND `Extent4`.`AssignmentId` = 0000
                AND `Extent4`.`ResultRecordId` = `Extent1`.`Id`)
ORDER BY `Extent1`.`DateCreated` ASC , `Extent2`.`Name_First` ASC , `Extent1`.`Id` ASC
LIMIT 0 , 25

此查询现在在0.10秒内运行!解释计划现在是这样的:

| id | select_type  | table       | type | possible_keys                                                                                                                                                                | key                               | key_len | ref                    | rows | extra                                       |
|  1 | PRIMARY      | <subquery2> | ALL  | distinct_key                                                                                                                                                                 |                                   |         |                        | 1    | Using temporary; Using filesort             |
|  1 | PRIMARY      | Extent1     | ref  | PRIMARY,IX_ResultRecord_CustomerId,IX_ResultRecord_DateCreated,IX_ResultRecord_IsDeleted,IX_ResultRecord_InUseById,IX_ResultRecord_LockExpiration,IX_ResultRecord_DivisionId | PRIMARY                           | 8       | Extent3.InputEntity_Id | 1    | Using where                                 |
|  1 | PRIMARY      | Extent4     | ref  | IX_RA_AT,IX_RA_A_ID,IX_RA_RR_ID                                                                                                                                              | IX_RA_RR_ID                       | 8       | Extent3.InputEntity_Id | 1    | Using where; Start temporary; End temporary |
|  1 | PRIMARY      | Extent2     | ref  | PRIMARY                                                                                                                                                                      | PRIMARY                           | 8       | Extent3.InputEntity_Id | 1    |                                             |
|  2 | MATERIALIZED | Extent3     | ref  | IX_InputEntity_Id,IX_InputEntityIdentification_Type,IX_InputEntityIdentification_Number                                                                                      | IX_InputEntityIdentification_Type | 4       | const                  | 1    | Using where                                 |

现在,我已经多次在整个系统中遇到过这个问题,很明显,MySQL EF 6连接器决定始终将查询包装在子选择中以应用ORDER BY,这是一个问题,但仅限于查询中有一个连接。这导致了重大的性能问题。我看到的一些答案建议修改连接器源代码,但这可能很乏味,任何人都有同样的问题,知道一个解决方法,已经修改过连接器或者除了简单地转移到SQL Server并留下MySQL之外还有其他任何建议,因为那不是一种选择。

2 个答案:

答案 0 :(得分:0)

你有没有看过SQL Server生成的SQL?它是不同的还是只有表演不同?

因为[通常]不是决定查询结构的提供者(即命令子查询)。提供者只是将查询结构转换为DBMS的语法。因此,在您的情况下,问题可能是DBMS优化器。

在类似于我的问题中,我使用了一种基于将查询映射到实体的不同方法,即使用ObjectContext.ExecuteStoreQuery

答案 1 :(得分:0)

事实证明,为了解决这个问题,使用MySQL驱动程序,你的整个lambda必须一次写入。意义在一个地方(..)谓词。这样驱动程序就知道它是一个结果集。现在,如果您构建一个初始的IQueryable,然后继续向其附加访问子​​表的Where子句,它将认为有多个结果集,因此将整个查询包装到子选择中以便对其进行排序和限制。