嵌套的FutureBuilder与数据库中的嵌套调用

时间:2019-05-22 20:51:41

标签: database sqlite flutter flutter-layout sqflite

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

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

员工:

+-------------+-----------------+------+
| 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制作卡,因此我需要来自部门的信息表。 我从 Emplyee 表中获取的那些ID的完整信息位于部门表中。

  

每个表中都有数千行。

我有一个数据库助手类可以连接到数据库:

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的部门对象(模型) 。(getDepartment

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

第二个:

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

哪个更好?? 我应该让FutureBuilders处理该问题还是应该向dbHelper施加压力以使其陷入困境?

如果我使用第一种方法,则(据我所能想象!)必须放置第二个以后的调用(该调用基于其id获取部门对象(模型)的调用) getDepartment函数上的build)),建议不要这样做。

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

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

我检查了一些数据,但无法弄清楚哪个更好。我猜这取决于flutter和sqlite(sqflite)。

哪个更好或更有效?

2 个答案:

答案 0 :(得分:2)

鉴于在此示例中我看不到太多代码,因此我将对您的问题做一个高级答复。

评估方法一

  1. 马上就要开始了:“从Employee表中返回所有dep_id”
  2. 我想从头开始,因为“全部归还”通常都不是一个好的解决方案,尤其是因为您提到表中有很多行。

评估方法二

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

典型的“无尽”列表方法

  1. 您将在 Employees 表上进行查询并加入 Departments 表。
  2. 您将在UI上实现分页,并将值从第一步开始传递给查询。
  3. 从根本上讲,您需要这些变量:Take,Skip,HasMore
  4. 接受:请求每个查询的项目数
  5. 跳过:在下一个查询中要跳过的项目数,这就是驱动用户界面的内存中当前列表中的项目数的大小。
  6. 具有更多信息:您可以在每个查询的响应中进行设置,以使UI知道是否还有更多项目。
  7. 当您向下滚动列表时,到达底部时,您将请求更多物品。

最初发出一个查询,例如:取:10,跳过:0 到达用户界面底部时的下一个查询:接受:10,跳过: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

希望这会有所帮助,根据代码方面,我不确定您实际上要做什么。

答案 1 :(得分:1)

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

如果是这种情况,那么它是为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并可能进行更多处理,并将处理推送到backround线程,然后该线程调用SQLITE库,进行更多处理以了解请求,然后实际读取磁盘上的数据以执行操作,然后返回到java / objc层,该层将响应推送到UI线程,而UI线程又将响应返回给dart。

如果这听起来不是特别有效,那是因为它不是= D。如果您进行几次(甚至几百次),可能没问题,但是如果您陈述的次数成千上万次,它可能会开始变慢。

您建议的替代方法是提出一个大要求。您会比我更清楚这是否明智;如果是几千,但只有几千,并且您返回的数据总是相对较小(例如,只有10-20个字符的名称和部门名称),那么您会说(20 + 20 )* 2000 = 8000b = 80kb的数据。即使您认为开销将是该大小的两倍,160 kb的数据也不足以使任何相对较新的智能手机(毕竟比任何一张照片都要小!)。

现在,借助一些特定领域的知识,您可以对其进行优化。例如,如果您知道部门的数量比雇员少得多(即<100左右),则可以跳过整个加入联接的过程,只需在开始之前请求所有部门并将其放入地图中(dep_id = > 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'这样的东西来执行实际的请求,并且您的列表会调用它来获取数据。但这可能超出了这个问题的范围。