【问题标题】:Nested FutureBuilder vs nested calls for lazy loading from database嵌套的 FutureBuilder 与从数据库延迟加载的嵌套调用
【发布时间】:2019-05-22 20:51:40
【问题描述】:

我需要在我可以遵循的两种方法中选择最佳方法。

我有一个使用 sqflite 保存数据的 Flutter 应用程序,在数据库中我有两个表:

员工:

+-------------+-----------------+------+
| employee_id | employee_name   |dep_id|
+-------------+-----------------+------+
|     e12     | Ada Lovelace    | dep1 |
+-------------+-----------------+------+
|     e22     | Albert Einstein | dep2 |
+-------------+-----------------+------+
|     e82     | Grace Hopper    | dep3 |
+-------------+-----------------+------+

SQL:

CREATE TABLE Employee(
    employee_id TEXT NOT NULL PRIMARY KEY,
    employee_name TEXT NOT NULL ,
    dep_id TEXT,
    FOREIGN KEY(dep_id) REFERENCES Department(dep_id)
    ON DELETE SET NULL
);

部门:

+--------+-----------+-------+
| dep_id | dep_title |dep_num|
+--------+-----------+-------+
| dep1   | Math      | dep1  |
+--------+-----------+-------+
| dep2   | Physics   | dep2  |
+--------+-----------+-------+
| dep3   | Computer  | dep3  |
+--------+-----------+-------+

SQL:

CREATE TABLE Department(
    dep_id TEXT NOT NULL PRIMARY KEY,
    dep_title TEXT NOT NULL ,
    dep_num INTEGER,
);

我需要显示存储在 Employee 表中的部门的ListGrid。我应该查看 Employee 表并从中获取部门 id,这很容易,但是在获取 dep_id 之后,我需要从这些 id 中制作一张卡片,所以我需要来自 Department 的信息强>表。 我从 Emplyee 表中获取的那些 id 的完整信息在 Department 表中。

每个表有数千行。

我有一个数据库助手类来连接数据库:

DbHelper 是这样的:

Future<List<String>> getDepartmentIds() async{
    'fetch all dep_id from Employee table'
}

Future<Department> getDepartment(String id) async{
    'fetch Department from Department table for a specific id'
}

Future<List<Department>> getEmployeeDepartments() async{
    '''1.fetch all dep_id from Employee table
    2.for each id fetch Department records from Department table'''

    var ids = await getDepartmentIds();
    List<Departments> deps=[];
    ids.forEach((map) async {
        deps.add(await getDepartment(map['dep_id']));
      });
}

有两种方法:

第一个:

  1. 在 dbhelper 中定义一个函数,该函数返回 Employee 表中的所有 dep_idgetDepartmentIds 以及另一个返回该特定 ID 的部门对象(模型)的函数。(@987654332 @)

  2. 现在我需要两个FutureBuilder,一个用于获取ID,另一个用于获取部门模型。

第二个:

  1. 定义一个函数,该函数首先获取 id,然后在该函数内部将每个 id 映射到部门模型。(getEmployeeDepartments)
  2. 所以我需要一个FutureBuilder

哪个更好?? 我应该让 FutureBuilders 处理它,还是应该向dbHelper 施加压力来处理它?

如果我使用第一种方法,那么我必须(据我所知!)放置第二个未来调用(基于它的 id 获取 Department Object(model) 的那个( getDepartment)) 在 build 函数上,建议不要这样做。

第二个的问题是它在dbHelper 中做了很多嵌套调用。

我使用ListView.builder 来提高性能。

我用一些数据检查了两者,但无法确定哪一个更好。我想这取决于颤振和 sqlite(sqflite)。

哪个更好或有更好的方法?

【问题讨论】:

    标签: database sqlite flutter flutter-layout sqflite


    【解决方案1】:

    鉴于我在此示例中没有看到太多代码,因此我将对您的问题进行高级别的回答。

    评估方法一

    1. 这部分立即突出:“从 Employee 表返回所有 dep_id”
    2. 我会说从头开始,因为“全部返回”通常不是一个好的解决方案,尤其是因为您提到您的表有很多行。

    评估方法二

    1. 我不确定这与第一种方法相比在性能上有何不同,出于同样的原因,这似乎也很糟糕。我认为这只是极大地改变了您的 UI 逻辑。

    典型的“无尽”列表方法

    1. 您将查询 Employees 表并连接到 Departments 表。
    2. 您将在您的 UI 上实现分页,并将您的值传递给第一步中的查询。
    3. 在基本级别上,您需要以下变量:Take、Skip、HasMore
    4. Take:请求每个查询的项目数
    5. 跳过:在下一个查询中要跳过的项目数,这将是您当前在驱动 UI 的内存中列表中的项目数的大小。
    6. HasMore:您可以在每个查询的响应中设置此项,让 UI 知道是否还有更多项目。
    7. 当您向下滚动列表时,当您到达底部时,您将请求更多项目。

    最初发出查询例如:Take: 10, Skip: 0 到达 UI 底部时的下一个查询:Take: 10, Skip: 10 等等。

    示例 sql 查询:

    SELECT * 
    FROM Employees E
       JOIN Departments D on D.id = E.dept_id
    order by E.employee_name  
    offset {SKIP#} rows
    FETCH NEXT {TAKE#} rows only
    

    希望这会有所帮助,我不完全确定您实际上要做什么 - 就代码而言。

    【讨论】:

    • 谢谢@alan-negrete,它很有帮助,我在这里读到了分页,但我不知道 SQL 中的偏移量。
    • 嗯。在这种情况下,使用 offset 和 fetch next 可能会正常工作,因为我假设列表是相当静态的,但总的来说,我建议使用 WHERE &gt;= lastValue 方法,因为如果添加项目,这可以更好地避免重复或跳过/在页面查询请求之间从数据库中删除。
    • @rmtmckenzie 感谢您的建议,我会尝试两种方法。
    • @alan-negrete 的这种方法是最有效的,我想添加以确保按顺序排列的字段(例如 E.employee_name)确保绝对排序,否则您可能会“松动” ' 如果您有分页功能(10、下 10、下 10 等等),则记录。使用实际的 order by 字段后跟主键是一种标准的解决方法。
    • @NoobN3rd 是的,祝你好运!我建议制作一个通用小部件来处理所有这些无休止的滚动,以便您每次到达底部时执行回调函数。
    【解决方案2】:

    据我所知,您要做的是获取包含相关信息(包括部门)的员工列表。

    如果是这样,那么它就是为 INNER JOIN 量身定做的。像这样的:

    SELECT Employee.*, Department.dep_id, Department.dep_title 
       FROM Employee INNER JOIN Department 
       ON Employee.dep_id = Department.dep_id;
    

    (尽管您可能想仔细检查一下,我的 SQL 有点生疏)。

    这一步就能完成您需要的工作。但是,仍然存在您要问的问题,这似乎是“执行许多小请求还是执行一个大请求更有效率,以及性能影响是什么”。

    这个问题的答案有点特定于 Flutter。当您使用 SQFLITE 发出请求时会发生什么,它正在处理您传递给它的任何内容,将其发送到 java/objc 并可能进行更多处理并将处理推送到后台线程,然后调用 SQLITE 库做更多的处理来理解请求,然后实际读取磁盘上的数据进行操作,然后返回到 java/objc 层,该层将响应推送到 UI 线程,然后再响应回 dart。

    如果这听起来不是特别有效,那是因为它不是 =D。如果你这样做几次(甚至几百次)可能没问题,但如果你说的达到数千次,它可能会开始放慢速度。

    您提出的替代方案是执行一项大型请求。你会比我更清楚这是否明智;如果它是几千但只有几千,并且您返回的数据总是相对较小(即只有 10-20 个字符的名称和部门名称),那么您会说 (20+20 )*2000 = 8000b = 80kb 数据。即使您假设开销会增加一倍,160 kb 的数据也不应该足以吓倒任何相对较新的智能手机(毕竟这比任何一张照片都要小得多!)。

    现在,利用一些特定领域的知识,您可以对其进行优化。例如,如果您知道部门的数量比员工少得多(即 dep_title),然后一旦你请求了员工,你就可以简单地自己从 dep_id 到 dep_title 进行查找。这样您的请求就不必一遍又一遍地包含 dep_title。

    话虽如此,您可能需要考虑分页员工查找,无论您是否使用联接。您可以通过一次请求 100 名员工(或任何数量)而不是整个批次来做到这一点 - 这样您就没有通过堆栈进行 1000 多次调用的开销,但您也没有大块内存中的所有数据。

    SELECT * FROM Employee
    WHERE employee_name >= LastValue
    ORDER BY employee_name
    LIMIT 100;
    

    不幸的是,这与 Flutter 处理列表的方式不相符,因此您可能需要有类似“EmployeeDatabaseManager”之类的东西来执行实际请求,并且您的列表会调用它来获取数据。不过,这可能超出了这个问题的范围。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      相关资源
      最近更新 更多