Obsidian Dataview入门:用元数据查询把笔记库变成数据库 简介面向 Obsidian 个人知识管理用户的 Dataview 插件离线安装包适合在笔记中实现倒计时、动态表格与任务查询等进阶功能的各类使用者无论是学生、研究人员还是职场人士均可从中受益。压缩包共 4 个文件涵盖核心功能脚本、界面样式表、插件元数据配置以及示例数据文件整体大小约 460KB解压后可直接放入 Obsidian 插件目录使用。已有 863 人学习下载。核心脚本承载了日期计算、任务筛选与表格生成等主要逻辑样式表负责定制颜色、字体与布局元数据配置记录了插件名称、版本及作者信息示例数据则可帮助快速验证查询语法。通过这份资料用户不仅能离线安装 Dataview还能从源码层面理解其运行机制并根据自身工作流调整样式与功能将 Obsidian 打造成真正个性化的知识管理与任务追踪系统。1. Obsidian Dataview 插件到底是什么它把笔记库变成了可以查的数据库如果你用 Obsidian 记了上千条笔记你一定遇到过这个场景想找「上个月读过的所有评分 4 星以上的书」或者「本周还没完成的任务」于是打开搜索框输入关键词出来的结果要么太宽要么太窄最后只能手动翻文件夹。Obsidian Dataview 插件解决的就是这个问题。它允许你在任意笔记里写一段类 SQL 的查询代码把散落在整个库里的笔记元数据标签、属性、日期、任务状态抽出来动态生成列表、表格和任务清单。它不是全文搜索的替代品而是建立在结构化元数据之上的查询引擎。适合所有用 Obsidian 管理任务、读书笔记、项目记录、周期性日记并且已经觉得「信息进得去、出不来」的人。2. 元数据体系Dataview 能查得快靠的是先把数据喂对2.1 两类元数据Frontmatter 和 Inline 字段各自负责什么Dataview 本身不存数据它读取的是笔记里的元数据。常用的元数据载体有两类YAML Frontmatter 和 Inline 字段。Frontmatter 写在笔记最顶部用三条横线包起来Inline 字段写在正文任意位置格式是字段名:: 值。这两者在 Dataview 里都会被识别为笔记的「属性」可以参与查询、排序和过滤。Frontmatter 适合放结构化程度高的信息比如一本书的标题、作者、评分、读状态。Inline 字段则更适合放在正文段落中的局部属性比如在某条项目笔记的特定小节里标记「负责人:: A同学」。下面这段 Frontmatter 是从我实际的读书笔记中摘出来的--- title: 复杂系统思考 author: 某导师 rating: 4 status: 读完 date: 2024-05-12 tags: - 读书笔记 - 方法论 ---这里的rating是数字类型status是字符串date用引号包起来了。这一点很关键后面避坑章节会展开讲。Dataview 读取的时候会把整个笔记理解成一行「记录」文件名是主键字段名是列名字段值是单元格。每一篇笔记有同样结构的元数据时查询就是对这些行做筛选、排序和分组。当你需要给正文中某一段文字加属性又不想翻回文件开头去写 Frontmatter 时用 Inline 字段最顺手。它的写法是在任意位置写字段名:: 值比如这个模块的核心结论是增加缓存层能显著降低延迟状态:: 已验证 相关代码片段放在 [[某跨平台系统]] 的附录里优先级:: 高和 Frontmatter 一样状态和优先级会作为该笔记的字段参与查询。我的习惯是凡是需要跨笔记统计的维度就放进 Frontmatter凡是只在单篇笔记内部标记的属性用 Inline 字段。这两种方式混用时查询结果会以同一个字段名合并除非两者同名同名时 Inline 字段会覆盖 Frontmatter 中的同名值所以命名冲突要尽量避免。2.2 元数据设计先行写笔记之前先定字段否则查询全是空的Dataview 最常见的使用误区是先把笔记写完再回头想查什么结果发现字段没写全。正确的做法是在建笔记模板的时候就把要检索的维度固定下来。我的笔记模板里会放这样一张字段设计表每次新建笔记时照着填字段名类型取值举例用途status字符串未开始 / 进行中 / 已完成任务看板筛选due日期2024-06-30截止日期排序rating数字1 到 5读书笔记评分area字符串后端 / 前端 / 运维按技术领域归档progress数字0 到 100周报自动汇总进度设计字段时有两个原则。第一字段名全库统一不要在这篇笔记里写status、另一篇写state否则查询时会丢数据。第二类型要明确日期必须加引号数字不要加引号字符串加不加引号都行但保持一致最好。Dataview 对类型辨识是严格的数字和字符串混用会导致排序结果不符合预期。做完这一步Dataview 才能发挥真正的作用。它不像搜索那样去全文匹配文字而是沿着元数据这条线把笔记变成一张随时可以执行 SQL 的表。这个设计听起来简单但绝大多数查询翻车根源都在元数据没写好。3. DQL 基础查询从 LIST 和 TABLE 到 WHERE、SORT、GROUP BY3.1 第一个查询用 FROM 限定范围用 LIST 输出列表DQLDataview Query Language的查询语法接近 SQL但去掉了 SELECT它默认输出整个笔记的所有字段。最基础的查询是限定范围加列表输出。新建一篇笔记在代码块里写上dataview LIST FROM #reading WHERE status 读完 SORT rating DESC代码含义拆开说。LIST 是查询类型输出一行一行的笔记链接列表后面跟笔记标题FROM #reading 表示从所有包含 #reading 标签的笔记里查WHERE status 读完 过滤出状态字段等于读完的笔记SORT rating DESC 按评分字段降序排列。这个查询在读书笔记库里跑起来会直接列出一份所有已读的书单评分高的排在上面。 FROM 是 Dataview 最灵活但最容易被忽略的语句它支持三种范围写法。标签写法 FROM #标签文件夹写法 FROM 文件夹路径链接写法 FROM [[笔记名]] 或 FROM outgoing([[笔记名]])。文件夹写法对性能影响很直接如果你知道数据在哪个目录优先用文件夹限定能显著减少扫描范围。我一般这样组合使用 sql dataview LIST FROM 项目/开发 WHERE progress 100 AND progress 0这里 FROM 项目/开发 只扫描 项目/开发 这个目录下的笔记和全文检索不同它不会去碰其他位置的文件。AND 是逻辑与两个条件同时成立才输出。要注意 progress 字段是数字类型所以可以直接做大小比较不要写成 progress 50字符串和数字比较会返回空结果这是新手最容易踩的隐性问题。 ### 3.2 条件过滤与排序contains、日期比较、用 TABLE 输出表格 WHERE 不只是等值比较它支持包含判断、日期运算和布尔逻辑。contains 是高频函数它判断某个字段里是否包含指定值适合处理多值字段或者子串匹配。例如 sql dataview TABLE title, author, rating, status FROM 读书笔记 WHERE contains(tags, 方法论) OR contains(tags, 系统设计) SORT rating DESC, date ASCcontains(tags, 方法论) 检查该笔记的 tags 字段这个字段是数组所以 contains 在这里判断数组里是否含有元素。注意它和字符串模糊搜索不一样contains 是严格包含关系。SORT 后面可以接多个字段用英文逗号分隔先是评分降序评分相同则按日期升序这个排序优先级在列表数据量大了之后非常有用。 TABLE 查询类型和 LIST 的差别在于TABLE 会生成真正的表格指定列名输出适合做看板。它还可以用 WITHOUT ID 把第一列的文件名隐藏掉只显示你关注的字段。日期比较用 date() 函数或直接写字段名。比如查最近 7 天创建的笔记 sql dataview TABLE WITHOUT ID file.name AS 笔记名, dateformat(file.ctime, yyyy-MM-dd HH:mm) AS 创建时间 FROM 日记 WHERE file.ctime date(today) - dur(7 days) SORT file.ctime DESC这里两个值得说的点。file.ctime 是 Dataview 自动给每篇笔记注入的元数据代表文件创建时间date(today) 取今天零点dur(7 days) 是 7 天持续时间对象两者相减得到 7 天前的时间戳。AS 是重命名列标题英文不能省略。dateformat 函数用来格式化时间输出写法和 Java 的日期格式一致。这段代码查的是日记目录下最近 7 天新建的笔记可以作为每周回顾的起点查询。 ### 3.3 分组与聚合GROUP BY 把行折叠成组组内数据可展开统计 GROUP BY 是 DQL 里功能最重的语句之一。和 SQL 类似它把记录按照某个字段的值分组每一组只输出一行摘要同时每组内的所有记录可以通过 rows 变量来引用。这个特性在做分类统计时非常好用。例如按状态分组统计笔记数量 sql dataview TABLE length(rows) AS 数量, filter(rows, (r) r.progress 100).length AS 已完成数 FROM 项目 GROUP BY status这段代码的输出是按 status 分组的行第一列是状态值未开始 / 进行中 / 已完成第二列是当前组的笔记数量第三列是状态为进行中但 progress 字段大于等于 100 的数量。filter 函数类似 JavaScript 的 Array.prototype.filter它接收两个参数数组和箭头函数返回满足条件的元素组成的新数组再取 .length 得到数量。 GROUP BY 有一个隐式行为当你分组后输出行的字段访问方式会变化。在未分组的查询里rating 直接访问当前笔记的字段分组之后原始笔记被折叠进 rows 数组直接写 rating 会报错或者返回空结果必须写 rows.rating 才能访问组内第一条记录的字段。这个差异很隐蔽我在刚接触 Dataview 时经常在这里栽跟头。建议的做法是分组查询里优先用 length(rows) 统计数量用 rows.field 取组内值不要试图直接引用单个字段。 到这一步你已经可以用 Dataview 做基本的信息聚合了。但实际工作中最有价值的是任务管理和周期性汇总这些需要借助 TASK 和 FLATTEN。 ## 4. 进阶场景任务聚合、周报生成和读书看板的真实写法 ### 4.1 TASK 查询跨笔记聚合未完成任务组内自动按文件分组 Obsidian 原生支持在笔记里写 - [ ] 待办事项 复选框。Dataview 的 TASK 查询类型可以扫描任意范围内的任务并跨笔记汇总。这是我用得最多的功能之一比如把某个项目文件夹下所有未完成任务拉出来 sql dataview TASK FROM 项目/某跨平台系统 WHERE !completed GROUP BY file.linkTASK 会遍历 FROM 范围内的所有任务输出任务文本和所在笔记链接。WHERE !completed 过滤掉已完成任务completed 是任务自动携带的布尔属性。GROUP BY file.link 是 TASK 查询的常见搭配它会让输出按文件分组而不是平铺地列出所有任务。没有这行分组一百个任务会压成一大片没法阅读。 如果你希望任务带截止日期显示可以借助 Inline 字段。比如任务行里写 - [ ] 修复登录模块 bug 截止:: 2024-06-15然后查询时按日期过滤 sql dataview TASK FROM 项目/某跨平台系统 WHERE !completed AND 截止 date(today) SORT 截止 ASC这里 截止 是任务上的 Inline 字段每个任务都可以拥有独立的属性。date(today) 取今天零点所以 截止 date(today) 的含义是已经过了截止日期还没完成的任务也就是过期任务。这个查询可以挂在每日笔记顶部每次打开就知道哪些事情已经超时。注意 Inline 字段写在任务文本里必须是 字段名:: 值 的格式不能写在任务后面用普通冒号否则 Dataview 不会识别。 ### 4.2 周报自动聚合用 FLATTEN 展开任务数组按日记日期筛选 周报是我认为 Dataview 最值回票价的场景。如果你每天都写日记并且日记里的任务是复选框那么周报内容其实不需要手动汇总直接查询即可。关键函数是 FLATTEN它把一个笔记下的任务数组展开成独立的行每行代表一个任务同时保留笔记本身的属性。 sql dataview TABLE WITHOUT ID file.day AS 日期, task.text AS 任务内容, task.completed AS 是否完成 FROM 日记 FLATTEN file.tasks AS task WHERE file.day date(today) - dur(14 days) AND file.day date(today) SORT file.day DESC这段查询的逻辑是先扫描 日记 文件夹下的所有笔记file.tasks 是每篇日记自动携带的任务数组FLATTEN file.tasks AS task 把这个数组里的每个任务变成一行。WHERE file.day date(today) - dur(14 days) 限定了最近 14 天AND file.day date(today) 排除了今天尚未结束的日记。输出是一个二维表每行是一个任务带日期和完成状态。 file.day 是 Dataview 根据文件名自动推断的日期属性前提是文件名符合 2024-06-01 这类格式。如果你的日记文件命名不规范file.day 会是空值WHERE 条件就失效了。另一个细节是 task.text 返回的是去掉复选框标记后的纯文本不包含 - [ ] 这几个字符所以拼接周报内容时不用再清洗。 如果你希望周报直接生成一个可复制的 Markdown 列表而不是表格可以把 TABLE 换成 LIST去掉 WITHOUT ID输出效果就是一段可以粘贴到周报文档里的任务列表。这个场景下 FLATTEN 是核心它让 Dataview 能从「篇」往下钻到「任务」。 ### 4.3 读书笔记看板把评分、日期和状态渲染成一张带文本状态的表 信息聚合的终极形态是看板。用 TABLE 直接输出你关注的字段加 choice 函数把布尔值或状态值渲染成可读文本最后按优先级排序。比如我维护一个读书笔记看板挂在主页 sql dataview TABLE WITHOUT ID title AS 书名, author AS 作者, choice(status 在读, ●, status 读完, ○, ○) AS 状态, rating AS 评分, dateformat(date, yyyy-MM) AS 读完月份 FROM 读书笔记 WHERE contains(tags, 读书笔记) SORT choice(status 在读, 0, status 读完, 1, 2) ASC, rating DESCchoice 是 Dataview 内置的三目运算函数接收三个参数条件、为真时的值、为假时的值。嵌套 choice 可以实现多条件分支这里把状态映射为「●」和「○」的显示文本。SORT 里也用了 choice目的是自定义排序优先级在读排在前面读完排在后面未开始排最后同状态内再按评分降序。 用这个看板我的主页就是一张读书进度仪表盘。要读什么、在读什么、读完什么一眼扫过去就有数。同样的模式可以套用到任何带状态和日期的笔记集合上。结合前面讲到的 GROUP BY甚至可以再做一层按月份的聚合统计比如产出一张「每月读完书目」的报表。这些查询片段的组合能力很强但它们都建立在元数据规范的基础上所以模板设计这一步永远值得花时间。 ## 5. 避坑指南五个把 Dataview 结果查错或查慢的经典原因 ### 5.1 日期字段写裸值查询结果全变成年份差值 现象我在读书笔记里写了 date: 2024-05-12用 WHERE date date(2024-01-01) 筛选结果只有 2022、2023 之类乱七八糟的数字或者直接报错。 原因YAML 里裸写 2024-05-12 会被解析成减法表达式结果是 2024 减去 5 减去 12等于 2007而且这个值会被当成数字类型存储和日期类型比较时必然对不上。 解决日期字段在 YAML 里加引号写成 date: 2024-05-12。或者用 date: 2024-05-12 这种写法在部分版本里可能能被识别但为了保险统一加引号。如果已经写坏了批量替换时搜 date: 2024 改为 date: 2024逐个补后半边引号。 ### 5.2 布尔字段用 is true 判断结果为空 现象Frontmatter 里写了 done: true查询写 WHERE done is true结果一条都查不出来。 原因is 是类型判断操作符不是等值判断。is true 的含义是「是否为布尔类型」而不是「是否为真」。done 字段确实是布尔类型但 is true 这个写法在 DQL 里并不会做值比较。 解决改写成 WHERE done true 或者 WHERE done。同理is null 是判断字段是否存在而 null 是错误写法查询空字段要写 WHERE field null 或者 WHERE !field具体看你是要筛类型还是筛值。 ### 5.3 标签嵌套和大小写导致 FROM 扫错范围 现象笔记打了 #项目/开发 标签查询写 FROM #项目结果输出为空。或者打了 #todo 标签查询写 FROM #Todo结果也查不到。 原因FROM #项目 只匹配 项目 这个原子标签不包含子标签 项目/开发。而标签大小写是敏感的#todo 和 #Todo 被当作两个完全不同的标签。 解决查询时要么展开写子标签 FROM #项目/开发要么用 WHERE contains(file.etags, #项目) 做包含匹配。file.etags 是 Dataview 自动收集的全部标签数组contains 做数组元素匹配时不受嵌套层次限制。日常维护中最好统一标签命名规范全部小写加连字符从源头规避。 ### 5.4 字段名随手写同义不同名导致统计缺行 现象做了个项目进度看板查询 WHERE status 已完成结果只显示一部分笔记翻原始笔记发现有的写的 状态:: 已完成有的写 status: 已完成还有一篇写 Status: 已完成。 原因字段名大小写不一致和中英文混用对 Dataview 来说是三个不同字段。查询是精确匹配字段名的拼写不一致意味着部分记录根本不进入结果集。 解决没有银弹只能靠模板治本。在 T emplates 里把字段模板固定好新建笔记时复制粘贴避免手敲。同时可以写一个辅助查询列出全库所有字段名及出现次数定期检查异常字段 sql dataview TABLE length(rows) AS 出现次数 FLATTEN file.etags AS unused GROUP BY file.fields这个查询实际做的是基于笔记字段名的分组统计file.fields 是自动属性数组展开后按字段名分组就能看出哪些字段名被混用了。 ### 5.5 大库查询卡顿首屏加载要等十几秒 现象笔记库文件数到了一万篇左右主页挂了 6 个 Dataview 查询每次打开 Obsidian 都要转圈很久。 原因Dataview 在启动时会全库扫描并建立缓存索引视图数量越多计算量越大。尤其是 FROM 全库范围配合 FLATTEN file.tasks 这类展开操作会把所有任务都拉进内存。 解决三个手段同时用。第一收窄 FROM 范围尽可能用文件夹替代标签第二减少主页视图数量把不常用的查询移到单独的笔记里需要时再打开第三给查询加时效截止例如 WHERE file.ctime date(today) - dur(30 days)让旧数据不参与计算。Obsidian 的索引机制会自动更新但 Dataview 是实时计算的无法预计算缓存所以范围控制是唯一的优化方向。 ## 6. 复杂表格怎么提速以及一个可直接套用的主页看板模板 Dataview 性能瓶颈不在查询语句本身而在数据范围。最有效的提速手段是把可能命中一万条记录的查询改写成只扫描一个子目录的查询。比如把一个全库任务查询拆成按项目名命名的独立文件夹每个项目一个查询单独挂载。我测过一个两万篇笔记的库全库 FLATTEN 查询耗时约 8 秒收窄到 FROM 项目/当前 后降到 1 秒以内。这个差距是可感知的。 另一个习惯是把多次查询合并成一次。例如主页同时要显示「未完成任务」「最近读书」「本周新增笔记」如果写三个 TABLE 查询页面上会有三张表每次刷新计算三次。可以改成一个查询利用 choice 和分组状态字段把不同维度的数据统一标注后输出到一张表 sql dataview TABLE WITHOUT ID file.link AS 名称, choice(contains(file.etags, #任务), 任务, contains(file.etags, #读书笔记), 读书, 其他) AS 类型, status AS 状态, dateformat(file.ctime, MM-dd) AS 创建日期, progress AS 进度 FROM 项目 OR 读书笔记 OR 日记 WHERE file.ctime date(today) - dur(30 days) SORT progress ASC这个查询同时扫了三个目录用 choice 根据标签决定显示类型输出一张混合看板。FROM 支持多个范围用 OR 连接注意这里 OR 作用于范围匹配和 WHERE 里的逻辑或不同。运行效果是主页上只有一张表滚动一次就能看完所有近期活跃项。 如果你希望看板更接近个人仪表盘可以再加一列状态映射把「未开始」「进行中」「已完成」转换为进度条文本或者用 join 函数把标签数组拼成一行字符串。这些都不是复杂写法但组合起来就是一套很顺手的信息聚合面板。 最后分享一个我自己的教训。早期用 Dataview 时我不重视元数据设计想到什么字段就写什么结果一个月后查询结果频繁缺行排查了整整一个下午才发现是 status 和 状态 两个字段混用了。从那以后我给自己定了一条强制规则新建任何笔记类型之前先在模板里把字段清单定下来至少包含 status、date、tags 三个核心字段其他字段按场景扩展。这条规则听起来很基础但它保证了所有 Dataview 查询都有稳定的数据结构可以依赖。希望这些踩坑经验对你有帮助。 p a hrefhttps://download.csdn.net/download/weixin_40592845/89336114 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p