SQL语句生成器怎么用?安全地批量改一批文章的SEO字段
本文目录
摘要:这个SQL语句生成器,干的是把你在表单里点选、填写的条件,拼接成一条SQL文本给你——它只生成、不执行,吐出来的语句要你自己复制到数据库里跑,工具本身从不碰你的库。它真能生成的是SELECT、INSERT、UPDATE、DELETE、CREATE TABLE五类基本语句,外加一个挺实用的模拟数据生成器:粘一段建表语句或JSON结构进去,它按字段名猜类型,给你批量造出一堆测试数据,能导成INSERT、JSON或CSV。它号称支持五种数据库,但所谓方言区别只停在引号符号和分页写法这一层,造数据的逻辑各库完全一样,是个语法级而非真正的方言适配。最该死死记住的一条:它对你填进WHERE和SET里的内容不做任何转义,直接拼进SQL——这意味着生成的语句天生带注入风险,你要是不逐行看一遍就拿去生产库执行,一条写歪的DELETE或者一个被特殊字符撑破的条件,足以酿成数据灾难。它适合的是快速搭简单的增删改查、造测试数据、学SQL语法;多表JOIN、子查询、窗口函数这些复杂活它一概不会,得自己手写。把它当草稿笔,写完必先备份、必在测试库验过再上生产,它是个省事的帮手;把它当能直接产出安全SQL的黑盒,迟早出事。
做CMS的SEO维护,绕不开数据库这一关。一两千篇文章的slug要批量规范、meta description要成批补齐、积压的重复数据要清理、新模块上线要先灌一批测试数据——这些活,靠后台一条条点鼠标能把人点到崩溃,写SQL批量处理才是正道。可SQL这东西,语法琐碎、标点严格、还分数据库方言,手生的人写起来容易卡壳。
这款SQL语句生成器就是冲着这个痛点来的:你在表单里点选操作类型、填上表名和条件,它替你把SQL拼好。保哥团队在帮客户做批量数据维护时常拿它打草稿。但用它有个大前提必须先说在前头——它生成的SQL绝不能闭着眼睛拿去生产库跑。这篇我们团队就把它到底能干什么、那几处方言和功能上的虚标、最要命的注入边界、以及怎么安全地用它做CMS批量维护,一次讲清楚。
这个SQL生成器,到底是生成还是执行?
先把最关键的一条定性钉死:它只生成SQL文本,不连接任何数据库执行。你点完生成,它给你的是一段可以复制的SQL字符串,至于这段SQL拿去哪个库、跑出什么结果、有没有把数据改坏,工具一概不管也管不着。这条边界看似简单,却是安全使用它的全部前提——它是张草稿纸,不是执行器。
从实现上看,它是后端PHP生成加前端表单交互的结构。前端把你点选的参数打包成JSON发给后端,后端按三种动作处理:一种是解析,把你粘进去的建表语句或JSON结构拆成字段定义;一种是生成,按解析出的结构造模拟数据;还有一种是构建,把表名、条件这些拼成最终的SQL语句。三种动作处理完,结果回传到前端展示,前端还做了关键字、字符串、数字的着色,看起来清爽。
这里要澄清一个常见误解:它不是在帮你解析或优化SQL,它就是个模板拼接器。你给的参数往模板里一套,SQL就出来了,中间没有任何对SQL语义的理解,也不会校验你拼出来的语句合不合法、合不合理。所以它能拼出语法没错但逻辑致命的语句——比如一条没带条件的DELETE,它照样老老实实给你生成,至于跑下去会清空整张表,它不会拦你。
想清楚这一点,用它的心态就摆正了:把它当成帮你快速起草SQL的助手,省去手敲关键字和标点的工夫;但起草完的稿子,质量得你自己把关。它生成的每一条上生产库的SQL,都该被当成你亲手写的来审查,而不是因为工具吐的就默认它对、它安全。
它真正能生成哪些SQL语句?
盘一盘它的真本事。可视化构建这块,它支持五类基本语句,覆盖了日常增删改查的主干。
SELECT查询是用得最多的,支持WHERE条件、GROUP BY分组、ORDER BY排序、LIMIT限制行数,基本的查询四件套都在。INSERT插入支持指定字段列表和对应的值。UPDATE更新支持SET赋值加WHERE条件,这是批量改字段的主力。DELETE删除支持WHERE条件——这条最危险,后面专门说。CREATE TABLE建表支持字段定义,MySQL下还会带上引擎声明。
除了这五类语句,它还有个挺好用的模拟数据生成器,是另一块独立功能。你把一段建表语句或者一段JSON结构粘进去,它解析出字段,按你设的行数批量造数据,能输出成一堆INSERT语句、一段JSON、或者一个CSV表格。给测试库快速灌数据、或者给前端造点假数据联调,这功能省事。
但也得知道它生成不了什么。多表JOIN关联查询,没有;嵌套的子查询,没有;CTE公共表表达式和窗口函数这些进阶语法,没有;UNION合并、事务控制的BEGIN和COMMIT,都没有;ALTER改表结构、DROP删表,界面里也找不到。它的地盘就是单表的简单增删改查加造数据,复杂的关联和分析查询,超出它的能力,老老实实手写。
还有个细节值得点出来:它的SELECT表单里,聚合函数没有可视化的构建入口。你想查个COUNT或者SUM,只能在字段那栏自己手敲COUNT(*)这样的字符串,工具原样输出,既不验证也不智能提示。说白了,越往复杂走,你越得自己懂SQL,工具帮的忙越少。
号称支持五种数据库,方言区别到底在哪?
它的数据库下拉给了五个:MySQL、PostgreSQL、SQL Server、Oracle、SQLite。看着挺唬人,但得搞清这个方言支持到底深到哪一层,免得被下拉框误导。
真有区别的地方有这么几处。一是标识符引号:MySQL用反引号,SQL Server用方括号,其余几家用双引号,这个它确实按库切换。二是分页语法:SQL Server用TOP,Oracle用FETCH FIRST,PostgreSQL和SQLite用LIMIT,这块也按库给了不同写法。三是建表引擎:只有MySQL会补上ENGINE=InnoDB,其他库输出的结构一样。
但虚标也在这儿。它的模拟数据生成逻辑,在所有数据库下完全一致——同样一个小数字段,不管你选MySQL还是Oracle,造出来的值都一个样,并没有按各库的数据类型特性做真正的区分。换句话说,它的方言适配只到语法外壳这一层,引号换一换、分页关键字换一换,到了真正生成数据、处理类型的内核,五个库走的是同一套代码。
所以别指望它能帮你处理真正棘手的跨库差异。各家数据库在数据类型、函数名、日期处理、自增机制上的深层区别,它一概没碰。你拿它给MySQL生成的语句,换到PostgreSQL多半还得自己改。把它的方言支持理解成换个引号风格,期望值就对了;当成能产出地道方言SQL的转换器,会失望。
这倒不全是缺点。对大多数CMS维护场景来说,你的库就是固定的那一个——WordPress和Typecho都是MySQL,你压根用不上跨库。只要选对MySQL,引号和分页都对,它生成的语句直接能用。方言这块的虚标,对单库用户其实没太大杀伤,知道有这回事就行。
为什么它生成的SQL不能直接拿去生产库跑?
这是全篇最该警惕的一节。它生成的SQL天生带着注入风险,根源在于:它对你填进WHERE、SET、ORDER BY这些地方的内容,不做任何转义,原样直接拼进SQL字符串。
OWASP的SQL注入攻击说明里讲得很透:当用户输入未经处理就拼进SQL语句,攻击者就能用精心构造的输入改写整条语句的逻辑。这款工具的构建逻辑正是直接拼接——你在WHERE里填什么,它就往SQL里塞什么。要是这内容来自不可信的地方、或者你自己手滑填了带引号分号的东西,拼出来的SQL就可能是一条逻辑全变了样的危险语句。
它的模拟数据生成那块稍微好点,造数据时用了addslashes给值做转义,但这远不是真正的防护。addslashes只是简单地给引号前面加反斜杠,在某些字符集下还有被宽字节绕过的老问题,跟参数化查询那种从机制上隔离数据和指令的做法不是一个量级。它能挡住最粗浅的情况,挡不住有心的构造。
真正安全的做法,PHP官方的mysqli预处理语句文档讲得很清楚:用带占位符的预处理语句,把数据和SQL指令彻底分开,数据库引擎把绑定的值永远当数据处理,绝不会当成SQL指令执行。这才是从根上防注入的正解。而这款工具不提供任何参数化选项,它产出的就是把值硬拼进去的拼接式SQL。
所以结论很硬:它生成的SQL,尤其是带条件的UPDATE和DELETE,绝不能不审查就拿去生产库跑。正确姿势是——执行前先用mysqldump之类把库备份了,把生成的SQL拿到测试库或者本地副本上跑一遍验证无误,确认WHERE圈定的范围正是你想动的那批行,再上生产。真要在程序里反复执行,别用它拼好的语句,照预处理语句的路子自己用占位符重写一遍。
怎么用它批量改一批文章的SEO字段?
讲个最实用的场景:拿它生成UPDATE语句,批量规范一批文章的slug或者补齐meta description。这是CMS的SEO维护里的高频活,也是它最能帮上忙的地方。下面是保哥团队常走的安全步骤。
- 先想清楚你要改哪张表的哪个字段、圈定哪批行。比如WordPress要批量改文章的slug,就是改
wp_posts表的post_name字段,条件圈定post_type是post的那些行。把表名、字段、条件先在纸上理清楚。 - 在工具里选UPDATE,数据库选MySQL,填上表名,SET那栏填要改的字段和新值,WHERE那栏填圈定行的条件。这里务必把WHERE写够精确,宁可圈小一点分批改,也别图省事写个宽泛条件把不该动的行也卷进去。
- 点生成,把吐出来的SQL逐行读一遍。重点盯WHERE条件对不对、SET的值有没有被引号或特殊字符撑破、整条语句有没有意外的分号截断。这一步绝不能省,工具不替你把关,你自己就是最后一道闸。
- 先备份。用mysqldump把要动的表导出存好,或者至少把这批行的原值先
SELECT出来留底。备份是后悔药,没有它,UPDATE改错了就只能干瞪眼。 - 拿到测试库或本地副本上先跑一遍,
SELECT出来核对改的结果是不是预期的那样,受影响的行数对不对。确认无误,再上生产库执行,执行后再查一遍确认生效。
这套步骤的核心就一个字:稳。UPDATE语句里SET和WHERE的写法、多字段一起改的语法,MySQL官方的UPDATE语句参考手册讲得最权威,拿不准时对着它核一遍准没错。
工具帮你省了手敲SQL的工夫,但备份、审查、测试库验证这三道关,一道都不能少。批量UPDATE的威力越大,写歪了的破坏也越大,多花十分钟走完这套流程,比改错了花一整天去恢复划算得多。顺带一提,文章表里的created、modified这类时间戳字段如果也要一起处理,可以配合时间戳转换工具把日期算成数据库存的Unix秒数,再填进SQL。
模拟数据生成功能怎么用、靠不靠谱?
它的模拟数据生成器是个被低估的实用功能,单拎出来说说。流程是:把一段建表语句或者一段JSON结构粘进去,点解析让它认出字段,设好要造多少行、起始ID、输出什么格式,点生成就得到一批数据。
它造数据的聪明之处在于会按字段名猜类型。字段名里带email的,给你造邮箱样子的值;带name的造名字;带price的造数字;带date的造日期。这种按名猜意的做法,造出来的数据比纯随机字符串像样多了,灌进测试库做联调或者性能测试,看着也舒服。
但它的靠谱程度有几条边界得知道。一是它只认英文字段名,你的字段要是用中文命名,它猜不出类型,只能给通用值。二是数据量一大就重复,比如邮箱域名它就那么几种,造个上千行会有大量重样的值,做唯一性相关的测试得留神。三是日期范围是写死的,只在固定的几年区间里造,要特定时间段的数据它给不了。四是行数有上限,超过一千行会被悄悄截断,不报警。
输出格式给了三种,各有用处。INSERT语句直接拿去灌库;JSON适合丢给前端做假数据或者导进别的工具;CSV适合用表格软件打开看或者做简单分析。要是你造的是JSON输出、字段又多、想看清嵌套结构,可以把它拷进JSON格式化工具里展开细看。
总的说,这功能适合快速造一批形似的测试数据应急用,省去你手敲一堆假数据的工夫。但它造的数据只是形似,不保证业务逻辑上的合理和一致,正经的测试数据集还是得自己设计。当个快速填充的草稿工具用,它够格;当生产级的数据工厂使,它不够。
它做不了的复杂活,有哪些得自己手写?
把它的天花板再明确一下,省得你拿它去撞南墙。有几类活它结构上就不支持,硬要用它只会浪费时间。
多表关联是头一个。比如你要一边更新文章表、一边联动更新文章的元数据表,这需要JOIN或者子查询,工具的构建逻辑里压根没有JOIN这回事,这类语句只能手写。同理,按某个关联表的条件来删主表数据、或者用子查询算出一个值再拿去更新,这些带嵌套的活它都干不了。
带函数的批量处理也得自己来。比如你想把一批URL里的某段字符统一替换,这要用到数据库的字符串函数,工具不支持在SET里调函数做这种变换,它只能填死值。这种场景,配合正则测试工具先把替换规则在外面验明白,再手写带函数的SQL,比硬抠工具靠谱。
还有个隐蔽的虚标得提:它的SELECT表单虽然在文档里暗示支持HAVING分组过滤,但前端实际上根本没给HAVING的输入框,参数收集了也永远是空的,等于这功能名存实亡。指望它生成带HAVING的聚合查询,会扑空。
视图、存储过程、触发器这些数据库对象,它完全没有对应模块,想都别想。再加上前面说的CTE、窗口函数、UNION,凡是超出单表简单增删改查的,基本都在它的能力圈外。认清这条线,把它用在它擅长的简单批量活上,复杂的交给手写或者专业的数据库客户端,分工才合理。
把它接进CMS数据库维护工作流
讲个完整的实战。保哥团队带过一个做跨境儿童教育玩具的独立站客户,站点积累了上千个产品页和文章页,早期建站不规范,留了一堆数据问题要清理。这工具在几个环节帮了忙。
第一件是批量规范slug。这站早年的文章slug是一串数字ID,对SEO很不友好。我们团队按前面那套步骤,用工具生成了一批UPDATE语句把slug改成有意义的英文短语,每批圈定几十行,备份、测试库验过、再上生产,分批推进,没出岔子。
第二件是清理重复的meta description。导数据时出过岔子,有一批页面的描述被填成了同一段模板文字,谷歌那边重复内容警告都冒出来了。我们团队先用工具生成SELECT语句把这批重复描述的页面捞出来核对,确认范围,再生成对应的UPDATE分批改掉,把重复问题清干净。
第三件是给新上的测试环境灌数据。客户要搭一个测试站验证新模板,空库不好测。我们团队用模拟数据生成器,粘了产品表的建表语句进去,造了几百行形似的产品数据导进测试库,模板的列表页、详情页很快就能看效果,省去了手工录入的麻烦。
这几件活的共性是:工具负责帮你快速起草SQL和造数据,但每一步上生产的操作,备份和测试库验证这两关我们团队从不跳过。它是个提效的帮手,把你从手敲SQL的琐碎里解放出来,但数据安全这根弦,握在你自己手里。想把接口调试和数据库操作这套组合拳打全,可以接着看配套的API测试工具那篇,两篇凑成开发者手里接口与数据的趁手二件套。
拿它生成SELECT导出数据做SEO分析,怎么用?
批量改字段之外,它生成SELECT语句的能力也常被用来导数据做SEO分析。这是个低风险又实用的用法,因为SELECT只读不写,不会改坏任何东西。
典型场景是把文章的标题、slug、描述、发布时间这些字段一次性捞出来,导成CSV丢进表格软件里分析。比如你想盘一盘全站标题的长度分布、有没有标题过长被搜索引擎截断的、有没有一批标题撞车重复的,先用工具生成一条带上你要的字段、加上排序和行数限制的SELECT,跑出来导出,再到表格里筛和算,比在后台一页页翻高效得多。
它支持的WHERE、ORDER BY、LIMIT在这种导数据场景下够用。WHERE帮你圈定范围,比如只看某个分类、某段时间的文章;ORDER BY帮你排序,比如按字数降序先看最长的;LIMIT帮你控制导出量,先导一小批看看结构对不对,再放开导全量。这套组合应付单表的数据导出绰绰有余。
但它的短板在这种场景下也很明显:不支持JOIN和子查询,意味着凡是要关联多张表才能算出来的指标,它都给不了。比如你想统计每篇文章关联了多少个标签、或者按某个元数据字段的值来筛文章,这些都涉及关联查询,工具拼不出来,得自己手写。它能干的是单表字段的直接导出,复杂的关联分析超出它的范围。
导出来的数据想做进一步处理也有讲究。如果你导的是CSV,表格软件直接打开就能用;如果导的是JSON、结构又比较深,建议拿专门的格式化工具展开看清层级再处理。另外,导出的数据里如果有需要批量做模式匹配、提取的部分,先把匹配规则在外面验明白再动手,处理起来更稳。
INSERT和CREATE TABLE这两类语句,实际怎么用上?
除了改和查,它的INSERT和CREATE TABLE这两类语句也各有用武之地,搭测试环境时尤其用得上。
INSERT插入语句最常配合模拟数据生成器用。你想给一张表灌测试数据,先把建表语句粘进生成器解析出字段,设好行数,让它批量造出一堆INSERT语句,复制到测试库一跑,几百行数据就进去了。手动构建单条INSERT的场景也有,比如往配置表里补一条记录,工具帮你把字段列表和值对齐拼好,省得自己数逗号对位置。
这里有个值得留意的细节:工具在SQL构建器里拼INSERT时,不会智能判断字段类型。数字字段你得填裸数字,字符串字段你得自己带上引号,工具不替你判断该不该加引号。要是漏了引号,字符串值会被当成别的东西导致语法错,这又回到那条老规矩——生成完必须逐行看一眼。相比之下,模拟数据生成器那条路因为做了基本转义,相对省心些。
CREATE TABLE建表语句适合快速起一张新表的骨架。你在表单里把字段名、类型定义好,工具生成建表语句,MySQL下还会自动补上引擎声明。新模块要加张表、或者搭测试环境要复刻一张表结构,拿它打个草稿挺快。
但建表这块它做得比较基础,得知道边界。外键约束、唯一索引、普通索引、字段注释这些,工具的建表生成都不管,它给你的就是字段加类型加引擎这么个光骨架。真正上生产的表,索引和约束往往是性能和数据完整性的关键,这些得你在工具给的骨架基础上自己补全。把它当建表的起手草稿用合适,当成能直接产出生产级表结构的工具就过头了。
说到底,INSERT和CREATE TABLE这两类语句,工具的定位还是草稿和提效。它帮你把语法框架快速搭起来,省去敲关键字和对齐的琐碎,但语句里真正关乎安全、性能、完整性的那些讲究,仍然需要你自己懂、自己补。
为什么备份和测试库验证这两关一道都不能少?
前面反复强调备份和测试库验证,这里专门把为什么讲透,因为这是用这类工具最该刻进肌肉记忆的安全意识。
先说备份。数据库操作和改文档不一样,文档改错了有撤销,数据库里一条UPDATE或DELETE跑下去,旧数据当场就没了,没有原生的后悔药。你以为WHERE圈定的是十行,结果条件写宽了圈进去一千行,等你发现时数据已经改完了。备份就是你唯一的后路——执行前用mysqldump把表导出存好,万一改错,还能从备份里捞回来。这一步花不了几分钟,省的却可能是一整天的恢复工夫,甚至是无法挽回的数据丢失。
再说测试库验证。工具生成的SQL是没经过任何校验的拼接结果,它可能语法对但逻辑错、可能被你填的特殊字符撑破、可能WHERE的范围跟你想的不一样。这些问题在生产库上跑出来就是事故,但在测试库上跑出来只是一次无害的演练。先在测试库或本地副本上跑一遍,SELECT核对结果、看受影响的行数对不对,确认无误再上生产,等于给危险操作加了一道保险。
这两关尤其在批量操作时不能省。单条手动操作错了影响有限,但批量UPDATE和DELETE的威力是成百上千行起步的,写歪一个条件,破坏就是成规模的。工具让批量操作变得很容易,这是它的价值,但容易也意味着犯错的代价被同步放大了。越是省事的工具,越要配上严谨的流程来兜底。
还有个进阶的稳妥做法:在生产库执行批量改动时,用事务把操作包起来。先开事务、执行、SELECT核对结果,确认对了再提交,发现不对就回滚,相当于在生产库上也留了一道撤销的余地。当然,这需要你的库和操作方式支持事务,但对重要的批量改动,多这一层保护很值得。把备份、测试库验证、事务这三样配齐,工具生成的SQL才能放心地用在要紧的地方。
新手拿它入门SQL,要注意避开哪些坑?
这工具还有个常被提到的用法:新手拿它学SQL语法。它把抽象的SQL拆成了表单里的点选项,你填什么、它对应生成什么,看着生成结果反推语法结构,确实是个直观的入门路子。但拿它学,有几个坑得提前知道,免得学歪了。
头一个坑是别把它当语法权威。它是个模板拼接器,不校验语句对不对,你在字段栏里填了不规范甚至错误的东西,它照样给你拼进去。新手要是把工具吐出来的每条语句都默认成正确范本,可能学到一些似是而非的写法。学的时候,工具生成的语句最好对照官方文档核一核,别全信工具。
第二个坑是它只覆盖最基础的部分。SQL的精华很多在JOIN关联、子查询、聚合分析这些工具不支持的地方。你光靠工具,学到的只是单表增删改查的皮毛,真正有用的复杂查询它教不了你。把它当入门的第一级台阶可以,但别指望它带你走完全程,进阶还得啃文档、动手写。
第三个坑也是最重要的——它会让新手对SQL的危险性缺乏敬畏。点几下就生成一条DELETE,看着轻松,但新手意识不到这条语句在真库上跑下去的破坏力。学SQL最该先学的不是语法,是对数据操作的敬畏:任何改动前先想清楚影响范围、先备份、先在测试环境验。这条工具教不了,得你自己刻进意识里。
用对了,它是个不错的语法直观化辅助。建议的学法是:拿它生成一条语句,先别急着用,自己把这条语句的每个部分讲一遍是什么意思,再对照官方文档确认,最后在测试库里跑跑看实际效果。这样工具就成了你的练习陪练,而不是替你思考的拐杖。把它当陪练,你学得扎实;把它当答案,你学得虚浮。
说到底,工具能降低SQL的上手门槛,但降不了真正掌握它需要的功夫。语法可以靠工具直观感知,但对数据的敬畏、对复杂查询的理解、对各种坑的警觉,这些只能靠自己动手、踩坑、复盘一点点攒出来。把工具用在它能帮上忙的入门阶段,剩下的路还得自己走。
常见问题解答
把大家最常问的几个问题集中答一下,都是拿它做数据库维护时真会撞上的。
问:这个工具会直接连我的数据库执行SQL吗?不会。它只生成SQL文本,吐给你一段可以复制的语句,至于这段语句拿去哪个库、跑出什么结果,全靠你自己手动复制到数据库里执行,工具本身从不连接也不碰你的库。这也意味着它造不成直接破坏,真正的风险在你把它生成的SQL拿去执行那一步。
问:它生成的SQL能直接在生产库跑吗?强烈不建议。它对WHERE、SET这些地方的输入不做任何转义,直接拼进SQL,天生带注入风险,也不校验语句合不合理。正确做法是先备份、把语句拿到测试库验证、确认WHERE圈定的范围没错,再上生产。带条件的UPDATE和DELETE尤其要逐行审查。
问:它说支持五种数据库,是真的能生成五种方言吗?只能算半真。它的方言区别只停在引号符号、分页关键字、建表引擎这些语法外壳上,真正造数据、处理类型的内核五个库走同一套代码。各家在数据类型、函数、日期上的深层差异它没碰。好在CMS维护通常就用MySQL一个库,选对了就够用。
问:模拟数据生成器造的数据能用于正式测试吗?能应急但别太指望。它按英文字段名猜类型造形似的数据,量大了会重复,日期范围写死,超过一千行还会被截断。当个快速填充测试库的草稿工具用够格,但数据只是形似、不保证业务逻辑上的合理,正经的测试数据集还得自己设计。
问:我要做多表关联的批量更新,它能生成吗?不能。它的构建逻辑只支持单表的简单增删改查,JOIN、子查询、CTE、窗口函数这些一概不支持,HAVING更是连输入框都没有名存实亡。多表关联、带函数变换的复杂语句,得自己手写,或者用专业的数据库客户端来做。
权威参考资料
本文标题:《SQL语句生成器怎么用?安全地批量改一批文章的SEO字段》
本文链接:https://zhangwenbao.com/sql-generator-batch-update-cms-database-injection-guide.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0