建索引前后,碰的行数和取数方式
给 v 建索引,把前后的取数方式和碰的行数一起量出来。输出四样(用 / 隔开)。
t 表:200 行 id 1 ~ 200,主键 cat 只有 "A" 和 "B" 两种值(奇数行 A、偶数行 B) v i * 3 % 500,取值范围 0 ~ 499 note "n1" ~ "n200" 两个现成的工具函数(题面里已经写好) plan(sql) → 打出 SCAN(扫全表)还是 SEARCH(顺着索引找) counted(sql) → 数出这条查询把条件判了多少次(条件里要放 tick())
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, cat TEXT NOT NULL, "
"v INTEGER NOT NULL, note TEXT NOT NULL)")
db.executemany("INSERT INTO t VALUES (?,?,?,?)",
[(i, "A" if i % 2 else "B", i * 3 % 500, "n" + str(i))
for i in range(1, 201)])
db.commit()
def plan(sql):
"""看数据库打算怎么取数:SCAN=扫全表,SEARCH=顺着索引找。"""
return db.execute("EXPLAIN QUERY PLAN " + sql).fetchall()[0][3]
def counted(sql):
"""数一数这条查询把条件判了多少次(= 扫了多少行)。
⚠️ tick() 必须写在条件的**最前面**:写在后面时它只对已经匹配上的行求值,
数出来的就变成"结果有几行",而不是"扫了几行"。"""
hits = {"n": 0}
db.create_function("tick", 0, lambda: hits.__setitem__("n", hits["n"] + 1) or 1)
db.execute(sql).fetchall()
return hits["n"]
p1 = plan("SELECT * FROM t WHERE v = 99").split()[0]
c1 = counted("SELECT * FROM t WHERE tick() AND v = 99")
db.execute("CREATE INDEX ix_v ON t(v)")
p2 = plan("SELECT * FROM t WHERE v = 99").split()[0]
c2 = counted("SELECT * FROM t WHERE tick() AND v = 99")
print(p1 + "/" + str(c1) + "/" + p2 + "/" + str(c2))
全部评论