对于oracle来说是要有DML操作,就会记录在undo中,就会产生lock
SQL基础
非关联子查询:子查询中并没有上一级表字段的介入
select * from emp where deptno = (select deptno from dept where loc = 'NEW YORK')
关联子查询:子查询和上一级表字段进行关联查询
select e.*,(select d.loc from dept d where e.deptno=d.deptno) from emp e
in和exists的区别:in是全盘扫描,exists是判断是否存在找到就返回。
查询emp表中是领导的员工
SELECT * FROM EMP A WHERE EXISTS (SELECT 1 FROM EMP B WHERE A.EMPNO=B.MGR)
查询emp表中没有员工的部门
SELECT * FROM DEPT A WHERE NOT EXISTS (SELECT 1 FROM EMP B WHERE A.DEPTNO=B.DEPTNO)
SQL高级
merge查询:查询emp_xx表中是否有empno=1且mgr=8的员工,如果有则更改它的job=项目组长,sal=20000,如果没有则添加员工:
merge into emp_xx a
using (select 1 empno,8 mgr from dual)b
on (a.empno=b.empno and a.mgr = b.mgr)
when matched then
update set job='项目组长',sal=20000
when not matched then
insert (empno,ename,job) values(8,'石印','架构师')
递归查询:一般用于查询tree
select * from city
(此处可以添加where条件)
start with id=306
connect by prior pid=id(父节点在前向上查询)
子节点在前,向下查询
oracle分析函数(主要运用于分析统计)
over查询:(oracle特有函数)
查询员工的薪水连续求和,注意over(order by ename)
select deptno,ename,sal,
sum(sal) over(order by ename) 连续求和,
sum(sal) over() 总和, --(此处sum(sal) over( )等同于sum(sal))
100*round(sal/sum(sal) over(),5) "(份额%)"
from emp
使用子分区查出各部门薪水连续的总和,注意按部门分区,注意over()条件的不同
select deptno,ename,sal,
sum(sal) over(partition by deptno order by ename) 部门连续求和,
sum(sal) over(partition by deptno) 部门总和,
100*round(sal/sum(sal) over(partition by deptno),4)在部门所占份额,
sum(sal) over(order by ename) 按个人连续工资总和,
sum(sal) over()总工资总和,
100*round(sal/sum(sal) over(),4)在总公司所占份额
from emp order by deptno
--根据部门分级,显示每个人在各自部门的工资等级
select e.*,Row_Number() over(partition by deptno order by sal desc)rank from emp e
--使用group by rollup()和grouping()统计各部门薪资总计以及所有部门总薪资
grouping()函数用法,带一个参数,参数为字段名,如果是本身的结果就返回0,如果是合计就返回1
(case when (grouping(job)=1 and grouping(deptno)=0) then '部门总计'
when (grouping(job)=1 and grouping(deptno)=1) then '所有总计'
else job end
) job,
sum(sal)
from emp
group by rollup (deptno,job)
--rank 每个人在各自部门中的排名
select e.*,
rank() over(partition by deptno order by sal desc)排名
from emp e
dense_rank 每个人在各自部门中的排名,会有并列,不会产生名次跳跃
select e.*,
dense_rank() over(partition by deptno order by sal desc)排名
from emp e
row_number() 每个人在各自部门中的排名,会有并列,不会产生名次跳跃
select e.*,
row_number() over(partition by deptno order by sal desc)排名
from emp e
oracle事务特性
事务必须具备以下四个属性,简称ACID属性:
原子性(Atomicity):事务是一个完整的操作,事物的各步操作是不可分的(原子的),要么都执行,要么都不执行。
一致性(Consistency):一个查询的结果必须与在开始查询的那一刻的状态一致(读不等代写,写不等待读)。
场景:查询数据,九点查询数据,九点十五查询完毕,在这期间所查询的数据被其他操作更新,那么查询结果一定是未被更改的数据,一般oracle是把这个没有更新的数据放入到undo里,如果oracle在undo中没有找到数据,则宁可报错,也不会让你看到其他操作更新后的数据。
隔离性(Isolation):对于其他会话来说,未完成的(也就是未提交的)事务必须不可见。
场景:事务与事务之间相互隔离,2个session一个查询一个更新,那么更新操作在commit之前,查询所看到的数据是没有提交之前的,相互没有影响。
持久性(Durability):事务一旦提交完成后,数据库就不可以丢失这个结果,数据库通过日志能够保持事务的持久性。
场景:事务提交后不可逆,提交数据是由内存的数据刷新到磁盘上,这个过程的快慢和性能有关,那么oracle主要是靠‘rudo’日志,先记录到日志上,再写进磁盘(commit的那一刻数据就已经在rudo日志中记录好了,哪怕下一秒断电,oracle在恢复后,会在‘rudo’日志中找到更新的数据记录,将没有完成的事务完成)。
查看事务:
select * from v$transaction
查看锁:
select * from v$lock;
加锁模式
自动加锁,做DML操作时,如insert,delete,update,以及select ... from update由oracle自动完成加锁。
select * from emp where deptno=10 for update;(会给结果集加上一个行级共享锁,其他的会话只能进行查询操作)
锁定数据后,如果其他用户在修改该记录会处于等待状态
修改其部门记录为10的记录会被锁定,我们可以进行试探要修改的记录是否已经加锁。如下三种形式均可:
select * from emp where empno=7782 for update nowait;
select * from scott.emp where empno=7782 for update wait 5;
select * from scott.emp where job='CLERK' for update skip locked;
如果这个锁占用时间太长,我们可以通过管理员杀掉session用户:
首先找到是哪个session(sid)占用了太长时间
select * from v$lock;
然后根据v$lock表中的sid,去v$session里面找到,进行kill操作。
select * from v$session where sid = 63;
alter system kill session' SID,SERIAL';
在scott用户(将表锁定的用户)commit时,就会显示如下:
oracle死锁
oracle会自动解决思索问题,把响应的死锁解除。
实验:
create table A(id int);
insert into A values(100);
insert into A values(100);
insert into A values(100);
select * from A;
>建立死锁机制
--A用户
update A set id = 1000 where id = 100;
--B用户
update A set id = 2000 where id = 200;
--A用户去触碰B用户正在使用的id=200的数据
update A set id = 1500 where id = 200;
--B用户去触碰A用户正在使用的id=100的数据
update A set id = 1100 where id = 100;
oracle索引
索引主要分为B树索引和位图索引,这里主要提到的是B树索引。
一般sql优化有几种方案:
1 索引(index)
2 分区(partition)
3 物化视图
4 并行查询
索引说明:
索引是与表相关的一个可选结构,在逻辑和物理上都独立于表的数据,索引能优化查询,不能优化DML操作,Oracle自动维护索引,频繁的DML操作反而会引起大量的索引维护(也就是进行写操作,产生IO时性能会降低,查询效率提升)。
如果SQL语句仅访问被索引的列,那么数据库只需从索引中读取,而不用读取表。
如果该语句还需要访问除索引列之外的列,那么数据库会使用rowid查找表中的行,
通常为检索表数据,数据以交替方式先读取索引块,然后读取相应的表块。
索引的目的是:主要是减少IO,这是本质,这样才能体现索引的效率。
创建索引的规则:
1 大表,返回的行数 < 5%(返回较少的记录)
2 经常使用where子查询的列
3 离散度高的列
4 更新键值代价低(不是频繁更新的数据,否则需要频繁的维护索引)
5 逻辑AND,OR效率高
6 参看索引在表的位置(哪一列)
select * from user_indexes;
select * from user_ind_columns;
索引的使用:建立索引时,最好在建表之初就确定好索引,将其建立好,不要在表中已经插入了数据后,再半途创建索引,这样效率会降低。
(1)一般索引: 索引键值可以重复
(索引名) (给表的什么列创建索引)
create index empno_idx on emp(empno);
(2)唯一索引:唯一索引键值,不可重复
create unique index empno_idx on emp(empno);
索引碎片:
查看执行计划:set autotrace traceonly explain;
索引碎片问题:
由于对基表进行DML操作,导致索引表,块的自动更改操作,尤其是基表的delete操作会引起index表的index_entries的逻辑删除,注意只有当一个索引块中的全部index_entry都被删除了,才会把这个索引块删除。索引对基表的delete、insert操作都会产生碎片索引的问题。
当一个项目上线很久后,其中的表经过大量的DML操作,其中的索引被频繁的更新、删除,就会产生大量的索引碎片,Oracle中是可以解决的。
在Oracle文档里并没有清晰地给出索引碎片的量化标准,Oracle建议通过Segment Advisor(段顾问)解决表和索引的碎片问题。如果你想自行解决,可以通过查看index_stats视图,当以下三种情况发生时,说明积累的碎片已经应该整理了。
1 HEIGHT > 4(树的层级)
2 PCT_USED < 50%(Oracle 服务器试图为表内每个数据块维持的已用空间最低百分比,PCTUSED的规则是,值越小,后面的INSERT操作会更快,缺点是浪费一些空间;值越大,空间利用率高一些,性能稍差。)
3 DEL_LF_ROWS/LF_ROWS > 0.2(删除的的索引/全部索引)
Oracle解决索引碎片问题:
实验:
首先创建A表,只有一个字段id,并为该字段创建索引
create table a(id int);
create index ind_1 on a(id);
查询一下我们创建的索引
select * from user_ind_columns;
执行插入记录
begin
for i in 1..1000000 loop
insert into a values(i);
if mod(i,100)=0 then
commit;
end if;
end loop;
end;
首先查询索引状态:发现状态为空
select name,height,pct_used,del_lf_rows/lf_rows from index_stats;
这是因为没有分析索引
分析:analyze index ind_1 validate structure;
分析过后再次查询索引状态:
查询结果完全符合上述,整理索引碎片的要求。
接下来破坏索引:
delete from a where id <= 700000
删除后再次查询索引状态,发现并没有任何改变
这还是因为没有再次分析索引
analyze index ind_1 validate structure;
最后一个指标变成了0.7,结合上述整理索引碎片的要求,说明已经需要整理。
Oracle可以在线实现整理索引碎片(不影响表的使用):
alter index ind_1 rebuild online [tablespace name];
(重构)(在线)
Oracle在9i的时候没有这个功能,之前要重新整理索引碎片,只能停掉服务,将表锁住,将其中内容倒入另外一张新表,然后进行整理。
在线整理后,分析索引,再次查询索引状态,发现del_lf_rows/lf_rows这一列已经归0,再一次符合上述3个要求,说明整理成功。
实际开发中,如果因为索引碎片产生影响,可以在线整理,先解决掉查询延迟的问题,然后将数据导出,再导入新表,将原表干掉,用新表代替之。
数据库表的设计
1 业务合理切分
2 逻辑分层(基础表的合理设计+业务表,也叫数据库分层, 垂直拆分,分库)
3 数据库表结构
水平拆分:根据规则策略拆分同一张表,根据id或者vip级别(因为考略到vip级别会变动,根据VIP拆分需要有张中间表,有更新时将更新数据放入中间表,做到数据留痕,查询时先查看数据是否有更新,如果有则查询中间表,再定时将更新后的数据同步到相对应的其他表中)拆分。
(1)mysql水平拆分(分片):单表最多1000万条数据。
(2)oracle可以做很多分区,单个分区存储500万条数据。
(3)oracle物化视图,可以将几个表中常用到的字段抽取出来,保存到物化视图中,基表数据更新时,物化视图也会随之更新,这样就解决了跨库多表join问题。
(4)中间表:可以按照一定的规则,比如:年,月,日,做日月年统计分析时,按照规则在中间表查询,无需全表查询。
结构优化
(1)建立索引(索引不可以做任何的函数操作否则失效)(2)建立规则索引(例使用空间维度:按照省级id+地区id+本身id+UUID,要查询指定地区的数据,可以使用where id>=省级id+指定地区id+00000000(用零补位) and id>=省级id+(指定地区id+1)+00000000(用零补位),uuid可以转化为数字进行比较)
(3)复合索引
(4)数据规则:添加认为必要的扩展字段,比如:create_by,create_time,update_by,update_time,当做统计分析时,需要统计当天的结果汇总,过段时间发现数据需要改变,当数据更新后,当天的统计结果一样需要更新,就可以通过着四个时间段,找到更新了的数据,再更新当天的统计汇总。
(5)扩展字段:用于后期业务扩展后,和其他表产生关联,存储其他表的外键。
(6)合理冗余:将多个表常用的子段冗余到一张表中,合理使用可以减少表的join,大幅提高查询效率。
物化视图
我们都知道视图是一种虚表,其目的仅仅是为了方便我们进行综合数据的查询而已,他并不能够帮助我们提高性能。
物化视图这一概念的引出,在相应的场景下,他可以提高查询的性能
物化视图是一种特殊的物理表,如果有A、B两张表,将其中常用字段抽取出来,形成一张新表,这样每次查询无需跨库join,基表更新,物化视图也会随之更新。
物化视图特点:
物化视图从某种意义上说就是一个物理表(但不仅仅是一个物理表),这单通过user_tables查询出来,可以得到佐证。
物化视图也是一种段(segment),所以其有自己的物理存储属性。
物化视图会占用数据库磁盘空间,这点从user_segment可以查询出,得到佐证、
物化视图的类型:oracle提供了两种方式,手工刷新和自动刷新,即on demabd、on commit
二者区别在于刷新方法的不同,on demand顾名思义,仅在物化视图需要被刷新时,才更新物化视图,以保证和基表数据的一致性。on commit是说,一旦基表执行了DML操作,会立刻更新物化视图,同步基表数据。
创建语句:
create materialized view 物化视图名 【选项N】as select * from 表名
【选项1】:build(immediate,deferred)是否在创建视图时生成数据,默认生成deferred为不生成数据,需要时生成。
【选项2】:refresh(fast,complete,force,never):fast是增量刷新;complete是全表刷新;force是如果可以使用增量刷新则使用,如果不行就全表刷新;never从不刷新。
【选项3】:on(demand,commit),即手工或者自动刷新提交时的数据
【选项4】:start with 通知数据库从主表到本地第一次复制的时间。
【选项5】:next 刷新时间间隔,下一次刷新时间=上一次执行完成时间+时间间隔
create materialized view empdpt
refresh force on commit as
select e.ename,e.job,e.sal,d.dname from emp e,dept d where e.deptno=d.deptno
证明物化视图存在于物理视图中,这条创建表的语句是全表刷新,增量刷新还需其他配置。
on demand刷新:
create materialized view mv_empdet
refresh fast on demand
start with sysdate next sysdate+1/1440
as
select e.rowid as arowid,d.rowid as browid,e.ename,e.job,e.sal,d.dname from emp e,dept d where e.deptno=d.deptno
增量刷新:
create materialized view log on emp with rowid;
create materialized view log on dept with rowid;
--(每隔一分钟刷新一次)
create materialized view mv_empdet
refresh force on demand
start with sysdate next sysdate+1/1440
as
select e.rowid as arowid,d.rowid as browid,e.ename,e.job,e.sal,d.dname from emp e,dept d where e.deptno=d.deptno
同义词
字面上理解就是别名的意思,和视图的功能类似,就是一种映射关系。
同义词可以实现不同用户不同数据库之间的跨库join。
私有同义词:一般是普通用户自己建立的同义词,创建者需要具有create synonym权限
grant create synonym to scott;
create synonym abc for emp;
公有同义词:一般是由DBA创建,所有用户都可以使用,创建者需要create public synonym权限
grant create public synonym to scott;
create public synonym xyz for soctt.emp;(如果是其他用户创建scott用户的表的同义词需要指定用户名前缀)
drop public synonym xyz;
关于同义词的几个要点:
私有同义词是模式对象,一般在自己的模式中使用,如果其他模式使用则必须佣模式名前缀限定。
公有同义词不是模式对象,不能用模式名做前缀。
私有和公有同义词同名时,如果只想不同的对象,私有同义词出优先。
引用同义词的对象(表或试图)被删除了,同义词仍然存在,这同试图类似,重新创建该对象名,下次访问同义词时自动编译。
例:
create synonym xyz for emp1;
drop table emp1;
select * from xyz; (已删除表,同义词转换不再有效)
flashback table emp1 to before drop;
select * from xyz; (利用闪回表,同义词仍然有效)
DBLink
oracle的dblink用于对不同的数据库实例或者远程进行链接,实现跨库join查询,语法如下:
Create public database link link名称
connect to 用户名 identified by 密码 using '(description =
(address_list = (address=(protocol=tcp)(host= IP )(port=端口号)))
(connect_data=(service_name=服务名称))
)';
drop public database link lianlink; 删除dblink
dblink+同义词实现跨库join
此外如果要创建一个远程的数据库上的某张表的同义词,需要先创建一个Database Link(数据库连接)来扩展访问,然后在使用如下语句创建数据库同义词:create public synonym table_name for table_name@DB_Link;
数据分区
OLTP和OLAP
在互联网时代,海量数据的存储与访问成为系统设计与访问瓶颈,对海量数据处理按照使用场景,主要分为两类:联机事务处理(OLTP),联机分析处理(OLAP)
联机事务处理(OLTP,分布式事务)也称为面向交易的处理系统,其基本特征是原始数据可以立刻传送到计算中心进行处理,并在很短时间内给出处理结果(主要用于日常交易处理,业务逻辑相对简单,实时性读写要求高,事务具有强一致性)。
联机分析处理(OLAP)是指通过多维的方式对数据进行分析、查询和报表,可以通数据挖掘工具、统计分析工具配合使用(主要用于统计,分析,报表,业务逻辑复杂,事务要求相对较弱,实时性读写要求低)。
何为数据切分?
数据的切分根据切分规则的类型可以分为两类:
垂直切分:按照不同的表来分到不同的数据库,这种切分可以称之为数据的垂直切分。
垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务的耦合度非常低、相互影响很小、业务逻辑非常清晰的系统,这种系统中,可以很容易做到将不同的业务模块所使用的表拆分到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也简单清晰。
垂直拆分的优点:
业务逻辑清晰。
可扩展性强。
维护简单方便。
垂直拆分就是最上层的业务逻辑拆分,比如电商的供应商,商品,库存,订单,网站等模块的业务流程非常清晰可见,最上层垂直拆分即可。
水平切分:根据表中数据逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库上,这种切分称之为数据的水平切分。
水平切分对于垂直切分相比,稍微复杂一些,因为需要将同一张表中不同数据拆分到不同的数据库中,对应用程序而言,拆分规则本身就较按照表名来拆分更加复杂,后期的数据维护也更加复杂一些。
水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个数据库中,每张表包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行进行拆分,就是将表中某些行切分到一个数据库中,而另外的某些行又切分到其他数据库中。
切分表的条件:
1 当并发访问非常频繁时
2 数据库文件太大时(超过30G)
3 分表往往意味着分库,因为将分开的表放进同一个数据库中,当访问来临时依然还是集中在这个数据库中,无法减缓数据库本身的压力,所以必须分表+分库,这样由多个数据库分担压力。
水平切分的优点:
拆分规则做的足够好,基本可以单库实现join操作。
应用端改造较少,可以稍微轻松实现业务逻辑,但是后期业务变更维护较为麻烦。
不存在单库多数据,以及高并发下性能的瓶颈问题,提高系统的稳定性和负载能力。
水平拆比如涉及到用户信息,订单信息,一般会设计多个系统,比如用户信息系统统计用户信息,根据用户级别等划分到不同的库,或者根据用户类型的方式,把不同级别的用户分散到不同的数据库节点上去,或者按照用户的编号ID,做求模方式等等。(水平拆分一定是在精通业务的前提下才可以进行的,保证拆分的正确性,后期的维护扩展性等,可以根据不同的数据信息,如时间单位年月日;不同类型的角色用户如供应商,会员用户;或者按照不同的业务规则等去做的,又比如我们在电商网站购买商品时,商品也会做分类,比如自营商品和第三方商品,那么就会进行分库操作,比如订单提交后会流转到不同的服务节点上去)
数据拆分缺点和解决方案
引入了分布式事务的问题(解决方案:针对不同场景案例具体分析)
例如:
1 业务逻辑复杂时,使用SOA做通用服务,在Service层上做多个切面,配置多个事务。
(当一个业务逻辑,需要使用不同模块中的serivce时,可以另起一个service配置事务切面,让不同的service在其中执行,当前方法实行失败抛出异常后,自然会回滚其中的不同的service)
2 数据量大且分析逻辑复杂时,可以使用缓冲库(中间库),缓存表等数据库设计。
3 实时性要求非常高且数据信息、业务逻辑简单单一,使用第三方数据通信组件,例如消息队列做事务的回调服务,或者使用zookeeper建立分布式锁进行数据同步,或者使用直连的Netty进行通信,类似WebService,Restfual等直接请求。跨节点join的问题、跨节点合并、排序、分页等处理数据的问题。
1 通用方案是把数据组织好以后放到缓存中去,定时或者实时进行同步。
2 如果要求实时性不是特别高,也可以使用中间库的手段去解决。(可以使用oracle中的物化视图去解决这个问题,当数据更新时更新基表,然后提交给物化视图,当涉及到跨库查询时,查询物化视图,但是并发也不能太高(每秒吞吐量过万),因为物化视图也是基于物理文件,基表更新太频繁物化视图就会产生大量的IO)
多数据源管理问题:
1 类似使用mycat的代理平台,管理多个数据源。
2 在各个应用程序模块中配置管理自己所需要的一个(或多个)数据源,直接访问各个数据库,在模块内完成数据的整合。
分区表介绍
表分区是日常开发中最常用的技术,主要针对于大数据量、频繁查询等需求,有了表分区,我们可以对表进行区间的拆分和组织,提高查询的效率。一般来讲,oracle表分区的一个区间数据最好不大于500W条,也就是说500W条数据左右可以划分为一个区间,根据实际业务需求和表分区的性能而定。分区只有在创建表的时候指定,不能创建好表以后进行分区。
range分区就是区域分区,按照定义的区域进行分区。
create table sale(
product_id varchar2(5),
sale_count number(10,2)
)
partition by range(sale_count)
(
partition p1 values less than (1000),
partition p2 values less than (2000),
partition p3 values less than (3000)
);
查询分区信息
select * from user_tab_partitions;
插入三条数据,分别按分区查询,发现数据已经被分区
insert into sale values('掌纹',555);
insert into sale values('石印',1555);
insert into sale values('无恙',2435);
select * from sale partition (p1);
select * from sale partition (p2);
select * from sale partition (p3);
修改分区:
添加新的分区:
alter table sale add partition p4 values less than (maxvalue);
删除分区:
alter table sale drop partition p4;
更新数据时不可以跨分区操作,会出现错误,需要设置可以动的分区才能进行跨分区查询。
alter table sale enable row movement;
问题:如果开始建立的是普通的堆表,后期想要改成分区表该如何操作?
分区索引
分区之后虽然提高了查询的效率,但也仅仅是缩小了产寻的范围,所以我们在有必要的情况下,需要建立分区索引,进一步提高查询效率。
分区索引大体上分两大类:一类叫做local(每个的分区上建立索引,使用最多),一类叫做global(相当于全局索引,分不分区都是相同的,一般不使用)。
还有一种就是自定义数据区间的索引,也叫作前缀索引,这个是非常有意义的,自定义区域值时必须要注意设置maxvalue(比如:共有四个区间,每个区间容纳1000条数据,但是查询只要集中在1-2500之间,可以只针对于这个区间建立索引,就是前缀索引)。
另外要注意:在分区上建立的索引必须是分区字段列,否则就和普通的全局索引没有区别。
local方式语法:create index 索引名 on 表名(分区字段) local;
global自定义全局方式(前缀索引)语法:
create index 索引名 on 表名(分区字段) global
partition by range(field){
partition p1 values less than (value),
partition p2 values less than (maxvalue) //一定要有maxvalue限制
}
hash分区
hash分区实现均匀的负载值分配,增加hash分区可以重新分配数据。
建立散列分区表:
create table my_emp(
empno number,
ename varchar2(10)
)
partition by hash(empno)(
partition p1,partition p2 //没有指定分区数据就会平均分布
);
查看分区表结构:select * from user_tab_partitions where table_name='表名';
删除表后,分区被移动到了recycle bin(回收站)中,闪回表后,分区也将重新恢复,删除表后,perge recyclebin才会彻底删除分区。
间隔分区(interval分区):
是一种自动化的分区,可以指定时间间隔进行分区,这个功能在工作中非常实用,是range分区的引申(说白了按照时间维度进行分区)。
create table interval_sale
(sid int,sdate timestamp)
partition by range(sdate)
interval (numtoyminterval(1,'MONTH'))
(
partition p1 values less than (timestamp '2018-04-16 00:00:00')
);