关于 EntityFrameworkCore.Sqlite 效率的问题

一个简单的应用:管理员发布电子文档,若干用户进行签收。


/*
    三个 Model,分别是代表电子文档的 EDoc,代表签收的 Sign,代表用户的 User。
    EDoc 有创建者导航属性是 User 对象,Sign 有签收者导航属性也是 User 对象。
    EDoc 有对应的签收列表导航属性,是 List<Sign> 对象
*/

    [Index("Title")]
    public class EDoc
    {
        [Key]
        public int Id { get; set; }

        [Comment("标题")]
        [StringLength(250), Required]
        public string Title { get; set; }

        [Comment("文档内容")]
        public string Content { get; set; }

        [Comment("发布的用户Id")]
        public int CreaterId { get; set; }

        [Comment("发布的用户")]
        public User Creater { get; set; }

        [Comment("创建时间")]
        public DateTime CreateDateTime { get; set; } = DateTime.Now;

        [Comment("签收列表")]
        public List<Sign> SignList { get; set; } = new();
    }

    public class Sign
    {
        [Key]
        public int Id { get; set; }

        [Comment("所属的 EDoc 对象 Id")]
        public int EDocId { get; set; }

        [Comment("所属的 EDoc 对象")]
        public EDoc EDoc { get; set; }

        [Comment("签收的用户Id")]
        public int SignerId { get; set; }

        [Comment("签收的用户")]
        public User Signer { get; set; }

        [Comment("签收的内容")]
        public string Content { get; set; }

        [Comment("签收时间")]
        public DateTime? SignTime { get; set; }
    }

    [Index("Username")]
    public class User
    {
        [Key]
        public int Id { get; set; }

        [Comment("显示名称")]
        [StringLength(50), Required]
        public string DisplayName { get; set; }

        [StringLength(50), Required]
        public string Username { get; set; }

        [StringLength(128), Required]
        public string Password { get; set; }

        [Comment("是否禁用")]
        public bool Forbid { get; set; }

        [Comment("是否是管理员")]
        public bool IsAdmin { get; set; }
    }

很简单是吧?

用户登录后,要获取用户需要签收的文档对象:

// _db 为 DBContext,curUserId 是当前登录用户的 Id

//主语句:
var _q = _db.EDoc.OrderByDescending(x => x.CreateDateTime).Where(x => x.SignList.Any(y => y.SignerId == curUserId));

//因为要分页,所以先获取总数:
int recordCount = _q.Count();

//接下来再获取分页的列表:
_q.Skip(...).Take(...).ToList();

整个过程的示意代码步骤如上所示。

数据量也不大,EDoc 表 1.5W,Sign 表 10W

当执行 _q.Count() 时生成如下 SQL 语句:

SELECT COUNT(*)
FROM "EDocs" AS "e"
WHERE EXISTS (
    SELECT 1
    FROM "Signs" AS "s"
    WHERE ("e"."Id" = "s"."EDocId") AND ("s"."SignerId" = @curUserId))

DBContext 分别使用 EntityFrameworkCore.SQLServer 和 EntityFrameworkCore.Sqlite
SQLServer 正常,执行时间 50ms 以下,而切换到 Sqlite 时,执行时间竟然将近 1 分 30 秒!

两个数据库都是通过 EntityFrameworkCore.Tools 生成的,结构和数据相同,主键索引等也都正常。

请教各位看看是哪里出现了问题,是不是我的 Linq 语句写得不对,还是需要对 Sqlite 优化的问题?

建议对比一下在两个数据库上直接执行sql的执行时间,看看是不是数据库本身的效率差异

@hhhljlishen 在两个数据库上直接执行也是如此,SQLServer 很快,Sqlite 很慢。这样的话是 Sqlite 数据库本身的问题?