优化 SQL 多个嵌套选择的查询
Optimizing SQL query of multiple nested selects
我需要在 table 中拆分一列,在新视图或 table.
中,逗号将值分隔到不同的列中
目前对我来说最好的 运行 解决方案是
CREATE VIEW clearTable as select ID,Timestamp,s1,s3,s4,s5,s6,s7,s8,substr(s8r,1,instr(s8r,",")-1) as s9 from
(select ID,Timestamp,s1,s3,s4,s5,s6,s7,substr(s7r,1,instr(s7r,",")-1) as s8,substr(s7r,instr(s7r,",")+1) as s8r from
(select ID,Timestamp,s1,s3,s4,s5,s6,substr(s6r,1,instr(s6r,",")-1) as s7,substr(s6r,instr(s6r,",")+1) as s7r from
(select ID,Timestamp,s1,s3,s4,s5,substr(s5r,1,instr(s5r,",")-1) as s6,substr(s5r,instr(s5r,",")+1) as s6r from
(select ID,Timestamp,s1,s3,s4,substr(s4r,1,instr(s4r,",")-1) as s5,substr(s4r,instr(s4r,",")+1) as s5r from
(select ID,Timestamp,s1,s3,substr(s3r,1,instr(s3r,",")-1) as s4,substr(s3r,instr(s3r,",")+1) as s4r from
(select ID,Timestamp,s1,substr(s2r,1,instr(s2r,",")-1) as s3,substr(s2r,instr(s2r,",")+1) as s3r from
(select ID,Timestamp,s1,substr(s1r,1,instr(s1r,",")-1) as s2,substr(s1r,instr(s1r,",")+1) as s2r from
(select ID,Timestamp,cast(substr(payload,1,instr(payload,",")-1) as TIME) as s1,substr(payload,instr(payload,",")+1) as s1r from thebasetable))))))))
如您所见 - 对于每个 seperation-char 一个新的子查询级别。
结果是,我不会,但我正在寻找更好的方法 - 也许是更有效的解决方案。
作为一个工作示例,您可以使用 this SQL Fiddle.
此外,我想提一下,目前数据存储在 SQLite
中,但这可能会改变,因此不需要仅针对 SQLite
.
进行优化
欢迎所有提示。
让我从您当前解决方案中的一个错误开始(除了是否有效):对于第三行,它在第 s1
列中 returns 0
。据我了解您的意图,您想要 return 有效载荷中的第一个元素,第 3 行是 A
,而不是 0
。
它也没有 return s2
- 我不知道它是有意的还是无意的。我的解决方案 return 它。
现在,回答你的问题 我已经制定了一个运行速度更快的查询(在我的本地 sqlite 上测试时它给了我 3 毫秒,而 运行您的原始查询平均花费 11 毫秒)并且不会嵌套选择这么多。有点复杂,后面再解释。这是查询:
SELECT id,
timestamp,
max(CASE WHEN col = 1 THEN item ELSE '' END) AS s1,
max(CASE WHEN col = 2 THEN item ELSE '' END) AS s2,
max(CASE WHEN col = 3 THEN item ELSE '' END) AS s3,
max(CASE WHEN col = 4 THEN item ELSE '' END) AS s4,
max(CASE WHEN col = 5 THEN item ELSE '' END) AS s5,
max(CASE WHEN col = 6 THEN item ELSE '' END) AS s6,
max(CASE WHEN col = 7 THEN item ELSE '' END) AS s7,
max(CASE WHEN col = 8 THEN item ELSE '' END) AS s8,
max(CASE WHEN col = 9 THEN item ELSE '' END) AS s9
FROM (
WITH RECURSIVE tmp (
id,
timestamp,
item,
data,
col
)
AS (
SELECT id,
timestamp,
substr(payload, 1, instr(payload, ',') - 1),
payload,
1
FROM thebasetable
UNION ALL
SELECT id,
timestamp,
substr(substr(data, instr(data, ',') + 1), 1, instr(substr(data, instr(data, ',') + 1), ',') - 1),
substr(data, instr(data, ',') + 1),
col + 1
FROM tmp
WHERE instr(data, ',') > 0 AND
col < 9
ORDER BY 1
)
SELECT id,
timestamp,
item,
col
FROM tmp
)
GROUP BY id,
timestamp;
查询使用通用 Table 表达式 (CTE)。您可以在 SQLite 的 SQL 语法文档中阅读更多相关信息(查找 WITH
语句)。
CTE 部分是这个:
WITH RECURSIVE tmp (
id,
timestamp,
item,
data,
col
)
AS (
SELECT id,
timestamp,
substr(payload, 1, instr(payload, ',') - 1),
payload,
1
FROM thebasetable
UNION ALL
SELECT id,
timestamp,
substr(substr(data, instr(data, ',') + 1), 1, instr(substr(data, instr(data, ',') + 1), ',') - 1),
substr(data, instr(data, ',') + 1),
col + 1
FROM tmp
WHERE instr(data, ',') > 0 AND
col < 9
ORDER BY 1
)
SELECT id,
timestamp,
item,
col
FROM tmp
它所做的是读取所有具有初始负载的行,从负载中获取 "first" 元素并为其添加等于 1
的 col
值。然后它将有效载荷传递给 CTE 的下一次迭代,但它从有效载荷中切断第一个元素,因此下一次迭代将第二个元素视为第一个元素。它还会为每次下一次迭代增加初始 1
值。
它循环遍历整个有效载荷,每次迭代移动第一个元素,直到它到达有效载荷的末尾(WHERE instr(data, ',') > 0
)。
我还在 WHERE
中添加了第二个条件:col < 9
- 这个条件控制将从负载中提取多少列。该数字应等于您要阅读的列数。如果将其设置为较小的数字,则结果中剩余的列将为空。如果你将它设置为更大的数字,它不会有任何坏处,除了查询会慢一点,这是不必要的。
最后,CTE 被包含在 SELECT
中,它将 CTE 的结果按 ID
和 Timestamp
分组,然后通过检测是否有任何从其余列中获取值行的值,或者不是。很难解释。 CTE部分自己去执行会更好,看看它return是什么,然后你就会明白外面的SELECT
是干什么的。
注意 - 此解决方案需要 SQLite 3.8.3,因为这是将 CTE 引入 SQLite 时的版本。
CTE 是数据库中的一个常见功能。大多数流行的数据库都支持它(我刚刚查了一下,它存在于 MySQL、MS SQL、Oracle、PostgreSQL,所以它看起来很不错)。
我需要在 table 中拆分一列,在新视图或 table.
中,逗号将值分隔到不同的列中
目前对我来说最好的 运行 解决方案是
CREATE VIEW clearTable as select ID,Timestamp,s1,s3,s4,s5,s6,s7,s8,substr(s8r,1,instr(s8r,",")-1) as s9 from
(select ID,Timestamp,s1,s3,s4,s5,s6,s7,substr(s7r,1,instr(s7r,",")-1) as s8,substr(s7r,instr(s7r,",")+1) as s8r from
(select ID,Timestamp,s1,s3,s4,s5,s6,substr(s6r,1,instr(s6r,",")-1) as s7,substr(s6r,instr(s6r,",")+1) as s7r from
(select ID,Timestamp,s1,s3,s4,s5,substr(s5r,1,instr(s5r,",")-1) as s6,substr(s5r,instr(s5r,",")+1) as s6r from
(select ID,Timestamp,s1,s3,s4,substr(s4r,1,instr(s4r,",")-1) as s5,substr(s4r,instr(s4r,",")+1) as s5r from
(select ID,Timestamp,s1,s3,substr(s3r,1,instr(s3r,",")-1) as s4,substr(s3r,instr(s3r,",")+1) as s4r from
(select ID,Timestamp,s1,substr(s2r,1,instr(s2r,",")-1) as s3,substr(s2r,instr(s2r,",")+1) as s3r from
(select ID,Timestamp,s1,substr(s1r,1,instr(s1r,",")-1) as s2,substr(s1r,instr(s1r,",")+1) as s2r from
(select ID,Timestamp,cast(substr(payload,1,instr(payload,",")-1) as TIME) as s1,substr(payload,instr(payload,",")+1) as s1r from thebasetable))))))))
如您所见 - 对于每个 seperation-char 一个新的子查询级别。
结果是,我不会,但我正在寻找更好的方法 - 也许是更有效的解决方案。
作为一个工作示例,您可以使用 this SQL Fiddle.
此外,我想提一下,目前数据存储在 SQLite
中,但这可能会改变,因此不需要仅针对 SQLite
.
进行优化
欢迎所有提示。
让我从您当前解决方案中的一个错误开始(除了是否有效):对于第三行,它在第 s1
列中 returns 0
。据我了解您的意图,您想要 return 有效载荷中的第一个元素,第 3 行是 A
,而不是 0
。
它也没有 return s2
- 我不知道它是有意的还是无意的。我的解决方案 return 它。
现在,回答你的问题 我已经制定了一个运行速度更快的查询(在我的本地 sqlite 上测试时它给了我 3 毫秒,而 运行您的原始查询平均花费 11 毫秒)并且不会嵌套选择这么多。有点复杂,后面再解释。这是查询:
SELECT id,
timestamp,
max(CASE WHEN col = 1 THEN item ELSE '' END) AS s1,
max(CASE WHEN col = 2 THEN item ELSE '' END) AS s2,
max(CASE WHEN col = 3 THEN item ELSE '' END) AS s3,
max(CASE WHEN col = 4 THEN item ELSE '' END) AS s4,
max(CASE WHEN col = 5 THEN item ELSE '' END) AS s5,
max(CASE WHEN col = 6 THEN item ELSE '' END) AS s6,
max(CASE WHEN col = 7 THEN item ELSE '' END) AS s7,
max(CASE WHEN col = 8 THEN item ELSE '' END) AS s8,
max(CASE WHEN col = 9 THEN item ELSE '' END) AS s9
FROM (
WITH RECURSIVE tmp (
id,
timestamp,
item,
data,
col
)
AS (
SELECT id,
timestamp,
substr(payload, 1, instr(payload, ',') - 1),
payload,
1
FROM thebasetable
UNION ALL
SELECT id,
timestamp,
substr(substr(data, instr(data, ',') + 1), 1, instr(substr(data, instr(data, ',') + 1), ',') - 1),
substr(data, instr(data, ',') + 1),
col + 1
FROM tmp
WHERE instr(data, ',') > 0 AND
col < 9
ORDER BY 1
)
SELECT id,
timestamp,
item,
col
FROM tmp
)
GROUP BY id,
timestamp;
查询使用通用 Table 表达式 (CTE)。您可以在 SQLite 的 SQL 语法文档中阅读更多相关信息(查找 WITH
语句)。
CTE 部分是这个:
WITH RECURSIVE tmp (
id,
timestamp,
item,
data,
col
)
AS (
SELECT id,
timestamp,
substr(payload, 1, instr(payload, ',') - 1),
payload,
1
FROM thebasetable
UNION ALL
SELECT id,
timestamp,
substr(substr(data, instr(data, ',') + 1), 1, instr(substr(data, instr(data, ',') + 1), ',') - 1),
substr(data, instr(data, ',') + 1),
col + 1
FROM tmp
WHERE instr(data, ',') > 0 AND
col < 9
ORDER BY 1
)
SELECT id,
timestamp,
item,
col
FROM tmp
它所做的是读取所有具有初始负载的行,从负载中获取 "first" 元素并为其添加等于 1
的 col
值。然后它将有效载荷传递给 CTE 的下一次迭代,但它从有效载荷中切断第一个元素,因此下一次迭代将第二个元素视为第一个元素。它还会为每次下一次迭代增加初始 1
值。
它循环遍历整个有效载荷,每次迭代移动第一个元素,直到它到达有效载荷的末尾(WHERE instr(data, ',') > 0
)。
我还在 WHERE
中添加了第二个条件:col < 9
- 这个条件控制将从负载中提取多少列。该数字应等于您要阅读的列数。如果将其设置为较小的数字,则结果中剩余的列将为空。如果你将它设置为更大的数字,它不会有任何坏处,除了查询会慢一点,这是不必要的。
最后,CTE 被包含在 SELECT
中,它将 CTE 的结果按 ID
和 Timestamp
分组,然后通过检测是否有任何从其余列中获取值行的值,或者不是。很难解释。 CTE部分自己去执行会更好,看看它return是什么,然后你就会明白外面的SELECT
是干什么的。
注意 - 此解决方案需要 SQLite 3.8.3,因为这是将 CTE 引入 SQLite 时的版本。
CTE 是数据库中的一个常见功能。大多数流行的数据库都支持它(我刚刚查了一下,它存在于 MySQL、MS SQL、Oracle、PostgreSQL,所以它看起来很不错)。