嵌套 FutureBuilder 与嵌套调用以从数据库延迟加载
Nested FutureBuilder vs nested calls for lazy loading from database
我需要在我可以遵循的两种方法中选择最佳方法。
我有一个使用 sqflite
保存数据的 Flutter 应用程序,在数据库中我有两个 tables:
员工:
+-------------+-----------------+------+
| 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 table 中的 ListGrid
个部门。我应该查看 Employee table 并从中获取部门 ID,这很容易,但是在获取 dep_id
之后我需要从这些 ID 制作一张卡片,所以我需要 部门 table 的信息。
我从 Emplyee table 获取的那些 ID 的完整信息在 Department table.
中
There are thousands of rows in each table.
我有一个数据库助手 class 来连接到数据库 :
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']));
});
}
有两种方法:
第一个:
在 dbhelper 中定义一个函数 returns 所有 dep_id
来自 Employee table(getDepartmentIds
另一个函数是 returns 该特定 id 的部门对象(模型)。(getDepartment
)
现在我需要两个 FutureBuilder
彼此内部,一个用于获取 ID,另一个用于获取部门模型。
第二个:
- 定义一个函数,该函数首先获取 ID,然后在该函数内部将每个 ID 映射到部门模型。(
getEmployeeDepartments
)
- 所以我需要一个
FutureBuilder
.
哪个更好??
我应该让 FutureBuilders 处理它还是我应该向 dbHelper
施加压力来处理它?
如果我使用第一种方法,那么我必须(据我所知!)进行第二次未来调用(获取 Department Object(model)基于它在 build
函数上的 id(getDepartment
)),建议不要这样做。
第二个的问题是它在 dbHelper
中做了很多嵌套调用。
我用 ListView.builder
来提高性能。
我用一些数据检查了两者,但无法确定哪个更好。我想这取决于 flutter 和 sqlite(sqflite)。
哪个更好或者有没有更好的方法?
鉴于我没有在这个示例中看到太多代码,我将对您的问题进行 high-level 回答。
评估方法一
- 马上这部分就突出了:"returns all dep_id from Employee table"
- 我会说从头开始,因为 "return all" 通常从来都不是一个好的解决方案,特别是因为你提到你的 table 有很多行。
评估方法二
- 我不确定这与第一种方法相比在性能上有何不同,出于同样的原因似乎也很糟糕。我认为这只是大大改变了您的 UI 逻辑。
典型'Endless'列表方法
- 您可以查询 Employees table 并加入 Departments table。
- 您将在 UI 上实施分页,并将您的值传递给第一步中的查询。
- 在基本级别,您将需要这些变量:Take、Skip、HasMore
- 取:每次查询请求的项目数
- 跳过: 下一次查询要跳过的项目数,这将是您当前在内存列表中的项目数的大小驱动您的 UI.
- HasMore: 您可以在每个查询的响应中设置此项,让 UI 知道是否还有更多项目。
- 当您向下滚动列表时,当您到达底部时,您将请求更多项目。
最初发出一个查询例如: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
希望这对您有所帮助,我不完全确定您实际上在尝试做什么 - 就代码而言。
据我所知,您想要做的是获取包含部门相关信息的员工列表。
如果是这样,那就是为 INNER JOIN 量身定做。像这样:
SELECT Employee.*, Department.dep_id, Department.dep_title
FROM Employee INNER JOIN Department
ON Employee.dep_id = Department.dep_id;
(虽然你可能想仔细检查一下,我的 SQL 有点生疏了)。
这将一步完成您所需要的。但是,仍然存在您要问的问题,这似乎是 "Is it more efficient to do many small requests or one big one, and what are the performance ramifications".
这个问题的答案有点特定于 Flutter。当您使用 SQFLITE 发出请求时发生的事情是,它正在处理您传递给它的任何内容,将其发送到 java/objc 并可能进行更多处理并将处理推送到后台线程,然后调用 SQLITE库做更多的理解请求的处理,然后实际读取磁盘上的数据做操作,然后returns回到java/objc层,将响应推送到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' 这样的东西来执行实际请求,并且您的列表会调入它以获取数据。不过,这可能超出了这个问题的范围。
我需要在我可以遵循的两种方法中选择最佳方法。
我有一个使用 sqflite
保存数据的 Flutter 应用程序,在数据库中我有两个 tables:
员工:
+-------------+-----------------+------+
| 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 table 中的 ListGrid
个部门。我应该查看 Employee table 并从中获取部门 ID,这很容易,但是在获取 dep_id
之后我需要从这些 ID 制作一张卡片,所以我需要 部门 table 的信息。
我从 Emplyee table 获取的那些 ID 的完整信息在 Department table.
There are thousands of rows in each table.
我有一个数据库助手 class 来连接到数据库 :
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']));
});
}
有两种方法:
第一个:
在 dbhelper 中定义一个函数 returns 所有
dep_id
来自 Employee table(getDepartmentIds
另一个函数是 returns 该特定 id 的部门对象(模型)。(getDepartment
)现在我需要两个
FutureBuilder
彼此内部,一个用于获取 ID,另一个用于获取部门模型。
第二个:
- 定义一个函数,该函数首先获取 ID,然后在该函数内部将每个 ID 映射到部门模型。(
getEmployeeDepartments
) - 所以我需要一个
FutureBuilder
.
哪个更好??
我应该让 FutureBuilders 处理它还是我应该向 dbHelper
施加压力来处理它?
如果我使用第一种方法,那么我必须(据我所知!)进行第二次未来调用(获取 Department Object(model)基于它在 build
函数上的 id(getDepartment
)),建议不要这样做。
第二个的问题是它在 dbHelper
中做了很多嵌套调用。
我用 ListView.builder
来提高性能。
我用一些数据检查了两者,但无法确定哪个更好。我想这取决于 flutter 和 sqlite(sqflite)。
哪个更好或者有没有更好的方法?
鉴于我没有在这个示例中看到太多代码,我将对您的问题进行 high-level 回答。
评估方法一
- 马上这部分就突出了:"returns all dep_id from Employee table"
- 我会说从头开始,因为 "return all" 通常从来都不是一个好的解决方案,特别是因为你提到你的 table 有很多行。
评估方法二
- 我不确定这与第一种方法相比在性能上有何不同,出于同样的原因似乎也很糟糕。我认为这只是大大改变了您的 UI 逻辑。
典型'Endless'列表方法
- 您可以查询 Employees table 并加入 Departments table。
- 您将在 UI 上实施分页,并将您的值传递给第一步中的查询。
- 在基本级别,您将需要这些变量:Take、Skip、HasMore
- 取:每次查询请求的项目数
- 跳过: 下一次查询要跳过的项目数,这将是您当前在内存列表中的项目数的大小驱动您的 UI.
- HasMore: 您可以在每个查询的响应中设置此项,让 UI 知道是否还有更多项目。
- 当您向下滚动列表时,当您到达底部时,您将请求更多项目。
最初发出一个查询例如: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
希望这对您有所帮助,我不完全确定您实际上在尝试做什么 - 就代码而言。
据我所知,您想要做的是获取包含部门相关信息的员工列表。
如果是这样,那就是为 INNER JOIN 量身定做。像这样:
SELECT Employee.*, Department.dep_id, Department.dep_title
FROM Employee INNER JOIN Department
ON Employee.dep_id = Department.dep_id;
(虽然你可能想仔细检查一下,我的 SQL 有点生疏了)。
这将一步完成您所需要的。但是,仍然存在您要问的问题,这似乎是 "Is it more efficient to do many small requests or one big one, and what are the performance ramifications".
这个问题的答案有点特定于 Flutter。当您使用 SQFLITE 发出请求时发生的事情是,它正在处理您传递给它的任何内容,将其发送到 java/objc 并可能进行更多处理并将处理推送到后台线程,然后调用 SQLITE库做更多的理解请求的处理,然后实际读取磁盘上的数据做操作,然后returns回到java/objc层,将响应推送到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' 这样的东西来执行实际请求,并且您的列表会调入它以获取数据。不过,这可能超出了这个问题的范围。