请上传宽度大于 1200px,高度大于 164px 的封面图片
    调整图片尺寸与位置
    滚轮可以放大缩小图片尺寸,按住图片拖动可调整位置,多余的会自动被裁剪掉
取消
有关SQL(uid:832972)
讨论所有与SQL有关的内容 职业资格认证:尚未取得认证
可以拿来吊打面试官的 SQL Join (三)
回到正题,上一讲中,文末留了个预告,这回要讲  WHERE 和 ON 时,对索引的访问方式,以及无索引时,访问效率如何提高。 拿上讲中的例子,来做探讨。基于公平的基础上,我们比较 WHERE 和 ON 的性能,实际可以归纳成比较下两个例子的快慢: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     INNER JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Detail.Amount > 1000WHERE Header.OrderDate > '2020-01-01'  Detail.Amount > 1000 放在 WHERE 中: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     INNER JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId WHERE Header.OrderDate > '2020-01-01'     AND Detail.Amount > 1000 从原业务库,复制了一份轻量型的数据。暂时还没加上索引,肯定是不够优化的。正是由于这份原始的数据,我们得以窥探,在没有任何索引的时候,两表怎么优化。 面对调优,我的黄氏三板斧,大家应该都不陌生了。如果是新关注的读者,可以到文末的搜索框里搜索。点击#三板斧#的标签即可。 在这里,用到执行计划。 以上就是二段 SQL 的执行计划。一模一样。 不出意外的,如果没有索引,那么表扫描(Table Scan)是唯一的 Join 解法。平时跟大家说,要懂点英语,要懂点英语,好处就在这里。Table Scan, Hash Match, 下次你面试的时候,听到这些词,就不会胆怯。 按照 Table Scan 的图标,点进去,我们会有惊喜。 上面两个执行算子,都使用了 Predicate. Predicate 在这里的作用是条件判断。即筛选满足条件的记录。 Predicate 在 Table Scan 里面出现,说明它仅仅能减少结果数据集,不等于减少从磁盘读到的数据。因为 Table Scan 就说明了,数据的查找方式,是按照全表扫描方式进行的。 有细心的读者,可能会发现,这里有个 Hash Match (Inner Join). 好多读者对 Left Join, Right Join , Full Join  滚瓜烂熟。但对于 Hash Join, Merge Join, Nested Loop Join 是一点概念都没有。 这大概就是不好好读书,带来的差别。 回忆下我们这两张表数据的来历,就知道,在Join字段上,并没有加索引。没有索引的情况下,Join 怎么提高性能呢?答案就是 Hash join. 至于它的原理,还有 Merge Join, Nested Loop Join 的内幕,我打算留到后面,找合适的机会再讲。其实,我的知识星球里面,早已写得很明白。 如何给这段 SQL 提高性能? 解决之道在于索引。 ## 尝试给两表加索引CREATE INDEX IDX_HEADER ON tblOrderHeader(OrderId)CREATE INDEX IDX_DETAIL ON tblOrderDetail(OrderId)## end  但是呢,加了索引之后,发现执行时间还长了。真别不信,有时候 Table Scan 真要快很多。这就是个例子。 所以,我们继续要加索引: CREATE INDEX IDX_HEADER_ORDER_DATE ON tblOrderHeader(OrderDate)CREATE INDEX IDX_DETAIL_AMOUNT ON tblOrderDetail(Amount)     索引加对,速度爆表 等到加完索引,再回头比较两个 INNER JOIN, 其实他们的执行计划和速度都是一模一样的。 在本文中,提供了比较两个 Join 写法的思路,还有通过增加索引,提高性能的实战操作经验。现实环境中,条件比上面两个表,要复杂的多。可能需要花费我们一些耐心,才能找到最优解。 上面很多执行计划,其实都只写了一半,大家有可能看得云里雾里。没关系,下一文,我会继续展开讲解下在本章中出现的执行计划操作符。 敬请期待!
可以拿来吊打面试官的 SQL Join (二)
我在这个系列中,所分享的知识,力求逻辑严谨,实战辅证。但一如所有的文章一样,读者需要自己思考,是否正确无误,是否可以拿来直接作用于生产环境。对于没有理解透彻,就直接用于生产而造成的损失,本人概不负责。 不仅仅是看我的文章,看所有的文章,书籍或者教程,读者朋友们都该用自己的思维,带着批判的眼光,谨慎对待作者发表的观点,切勿盲目轻信。 回到正题,上一讲中,文末留了个预告,这回要讲 WHERE 和 ON 中,使用条件判断,哪个速度更快。 其实,对于数据库玩家来说,这是个很大的话题。讲明白这里面的道道,我们需要对很多限制条件做假设。或许太过于学术,枯燥和繁琐,但要让大家都明白,不得不这么做,所以才开这个系列。 刚才我说有很多限制条件要做假设,其实就为了公平的去衡量两者的执行性能。不能在 WHERE 中故意不加索引,或者故意在 ON 的条件上,加函数表达式。这样比起来就毫无意义。 比如拿第一篇文章中的例子来说: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Detail.Amount > 1000WHERE Header.OrderDate > '2020-01-01'  事实上,我们应该假设,使用了 INNER JOIN. 在上篇文章中,有读者就提到 INNER JOIN 的情况下, 筛选表达式放在 WHERE 和 ON , 功能类似。所以这里我们就先讨论 INNER JOIN的情况。 将例子改成 INNER JOIN: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     INNER JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Detail.Amount > 1000WHERE Header.OrderDate > '2020-01-01'  此时,比较 Detail.Amount > 1000 放在 WHERE 中: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     INNER JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId WHERE Header.OrderDate > '2020-01-01'     AND Detail.Amount > 1000 这样比较,就满足了假设条件公平。 那什么样的情况,假设条件是不公平的呢,比如放在 WHERE 中,我们把 Detail.Amount > 1000 写成了 Detail.Amount - 1000 > 0 这就是不公平。 Amount> 1000 和 Amount - 1000 > 0 看起来一样,那么为什么就不公平了呢?稍有经验的朋友,就知道,在字段前加了函数的判断,是不能有效利用索引的。 而 Amount - 1000 相当于加了函数: f(x)= x - 1000 这里要注意,有效利用索引和不走索引,完全是两码事。索引扫描 (Index Scan )和 索引搜索 (Index Seek)两者的性能截然不同。我们常说的索引,是平衡树(B-Tree)结构。Index Scan 会遍历这棵树,假如树上有 10W+ 叶子,也要全部访问一遍。但 Index Seek 是搜索树的叶子,每个树节点都记录着它叶子的数量还有编号,seek 的时候访问节点,就知道有没有需要的叶子,此时性能必然快。 image Windows画图画的,不要嫌丑啊 索引的结构类似这样,root page 记录了索引 Amount 的范围. Amount 在500以下的,归档在 Node 1 节点上,而500以上,1000以下的,归档在 Node 2 上。 Node 1 共有 5 条记录, Amount 在 1 到 200 之间的记录是 Node 1 的第一条记录;Amount 在 201 到 300 之间的记录,是 Node 1 的第二条记录,以此类推。 (注意:Node 1, root page, Node 2 都是数据页,索引的数据页。) 其中 Node 1 的第一条记录,保存的是 Amount 范围,即 在1 到 200,随之保存的还有表中记录的地址指针,以此地址指针可以回溯表记录的其他信息。这一过程,称之为回表 这就是索引的基本数据结构与信息。掌握了这个知识点后,我们看 index scan 与 index seek 的区别。scan 是把 Node 1 和 Node 2 上的所有数据都扫描了一遍,而 seek 则很快可以定位到,比如 Amount = 756 是在 Node 2 上,从而省却对 Node 1的访问,性能就快了。 实际操作中,索引的数据页要比这里多得多,记录的信息也复杂的多。但这幅图足够说明索引的益处。 好,今天的分享暂时到这里。这是个系列,所以不适合一文讲太多,消化下再继续。下文会讲 WHERE 和 ON 时,对索引的访问方式,以及无索引时,访问效率如何提高。
可以拿来吊打面试官的 SQL Join (一)
这一系列讲述,两张表的 Join, 或许你都不一定知道的事儿。 之前写过 SQL 的编译原理,很多朋友都不知道 SQL 背后,居然还有编译一说。SQL 用起来和 C#/Java 还是有些异样的。写好 SELECT * (虽然这么写很糟糕!)以后,按下 F5 便能得到结果。而不像 C#/Java 需要经过 CLR/JVM 这样的“转译”,才能看到实实在在的程序输入输出窗口。 正是由于 SQL 这个隐形编译器的存在,很多莫名的语法,看起来就很费解。比如最让初学者头疼的 Left Join: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId WHERE Header.OrderDate > '2020-01-01'     AND Detail.Amount > 1000 本意上,这段 SQL 要达到的目的是,找出 2020 年 1 月 1 日以来,单件商品超过 1000 元销售额的订单,并显示该件商品的产品名。 到底为止,若能根据这个要求,完整写出上面的 SQL,那就是合格的数据库开发工程师了。但有些朋友,经常会写出这样的 SQL: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Detail.Amount > 1000WHERE Header.OrderDate > '2020-01-01'  还有这样的 SQL: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Header.OrderDate > '2020-01-01'         AND Detail.Amount > 1000 看上去,及其相似的三段 SQL,为什么出来的结果就千差万别呢。甚至,还会把数据库给跑死。 这就是 SQL 编译的底层。 要了解 SQL 编译的底层,要从这张图,全面入手: 而 Parsing 就是我们正确理解 SQL 执行过程的第一步。 下面这段 SQL 是比较完整的全范本: (8) SELECT (9) DISTINCT (11) <TOP_specification> <select_list>(1) FROM <left_table>(3) <join_type> JOIN <right_table>(2) ON <join_condition>(4) WHERE <where_condition>(5) GROUP BY <group_by_list>(6) WITH {CUBE | ROLLUP}(7) HAVING <having_condition>(10) ORDER BY <order_by_list> 细心的读者可能会留意到每个 SQL 关键字前面都有一个用括号包起来的数字,这个数字就是 SQL 关键字执行的顺序。 我们通常会认为 SELECT 是 SQL 的第一步,其实 FROM 才是,紧接 From 的是 ON, JOIN. 之后才是 WHERE. 正确理解 JOIN 和 WHERE 的执行顺序,才可避免 LEFT JOIN 留下的坑。 当然,你别以为这样就结束了,那我也太不负责任了。接着往下读。 当两表 Join 的时候,先按照 ON 的条件做了一次笛卡尔积计算。甭管按照 ON 的条件能不能匹配,匹配的上,就拼接起来;匹配不上的,暂时保留。所以 ON 这一步,两边的数据,都会保留在一张虚拟的大表里。 比如,上面两张表, tblOrderHeader, tblOrderDetail. 他们的外键是OrderId. 这两个表,join 起来,会有这些情况: tblOrderHeader 有些数据,在 tblOrderDetail 里按照 OrderId 找不到对应的订单明晰数据。 同样的,在 tblOrderDetail 中有些明细的订单,却在 tblOrderHeader 头部中找不到订单表头信息,比如订单时间,商店,会员信息等。 更常见的,是互相找不到对应数据 用实线框,框起来的表示两表可以互相匹配的数据。而对方表缺失的部分就用白色标注。 两表 Join 的初步结果就出来了,就是上面最后一张图的情况,即保留两表所有的数据,匹配上的,排在前头,匹配不上的依次排在后面。但必须保留两张表所有的数据。这要牢牢记住。 接着根据第三步 JOIN 的 Join Type(Left join, Right Join, Full Outer Join)来限制留下哪部分。 Left Join, 留下左半部分: Right Join, 留下右半部分: Full Outer Join , 左右都留下: 接下来,才是执行 WHERE 命令的时候。 此时,下面这段 SQL , 即 SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId         AND Detail.Amount > 1000WHERE Header.OrderDate > '2020-01-01'  会比这条 SQL , 多出来很多数据: SELECT Header.OrderId    ,   Detail.ProductName    ,   Detail.Amount FROM tblOrderHeader Header     LEFT JOIN tblOrderDetail Detail         ON Header.OrderId = Detail.OrderId WHERE Header.OrderDate > '2020-01-01'     AND Detail.Amount > 1000 那是因为,在 WHERE 中,Detail.Amount > 1000 这个命令,限制了右半边的数据必须要对应上左半边的OrderId, 所以 tblOrderDetail 中如果没有 tblOrderHeader 中的OrderId, 则就被舍去。哪怕 tblOrderHeader 的 OrderDate 是符合 OrderDate 大于 2020-01-01的条件。 比如有 tblOrderHeader 是有 5 条记录,符合  OrderDate 大于 2020-01-01的条件。 我们用红色实星框表示符合条件的记录 但最终,因为在 WHERE 中添加了 Detail.Amount > 1000 的条件,相当于把 Left join 改成了 INNER JOIN, 即增加了 Detail.OrderId IS NOT NULL 条件
如何写好 5000 行的 SQL 代码
上千行的 SQL 代码常见,且永不过时! 经历了大大小小的 MIS 系统,小到几人用的协作系统,几十人用的 OA 系统,到上千人用的 MES/ERP 系统,再到百万人用的电商系统,存储过程的影子在半个世纪(20世纪70年代末开始)以来从未淡出它的战场。我们几个 SQL 老玩家经常自吹, SQL 是半衰期最长的编程语言。玩会它不用担心失业。 我之前写过如何去阅读和拆解一个上千行的 SQL 存储过程,详情可见以下两篇文章: 如何提高阅读 SQL 源代码的快感 如何写好上千行的 SQL 存储过程(附代码规范) 这两文中提到了四大步骤:理解代码,分拆代码,改写代码和保存代码。拆过无数的代码,从上千行缩减到 2 成,也组装过无数的代码,从上百行塞成了上千行,业务所需。见过最长的 SQL 代码超 5000 行,已简无所简,那就实事求是了。人有分分合合,有生命力的代码也一样。 就像我们能读懂村上春树的小说《且听风吟》、《刺杀骑士团长》一样,但我们无法写出来或者说无法写的那么好。当然那毕竟是村上赖以为生的技能,老人家写了30多年的小说,我们可能一部都没完整的写完过,没法儿比。既然如此,在我们赖以为生的SQL阵营,这门吃饭的技能一定是要好好磨练的。 下面的领悟来自我实战中真实的想法,趟过无数次的坑,用教训总结出来的几条自认为极有用的经验。 理解业务 快速实现 重构与测试 版本控制 复盘记录 1    理解业务: 你肯定不会去写没有业务逻辑的代码。充分理解业务逻辑对你有两个好处:一是写出可执行的并且可扩展的代码;二是主动了解业务将有利于职业生涯升级。 第一个好处不言而喻,写代码写出颈椎病的程序员,肯定意识到代码的扩展性,可以节省去医院的时间,可以霸屏更多次王者。 比如产品的价格。电商时代,产品的价格拥有明显的扩展属性。也就是说,今天是这个价,明天又是另一个价。电商时代给双11,双12附上了商业促销标签,对产品价格提出了高要求。此时,你去设定一个商品价格,你会怎么设计?是在原来的价格基础上直接更新,还是另起一列,承载新价格?这类价格设计,会直接影响对电商促销活动的成果分析。 如果我们直接更新价格,就会失去与历史销售对比的便捷,如果不随单记录单价,更是丢失了与历史的对比。从设计角度,这很失败,失去了灵活性,扩展性。这样的设计,每次更换价格,都需要大量更新产品价格表和销售历史表,对已有的商业活动造成干扰。更好的办法是,增加价格的有效使用日期。比如在这段时间内这个价格生效,在促销阶段又是另一个价格。并采用视图(view)的方式去提供产品数据,而不是直接从原表直接读取数据,失去中间业务的缓冲。 对这类业务的理解,kimball 最有说服力,他的《Dimensional modeling》(《维度建模》)总结了几十个行业的通用设计模型,堪称数据模型界的设计模式。 第二个好处可不是人人都能意识到了。虽然 SQL 是拥有最长职业生涯的编程语言,比如与其一起出现的 VFP 大概 90 后闻所未闻,但显然没人一辈子愿意鼓捣 CRUD 。玩吃鸡的同学把你的 iPhone X 放下,家里有矿没说你。理解业务使你成为整个应用生态中不可缺少的一环。信息化的目的不是写代码,最终落脚点还是利润。我觉得二爷(邱岳)肯定能赞同我这话。 话说到这份上,大家可以明白,我们写SQL就是在通晓一个行业的数据流,资金流,做好大盘的监控。那么还有谁比我们更了解一个企业的真实经营情况呢,没有,完全没有。前提是,你要做对,要通晓。当你还只是把自己定位成一个码工,那真是大材小用。追逐SQL的技巧可以,但最终还是商业会支持你走的更远。你永远不可能20岁,30岁,总有一天你会被希望拥有开拓事业的本领,拥有可以指导后生的经验。到那时,技术经验就很泛泛了。甚至有可能技能上完全不如年轻人。唯一能给你树立权威的,还在于你在其他方向上能够走的多远。 2    快速实现: 很多朋友(包括我)有时候碰到需求,苦思冥想,要的是一口气把 SQL 从头到尾完整的,畅快淋漓的写出来。“Wow” 和漂亮的回车,就是憋着这口气的期待。 但现实无数次打了我的脸! 越是有这种想法,越是憋得时间很长才写那么一点。总觉得这里不好,那里不行,这里的变量名称写得不够爽朗,那边的 Pivot 写得不够优化。结果往往是一个上午就在那里纠结,什么都没完成。 你是不是也有类似的经历?不孤独 村上春树、海明威、博尔赫斯,从来写小说都是第一遍爽快的写下去了,一旦写得卡壳了怎么办,束之高阁,明儿继续。我这里想说的策略,大家都可以猜得到了。先把业务实现了再说,命名规则,变量申明,事务控制以及性能优化,统统先放起来。写好 CRUD 交上第一稿,存档,Over! 作家们要是等灵感来了再动笔写,我们哪能看到那么多有趣的故事。同样,我们写代码哪能等到全盘都考虑好了再动手呢。想到一个数据流,用到哪些表,直接就可以写了。等着等着就慌了,写着写着思路就来了。 比如实现下面的CRUD,你会花多少时间? image 如果一开始,盯着这图你开始考虑日志怎么记,检查用户是否单点登录,用户是否用促销券,订单怎么撤回,要不要控制并发,那么无疑是给自己加了很多戏,很多无形的压力使得你自己无法动手做,越想越宏大,越觉得自己做不来。在你迷茫同时,如果有个会议,有个热闹的新闻,一开小差,再想回到你的宏伟蓝图上来,就难了。 怎么办?抓大放小 此时,你要做的第一件事,就是快速去实现这么几个关键点的CRUD代码。比如购物车的增删改查,用户登录,填写订单信息,还有结单。等到这一系列操作都完成,你对整个业务流,数据流都熟悉了,第二遍再去增加附加的功能。 3    重构与测试: 终于,在第一版本时,你增加好了附加功能。实现了绝大多数的业务功能。 那这个时候,是不是可以交稿,checkin你的代码了呢?并不是! 如果此时你就认为高枕无忧,那会死的很惨。你会成为别人口中的“猪一样的队友,坑货……” 《巴黎评论》中,村上春树提到他的小说经常修改 4 - 5 遍才交稿,而且编辑还需要修改。我们一遍过的 SQL 就免检了?这个时候才考验你 SQL 真实功底和编码素质。 再检查命名规则,变量申明,事务控制以及性能优化。你会发现还有很多事情要做。 我知道很多朋友会这么写 : SELECT * FROM  ( SELECT *  FROM (SELECT * FROM BASE ) T1 )T2 如果继续放任你的项目里存在这样的代码,那项目很快就失控了。 至少,第一遍走读代码,我们需要完成格式上的美化: SELECT * FROM     ( SELECT *       FROM (        SELECT * FROM BASE      ) T1     )T2 这样即使代码不够优雅,别人在阅读这块代码时,也不至于骂娘。 第二遍动手重构的时候,可以考虑减少嵌套,或加上 CTE 封装嵌套: ; WITH BASE_TABLE AS ( SELECT * FROM BASE )SELECT * FROM BASE_TABLE 一开始我们能把 unpivot 写出来就很好了,然后嵌套一层做聚合,如下: SELECT Convert(Date,OrderDate) as OrderDate    , Sum(Amount) AS Amount FROM (    SELECT         OrderDate,        Unp.Amount AS Amount    FROM FctOrderAmounts    UNPIVOT( Amount for Type in(Shipment,UnitCost) ) Unp ) RSL GROUP BY Convert(Date,OrderDate) 这么一看特别清晰,但是信息量大,结构复杂,加上中间可能有其他字段或者Join,变得复杂,那我们至少还需再一次简化: SELECT     OrderDate,    Sum(Unp.Amount) AS AmountFROM FctOrderAmountsUNPIVOT( Amount for Type in(Shipment,UnitCost) ) Unp Group by Unp.Amount 再好比,有很多的关键步骤,其实我们可以拆分开来,直到一个存储过程完成一个功能,这样既完成代码简化,还可以提供复用的接口,还可以使得组里的小伙伴协同作战。一举三得,这样的事情才值得花时间。 最后,将所有的测试分支跑完测试,提交! 4    版本控制: 如果你的团队没有 git, SVN, TFS 这些 Source Code Version Control, 赶紧上一个。没有自动化部署工具,自己想办法整一个。都 2020 年了,别偷懒吧。 为什么一定要版本控制呢?这,应该在刚入门编程的时候就知道。 好比你觉得越发讨厌现在的自己,或是太胖,或是太文弱,或是太没文化,好想要一台时光穿梭机,回到15,16岁,重新再来。你会告诉自己多吃蔬菜和水果,坚持每天锻炼,坚持每天看书写字读报。 虽然我们不能实现穿越,但代码可以。使用上述提到的软件,就可以帮助我们回退到想要重新开始的那个版本,修正代码。 5     复盘记录 做好上面4步,对公司项目是有个交代了。但做这一步,才是对自己有交代。 就好比刚才重构的时候,提到 CTE, UNPIVOT , 代码简化的策略,可能因为一时灵感或责任心爆棚,反正你当时想到了,但你不及时记录下来,可能很久过后就忘记你曾做过这么神奇的操作。 所以,等你费尽心思写完很长的代码,一定要通过复盘记录下来,放到你的 blog, github, 等你以后碰到类似情况,却想不出来如何解,你可以随时拿出来用上。 写好SQL代码,素质当然远不止这些! 分享一个最近做的脑图,掌握了这些才可以说 SQL 编码入门了摸着你的良心,看看这个图,有则改良,无则加勉 image   image   image   image   image   image   image   上面都是个人实战所学,所悟。鉴于本人技术水平和经验,还有表达能力有限,难免有些地方写得晦涩,有些地方深入不够,希望大家能够给予反馈,感谢!  
真以为自己懂 Group By 了?
有读者朋友,翻出 2018 年的文章,吐槽了下排版。 是的,这种排版就连现在的我,都看不下去了。于是,我重新格式化下,发出来,弥补下对各位看官的照顾不周。 这篇文章主要细讲 Group by,各位能一眼看懂这条语句在做什么吗? SELECT OrderMonth,City,Sum(OrderAmount) AS OrderAmountFROM dbo.FctSalesMonthly GROUP BY  Cube(OrderMonth,City) ORDER BY OrderMonth DESC,City 前两天我认认真真在看 MSDN 的文档,我发现我们的开发,写的明细列表和求和运算, 是动态 SQL 拼起来的: 先查询了明细列表,放在一段动态 SQL 里面 然后在这段动态 SQL 的外层,包了一层做了求和计算 这时,我们上次文章提到过的资深开发(当然2021年现在人家已是互联网头部开发副总),过来了,看到我在看 Group By,觉得很奇怪:“ Group By 有什么好看的 ”。我就把上面那条 SQL 给他看了,他看了连连摇头。 我说," 我觉得我们程序里的动态 SQL, 这么写不是很优雅,于是想借助官方文档,看看是不是另有技巧。果不其然, Group by 的写法还是可以很炫!" 记录如下: /*---------------------------Gruop By 子句:1.Plain Group By 2.RollUp 3.Cube4.Grouping Set*----------------------------*/CREATE TABLE dbo.FctSalesMonthly ( ID BIGINT IDENTITY(1, 1) NOT NULL ,OrderMonth INT NULL ,City VARCHAR(20) ,OrderAmount INT );INSERT INTO dbo.FctSalesMonthly ( OrderMonth ,City ,OrderAmount )SELECT *FROM ( VALUES (  201701  ,'Shanghai'  ,100  )  ,(  201701  ,'HangZhou'  ,200  )  ,(  201701  ,'Suzhou'  ,300  )  ,(  201702  ,'Shanghai'  ,500  )  ,(  201702  ,'Chongqing'  ,600  )  ,(  201702  ,'Suzhou'  ,800  ) ) tmp(OrderMonth, City, OrderAmount) 第一个怪写法:Cube SELECT OrderMonth ,City ,Sum(OrderAmount) AS OrderAmountFROM dbo.FctSalesMonthlyGROUP BY Cube(OrderMonth, City)ORDER BY OrderMonth DESC,City 第二个怪写法:RollUp SELECT OrderMonth ,City ,Sum(OrderAmount) AS OrderAmountFROM dbo.FctSalesMonthlyGROUP BY RollUp(OrderMonth, City)ORDER BY OrderMonth DESC,City Cube 与 RollUp 的区别: 当只有一个维度作总计的时候,Cube 与 RollUp 实现的结果是一样的,都是分计加总计; 当有2个或以上的维度作总计的时候,总计方法发生了变化: Rollup 的有向性 : 按照 literal 顺序, 从右到左的向上汇总,直到聚合了所有的维度数据; Cube 的自由组合性:将参与的每个维度,都按照 Rollup 的汇总方式,从左到右,从右到左,分别上卷汇总 第三个怪写法:Grouping Set Grouping Set 将参与的每个维度作分类统计,但不包含总计,使用()表达式将总计包含进来; 将Cube,RollUp 组合在一个表达式里面 按照每个维度做分计,但是不计算总计: SELECT OrderMonth,City,OrderDay,Sum(OrderAmount) AS OrderAmountFROM dbo.FctSalesMonthly GROUP BY  Grouping Sets  (OrderMonth,City,OrderDay)ORDER BY OrderMonth  按照每个维度做分计,并计算总计: SELECT OrderMonth,City,Sum(OrderAmount) AS OrderAmountFROM dbo.FctSalesMonthly GROUP BY  Grouping Sets  (OrderMonth,City,())ORDER BY OrderMonth  怎么样,是不是很有意思呢?
零基础 SQL 数据库小白,从入门到精通的学习路线与书单
一,学SQL的苦恼 我观察了 865 个 SQL 入门者,发现大家在学习 SQL 的时候,最大的问题不是 SQL 语法,而是对 SQL 原理的不熟悉。 很多初学者,往往被 SQL 能做什么,为什么会有 SQL 这门语言给迷惑到了。他们用学英语的方法,去学SQL,结果发现什么都学不到,而且感觉越来越难学,难用。想想你为什么讨厌英语就明白了,每次翻开词典,从 abandon 学起来,不到 10 页就学不下去了,不就是因为太枯燥,太乏味了嘛。 假如我告诉你,听听音乐 yesterday once more, 看看电影 iron man 就能轻松学好英语,是不是觉得有趣多了?学 SQL 和学英语一样,选择枯燥的学习模式,必然带来不好的学习结果。我们追求的是轻松学 SQL 的方法。 二,SQL 的原理 SQL 的原理,超级简单,四个字,集合运算。 所有复杂的 SQL 语句,命令都是围绕着这个本质在做文章,添油加醋。 如果对集合这个概念陌生,没关系,高中毕业那么久,不记得情有可原。毕竟当时老师也不知道,风靡全球的 SQL 会基于集合理念发明出来。 集合,最简单的例子就是数组,比如 { a, b, c, d}。举个形象化的例子,比如大学全班同学,这是个大集合,按照性别进而可以分成男同学集合和女同学集合。 男同学有什么特征?高大,威猛,有胡子。 女同学有什么特征?白嫩,温柔,有长发。 就这样,我们有两个大集合了,男同学集合,女同学集合。回到 SQL 的原理上来,针对这两个集合,SQL 运算做的事情,无非就是这几样: 寻找身高大于180cm的男同学 寻找身高最高的男同学 寻找有胡子的男同学 寻找长了胡子,身高低于160cm的男同学 统计没有长胡子的男同学人数 寻找最高的女同学 寻找最白的女同学 寻找又高又白的女同学 寻找正在谈恋爱的男女同学 寻找曾经谈过恋爱,而现在又有其他男女朋友的男同学和女同学 你看,无非就是做这些事情。是不是对SQL立马就有好感了呢! 三,SQL 的数学基础 如此有趣的一件事情,教科书偏要说的枯燥(希望下面离散数学,集合理论的科普不会烦到你,不想看,完全可以跳过) 交换律(Commutative Laws): A ∪ B = B∪A, A ∩ B = B ∩ A 结合律(Associative Laws): (A ∪ B) ∪ C = A ∪ (B∪C) = A ∪ B∪C           (A ∩ B) ∩ C = A ∩ (B ∩ C) = A ∩ B ∩ C 分配律(Distributive Laws): (A ∩ B) ∪C = (A∪C) ∩ (B∪C)         (A∪B) ∩ C = (A ∩ C) ∪(B ∩ C) 等幂律(Impotent laws):  A∪A = A,A∩A = A 吸收律(Absorption laws): (A∩B)∪A = A,(A∪B)∩A = A 同一律(Domination laws): A∪Ø = A,A∩Ω= A A∪Ω=Ω,A∩Ø = Ø; 参考:http://math001.com/laws_of_set_theory/这罗列了详细的集合运算数学表达式。 所以,千万别被教科书上的数学符号吓到,而失去了学习SQL的兴趣。学霸别听我的,这些符号我知道对你完全是小菜一碟。 四,SQL基础问题 下面我们用SQL来回答上面的10个男女同学问题: 把全班男女同学登记到花名册上 找班上的八卦同学打听男女朋友关系,记录到恋爱关系秘本上 这里用到的花名册,恋爱关系秘本就是数据库里的二维表。先建这两张表,分别是 Students(花名册表),Relationships(恋爱关系秘本)。为了更加形象,用 Excel 展示下,这两张表分别长这个样子:   第一张表是 Students 花名册表,共有 6 栏,分别解释下含义:   StudentId: 用来标注学生的学号,一个学生只有一个学号; Name:学生姓名; Gender:学生性别; Height:学生身高,cm 为单位 Mustache:有无胡子; SkinColor:肤色 第二张表是 Relationships(恋爱关系秘本),共有 6 栏,分别解释下: RelationshipId: 男女朋友关系成立的编号,用来确立两人曾经处过或正在处于关系之中; BoyId:男生编号; GirlId:女生编号; BeginDate:关系成立日期; CurrentActive:当前是否还在恋爱期;Y 表示正在恋爱;N 表示已经分手; EndDate:分手日期 注意:栏位在数据库中被称为字段 用 SQL 语句来表达建表的命令: Students:Create Table dbo.Students(    StudentId Varchar(10),    Name Nvarchar(200),    Gender Nvarchar(1),    Height Numeric(4,1),    Mustache Varchar(3),    SkinColor Nvarchar(1)); Relationships:Create Table  dbo.Relationships(    RelationshipId Int,    BoyId Varchar(10),    GirlId Varchar(10),    BeginDate Datetime,    CurrentActive Varchar(1),    EndDate Datetime    ); 注意:字段旁边必须加上数据类型,规范数据长度,精度,比如 varchar(3) 表示 3 个字长。有关数据类型,可以参考参考官方文档对基本数据类型的定义和举例 五,在线运行SQL工具 推荐可在线执行SQL的工具: http://sqlfiddle.com/ 这款工具,有两大特点: 支持六大数据库,MySQL,Oracle,SQL Server,PostgreSQL,SQLite; 支持在线编辑及执行SQL,无需自搭数据库环境 简单了解下: image 整个页面分为1,2,3个区域。 区域 1 的编辑器用来创建数据库 schema ,比如建表,新建数据; 区域 2 则用来编辑和执行 SQL; 区域 3 用来展示 区域 2 中的 SQL 执行结果 使用这款工具,无需你手工安装数据库便可以完成本指南所有的 SQL. 完美!我们成功的完成了第一步。 六,SQL 简易实战 接下来,让我们为巧妇准备好精米。 INSERT 使用这条 SQL 命令,将添加全班同学的个人信息,包括姓名,学号,性别,身高,肤色,有无胡子,以及男女朋友关系,(当然这可不能让班主任知道。) INSERT INTO Students(    StudentId,    Name,    Gender,    Height,    Mustache,    SkinColor)    Values    ('001',N'陈冠奇',N'男',186,'No',N'白'),    ('002',N'谢堂风',N'男',182,'No',N'白'),    ('003',N'黄博',N'男',176,'Yes',N'黑'),    ('004',N'李少杰',N'男',172,'Yes',N'黑'),    ('005',N'徐少斌',N'男',163,'No',N'黑'),    ('006',N'张白芷',N'女',172,'No',N'白'),    ('007',N'张少函',N'女',163,'No',N'白'),    ('008',N'灵昆',N'女',181,'No',N'黑'),    ('009',N'夏平',N'女',158,'No',N'白'),    ('010',N'莫文丽',N'女',156,'No',N'白') INSERT INTO Relationships(    RelationshipId,    BoyId,    GirlId,    BeginDate,    CurrentActive,    EndDate)    Values    (1001,'001','006','2002-04-01','Y',null),    (1002,'003','009','2001-04-01','Y',null),    (1003,'005','010','2003-04-01','N','2004-04-01'),    (1004,'002','010','2004-04-01','N','2004-05-01'),    (1005,'004','010','2004-05-01','N','2005-08-01') 接下来我们用 SQL 做一些有意思的事情: 查找身高大于180cm的男同学: SELECT NameFROM Students WHERE Height > 180 and Gender = N'男' 查找身高大于170cm的肤白女同学: SELECT Name FROM Students WHERE Height> 170 and SkinColor = N'白' and Gender = N'女' 查找最高的女同学: SELECT NameFROM Students WHERE Height = (SELECT MAX(Height) FROM Students WHERE Gender = N'女' ) and Gender = N'女' 查找有恋爱关系的同学: SELECT Boy.Name as BoyName,        Girl.Name AS GirlNameFROM Relationships Rel    INNER JOIN Students Boy on Rel.BoyId = Boy.StudentId    INNER JOIN Students Girl on Rel.GirlId = Girl.StudentId 最帅的陈冠奇居然留胡子了,让我们记录下: UPDATE Students     SET Mustache = 'Yes'WHERE Name = N'陈冠奇' 帅奇留胡子的原因居然是,陈冠奇和张白芷前两天分手了,我们要记录这段关系的破裂: UPDATE Relationships    SET CurrentActive = 'N',        EndDate = '2008-04-01'WHERE BoyId = (SELECT StudentId FROM Students WHERE Name = N'陈冠奇') AND     GirlId = (SELECT StudentId FROM Students WHERE Name = N'张白芷') 任何的痛苦都离不开恋人的离去,张白芷居然去国外留学了,因此花名册少了这个人: DELETE Students WHERE Name = N'张白芷' 帅帅的陈冠奇,怎么甘心就此沉沦,他又找了莫文丽做女朋友: INSERT INTO Relationships(    RelationshipId,    BoyId,    GirlId,    BeginDate,    CurrentActive,    EndDate)    Values    (1006,'001','010','2008-04-01','Y',null) 好奇的小乐,查了查莫文丽的后台,发现她居然曾有过三次恋爱关系: SELECT RelationshipId FROM Relationships WHERE GirlId = (SELECT StudentId FROM Students WHERE Name = N'莫文丽') 恭喜!到此为止,所有的SQL基本操作你都会了。 七,SQL 晋级分水岭 有竞争力的技术肯定是要花大功夫来磨炼的,否则人人看一遍就会的东西,卖得出什么高价呢! 因此当大家都度过第一阶段之后,如何意识到还有第二阶段,第三阶段,甚至第四阶段就很重要了。那会影响你的长期发展。舒适区是留给平庸者的。 从技术角度,我们习惯将 SQL 数据库开发分为 4 个阶段:入门,初级,高级,资深。 注:技术角度可分这四个阶段,并不代表做到资深,你一定能年薪百万。可千万别这么想,很多朋友咨询我的第一个问题是,“黄老师,以我的水平,能要个30K不?” 这我不打包票,能做到什么收入完全看你水平和天意(没错,很多时候,赚大钱靠运!) 在本文中,我只谈 SQL 技术水平的晋级,不谈其他。 上面的入门指南,相信你看完之后,写 SQL 基本掌握了。这一阶段过后,就是要通过反复的操练这些 SQL ,直到你熟透这些 SQL 命令和语法。 帮你度过这阶段最好的方式,是什么呢?刷题。没错,简单粗暴。这里推荐牛客网,你可以试着去刷: image 左边是题目,右边编辑 SQL, 点击【保存并调试】即可运行 SQL。系统会提示,你的答案是否通过! 刷完这 60 多道题目,相信你的感觉一定很好! 再推荐基本经典的小书,可以兼顾理论与实战,具体看哪一本,依据你使用的数据库软件来: 《SQL 必知必会》(综合)《T-SQL Querying》中文《T-SQL 查询》(SQL Server适用)《Oracle编程艺术》(Oracle适用,只要是 ASK Tom 主编 Thomas Kyte 的著作不会错) 八,SQL 数据库深入学习 当你跨过了第二个阶段,所有的 SQL 命令都会了,看到 SQL 题目也有自信了,自认为天下数据,为 SQL 皆可取的时候,你应该来挑战下高难度了 现在你的角色是河南郑州大学的学生会主席,所有学生的信息都归你管。别小看了这学生会主席,2017 年郑州大学在校人数总共有7.26万人。此时,你再查找个又白又高的女生,嘿嘿…别想歪咯,数据查询就会很难了。看你急不急! 怎么办呢,SQL 命令你也玩熟了,但每次查个人都要花费个20,30秒,查个交友记录,快则1,2分钟,慢则半天! 此时,你该修炼下 SQL 性能调优了。而 SQL 数据库开发真正的挑战才正式开始! 要研究数据,就要从体系着手,我总结了一下目前正在使用的 SQL Server,它的知识点可以用 20 张思维导图来表达。而 SQL 性能调优正是与这 20 个知识点息息相关。 这20个知识点可以在这篇文章中找到: 20 图归纳 SQL 数据库知识点 如果你使用的是其他数据库,那么一份详细的数据库体系图,是必不可少的。它就是深夜里的北极星,为你照亮了前进的方向。 当然你跟我一样,对纸质书情有独钟的话,这里推荐几本: 综合类的数据库调优书: 《数据库索引设计与优化》《数据查询优化器的艺术》 Oracle 相关调优书: 《Oracle Concepts》《Oracle 体系架构》《Oracle DBA 手记》(云和恩墨出品都是精品) MySQL 相关调优书: 《高性能 MySQL》 SQL Server 相关调优书: 《Inside SQL Server 》系列《T-SQL 性能调优密集-基于 SQL Server 2012 窗口函数》 这些书看下来应该会耗掉你2-3年的时间。当然,如果你愿意痛饮咖啡,用007(一周工作7天,每天连续12个小时)的速度来看书,那也可能1年左右就够了。但不建议这么干,身体健康比什么都重要! 九,SQL 人的职业生涯 当任何超过5秒的SQL,在你手下都变成了秒出时,你可能会昂天长啸,“老子终于天下第一啦”。不过且慢,你别忘记了,你还是会被项目经理催着干活,比如改个字段,改个需求,改处bug等等。此时,你恐怕最需要的是,一个得力的左膀右臂,一个能打的需求分析,更重要的是搞定灵活多变的业务变更,甚至你要挽起袖子,自己盯着业务变更,发布更灵活适配的数据模型。此时数据建模,业务管理,数据治理,数据挖掘,安全审核等等都来了 此时,正是你职业生涯的分水岭。 若你觉得你就是干不了开会,扯皮,来回折腾的活儿,就喜欢指挥机器干活,那就选择偏技术的方向,比如运维DBA,数据库研发。从事这条路线,意味着你要懂的更多的边角料知识,比如操作系统,网络协议,自动化编程,架构设计,分布式计算,云计算等。这些知识资料早已有现成的资料可以参考,只要耐得下心来学,一定有成功出头的那一天。但也极其容易放弃,因为东西实在太多,太杂,而且有些边缘性的学科,一时半会你还用不到,比如网络协议。但如果不懂网络协议,碰到数据库安全的问题,你就容易败下阵来,死的很惨。你的任何一个缺点都会伴随每一次的故障而被无限放大,被很多同事视为毫无战斗力,让你自己都开始怀疑自己,鄙视自己。所以干这行,首先要有一颗强大的心脏,在无数的口诛笔伐之下,你要坚挺自己的信念,不要怀疑自己的判断,丢失基本判断能力。 且运维不仅仅是个智力活,还是个体力活。 白天所有运行在数据库上的应用都好好地跑着,一般不会有大问题,看上去 DBA 很轻松。但是一到晚上,你们的活儿才刚刚开始。有索引重建,磁盘告警了;有 ETL Job 失败,数据库卡住了;有集群节点宕机,需要更换机器了,等等。总之 7*24 的重活,累活都来了,而且是必须赶在天亮业务开始之前修复。有时候,那些夜猫子也很凶悍,大晚上非还要抢点货,点个夜宵啥的。一下单,系统崩溃了,你的美梦也就泡汤了。或许你感冒了,正在休息,不行你得起来;或许你正在电影院看着复联呢,不行,你得回公司。总之,为了工作,为了抢修数据库,你必须第一时间赶赴现场。多少年轻的DBA都曾在高压现场,留下了人生中第一口鲜血。 你说DBA那么累,傻子才选,你走第二条路,扯皮! 恭喜你,你选了一条可以通向公司高层的路。也许是 CTO,也许是CEO。通过做数据运行,数据产品,或者数据分析,你认识了全公司上上下下的大小头目,今天与运营总监吃个饭,明天与生产总监喝个咖啡。总之他们提的一切需求,都希望你全部搞定。你说你团队人手不够,公司不肯招人,跟他们有什么关系?你说你excel就能实现的功能,为什么非要整一套炫酷而无任何实用价值的可视化报表分析软件?他们就想要!你说你这个需求要1个月才能做好,他们拍着你的肩膀说,“你可以的,要相信自己。俩天,顶多两天就能搞定!” 你带着这么多需求回到你的办公室,看着满脸青春的这些小伙小姑娘们,刷着微博,舔着朋友圈,个个脸上洋溢着热焦玛的香气。你好忍心让他们陪你加班到凌晨,还自费打滴滴么! 好不容易,2个shot的星爸爸热焦玛下肚,你来了精神。熬到凌晨2点,连续2,3天回家看不到老婆/老公的笑脸,还给自己打气,一切都是为了家庭。等到交上报告一看,“这里数据改一下,口径要和运营部统一”,“哪里整个版面太丑了,影响公司形象,再修得漂亮一些。还有,这些报表都要加上权限,不能让其他部门人看到。好吧,明天下班前我们再谈”。 好吧,这回你只得厚起脸皮请小朋友们一起改了,晚餐自费交了KFC全家桶,全组加班人员,一律打车报销。你默默的在群里丢了一个又一个红包。收到一个又一个的“老板帅气,老板再来一个,宝宝没抢到” 终于赶在deadline之前做完了,“怎么样,我说你可以的吧,看好你哦。这样,我们昨天几个人会后碰了下,发现你原来的设计也挺好,只是稍微还要再改改;还有,数据需要增加导入导出功能…” 如此反反复复,来来回回的折腾,某天你突然感慨,原来“别人用了一年,真的学到了我十年所用会的SQL技术”。此时,你会坚持自己的选择是对的吗,假如不幸的是,公司投资人撤资了,你所熟悉的环境没了,怎么办? 因此,在这些零零碎碎的项目锻炼中,你能否总结有效的规律,提高自己业务沟通的能力,掌握数据治理的策略,严格把控项目管理的进度,都成了你的必修课。一旦松懈和迷茫,可真是万劫不复! 十,SQL 数据库必读书单 如果你选的是 DBA,除了各家数据库文档要通读之外,还需要补充云计算相关: 《数据库系统实现》《云计算通俗讲义》《让云落地:云计算服务模式》《云计算架构技术与实践》《Kubernates权威指南》《微服务架构设计模式》 如果你选的是业务设计(无论是运营,产品,数据分析,尤其是数据分析师),那么这些书,肯定是要读一读的 Kimball 的系列书: 《维度建模权威指南》《数据仓库与商业智能宝典-成功设计、部署和维护 DW/BI 系统》 业务场景解决方案: 《计算广告》《推荐系统实战》《决战大数据》《大数据之路-阿里巴巴大数据实践》《企业IT架构转型之道》《设计数据密集型应用》 大数据系列: 《Hadoop 权威指南》《Spark 权威指南》《Hive 编程》《Hive 实战》《Spark 高级数据分析》《Hadoop构建数据仓库实践》 以上就是基本路线了,能在5-10年搭好这个基础,你定当可以独当一面。
从美团外卖的数据仓库建设中,我学到了什么?
前两天,转载了美团外卖的两篇数据仓库文章。第一篇讲他们的实时数据仓库建设,第二篇讲离线数据仓库。两篇文章都发人深思。于是,我花了点时间,拆解了各自的项目实现。   这是第二篇拆解,针对离线数据仓库,美团外卖讲述了他们的玩法。这篇感觉更接地气,和传统数据仓库项目,贴合得更紧。换句话说,这次的离线仓库,不仅对互联网行业具有借鉴意义,对非互联网行业,同样也具有参考价值。   传统数据仓库,数据基建包括了 ETL, 建模和可视化。从数仓范畴的概念上入手,美团外卖的离线数仓,也同样是这些。但形式、落地与逻辑,稍有些扩展。   比如,做传统行业的数仓工程师,都知道数据的组织与存储,以关系型结构化数据为基准,以维度模型为扩展。强烈的二维属性已经刻在我们脑子里。即使在二维中硬生生增加多维的扩展模型,本质上还是二维存储。   但在互联网业务中,比如美团外卖。很多业务数据开始有了离散的结构。就好比日志。一个用户的订单,来来回回有多种增删菜品的组合。这样的行为数据,需要入库,对于关系型数据库,极为不便处理。   再举一个例子,用户与商家,用户与用户之间的评论,又是离散的。这样一个主题帖,就像一棵树结构,最好的办法就是将整棵树都存起来。   综合美团外卖的业务,可以将数据分为两部分,一部分是事务性关系型业务数据;另一部分是离散结构,半结构化的业务数据,比如评论,用户行为日志等。   架构   与上篇实时数仓一样,这次的离线数仓,也是先从业务架构入手分析。     美团的业务图,做得十分清爽。每个业务链路,规划的都很明晰,一目了然。从用户下单,商户上单,骑手接单配送,销售运营,方方面面都考虑在了数据这条生态链上。   业务架构清晰了,数据架构自然也就跟上来了。     在四个数据层,层与层之间的数据交互,都有不同的工具实现。正因工具多变,监控这些工具的数据流是否正常,也是大事。所以,数据流及其流监控手段,还需要加在这幅全景图中。比如 Sqoop, Flume, 以及传统的 ETL 工具。   这里值得关注的是,美团将大量的 Hive ETL 工作都转移到了 Spark 上面。由此可见,将来的趋势,必将给与 Spark 更多机会。那么 Spark 与 Hive 相比,会有哪些明显的优势呢?文中指出,至少有 3 个:   算子丰富,如果是用 Spark 计算库来说,那是真的丰富。比如机器学习算法,HQL 毫无优势。   缓存中间集,不用像 Hive一样,每次都利用硬盘来缓存,是 Spark 最大的优势。   资源复用,申请的计算资源可以重复利用。这先按下不表,因为我也没理解,这优势的本质,在哪里。     转型   美团从传统的数据仓库过度到现代互联网主流数据仓库设计,经历了很长的路程。那么在这些历程中,哪些是关键点,为什么会做出如此的技术选型?看看美团怎么说。   刚开始,2016年以前,美团业务量不大,但竞争激烈。为了配合业务,堆人完成开发。造成的局面一度尴尬:整体开发效率低;统计口径不一;垂直切分技术资源,造成人力浪费。   来看下当时的美团技术架构:     分层也很明晰:ODS\明细\聚合\主题\应用。    ODS层, 这里特别说明下。从各个数据源汇总过来的数据,都会先落在ODS层,有一定的清洗,意味着数据有筛选,更干净,更符合标准格式。   可以看到美团数仓1.0的分层,是以总部+城市来展开的。这种分层,造成的重复计算是毋庸置疑的。很多计算指标都是重合的,总部和城市本身就是地区维度的上下层级关系,完全没必要分开。所以这种分层必须按照业务重新划分。   于是美团 2.0 时代就改变了:     这回,就彻底把分层做足了。按照应用来划分层次,并且在每个层次上又再分层。   其实这里有个很重要的转型时间点。并不是一上来就要精细化开发,把每个主题都安排的妥妥当当。还要看业务发展的势态。   业务早期,稳定性和持久性,还没有突破,过早进入精细化数仓建设,是不合理的。此时要做的事情,完全是辅助业务的开展,在没有准确供给业务所需数据时,就要上一些高大上的数仓指标体系建设,那是浪费资源。   所以,数仓的建设还要围绕着业务去开展,强烈关注业务的开展状态。   一旦业务稳定,势态良好,那么应用就会越来越多,这个时候开展数仓的分层设计,就会顺理成章。     分层   一切围绕业务应用而生,而业务应用,也再一次的分层:业务引导(数据挖掘,推荐)主题;分析(运营分析,财务分析)主题;业务主题(以事实业务过程为基础的分析)。   总的来说,这一层指导和铺垫了底层数据的分层建设,该层也叫主题标准。   这些主题标准切分开来了,但实现这些主题切分的人,还没有定义出来。到底是业务架构,还是技术架构兼任?   不管是谁来做,这样的融合必定是不可少的。懂技术的,并不一定懂业务,懂业务的,不一定懂技术。所以必须有人来双向融合。这大概就是架构师要做的事情。   主题区分开来了,技术的定型也就确定了。以前大家都是拿一块业务,还有可能是同一块业务,垂直的在各自造烟囱。看上去大家都是全栈,实则浪费资源。   此时,将人力资源分层,做建模,做数据应用,团队的资源就不会浪费在同一块地方。比如之前,数据组的每个人都在做商家统计,不同的是一组在处理总部来的需求,二组则在处理每个城市来的需求。其实有些共性的部分,大家可以放在一个模块来完成,不必各自为政。之前的这种团队划分,称之为垂直划分。   而美团数仓2.0,则更多横向划分。从建模到应用,每个段切分,专人专做整个链路的某一段。   从主题到最终的物理层实现,需要两组人马不停的融合。一组人负责不停的处理业务需求建模,另一组人负责物理数据的建模。这两组人一定需要在某个点上达成一致。所以分工标准就出来了,数据应用组和数据建模组。       刚才美团数仓1.0,数据分成了四层:ODS/明细/聚合/应用。现在需要将数据分得更细,做更多的解耦。   其实也可以用接单的stage1,stage2,stage3来划分。但每一层做些什么,当然还是要了然于胸。   比如stage1,整合多数据源的一致性建模,完成数据维度,事实组合。stage2,用来完成聚合汇总,进一步按照粒度划分,完成年月日级的聚合。至此,一个中央数据仓库就完成了。stage3,按照业务单元,做数据集市。比如营运,销售。这样提供给数据应用层,就有了完整的数据源。   在数据整合层,要注重排查的两个概念,一是宽表,二是聚合表。宽表与 kimball 的 fact table 不一样,我们通常所说的fact table,实际上仅仅是明细表的统称,而宽表,则是把相关的事实表,都整合到一起,这样的好处,一是加快速度,二是一次查询更加全面。   举个例子说明下大宽表的定义:选定实体对象(比如订单),圈定分析对象(比如订单头,明细,状态,订单召回等),构建宽表模型(通过订单id,将这些表关联到一张表)。     最终的应用层,会简单很多。主要是选型,也就是针对业务数据应用,会选择哪些数据库技术,分析引擎技术,还有报表计算。归纳起来,离不开存储,计算,可视化。     缺陷   美团数据仓库2.0,还是有很多缺点。如下图:   在数据集市层,会过度膨胀。因为层与层之间一旦分割,便会有不同的想法。今天她要这个指标,明天他又要那个指标,其实他俩指标都差不太多,但就是要设计两套,最终导致数据集市层膨胀。而数据仓库3.0就是来解决这样的问题。     说实话,这是我从来没有想到过的一层。使用建模工具替代人工开发。因为这套玩法,我从来没用过啊。这大概就是美团外卖的先进之处。 文章还提到另一个数据仓库方向是数据治理。它分享了三个小点:数据开发流程,数据安全管理,资源优化。这一块也是我的弱项,下回,我就来盘它!
大数据杀熟?我从银行数仓项目学到了什么
  这篇工行发布的论文,看得我笑出天际。   | 公众号后台回复“银行数仓”,下载论文       数据分析当下十分流行,但能把数据分析出花来的,本文算的上先驱。    做数仓的朋友,年底汇报时,写不出总结,在老板面前无功可表时,学学这。     乍一看,文章跟数据仓库技术相关,本以为讲述银行数据仓库项目。但顺着往下看,却是一场大戏。       文章开始,就交代了数据仓库,对银行的重要性。没有啰嗦,而是直接给出答案:中间业务利润,包括理财,贷款和信用卡。          通篇读下来,有三个有趣的结论,可能颠覆大家的认识。   同时也证明,优秀的数据分析,能更好地反哺业务。         第一张图,年龄与可用资金的非线性关系。   颠覆一般人的想象,不是年龄越大,可用资金就多,28岁是个可用资金分水岭。为什么30 到40岁的人缺失了银行资金?   两个因素:有娃,有生意!   有娃,家庭支出偏高;有生意,创业后,大把现金烧出去,存不住银行。   要把收益率低的长期理财推给 30岁的中年人,没戏。反过来,要贷款产品推给 28岁以下的年轻人,人家根本不会睬你。         具有实操意义的,还有第二张图。   占据可用资金前4位的职业人,都可以叫做“私营业主”。这部分人手握大量资金,完全可以兴风作浪。     第三个有趣的结论,没有图,但表述却十分奇特。大家知道为什么银行客户经理,都是女生?   文章给出了答案:据数据分析,男性比女性更热衷于理财。那我就好奇了,姑娘们,是真的?         文章看得很开心,同时也留给我一个问题,也是提给银行的。   支付宝有俩功能,花呗和借呗。分别对标银行的信用卡和储蓄卡。   一开始,我认为和信用卡、储蓄卡并没有不同。该还的钱一分不少!   但,细读第一段,似乎又有点不同。   银行推出信用卡业务,绝对不是救济穷人,做善事。但银行会对个人的一点分期利息有兴趣?显然不是。   我把文章开头读了好几遍,才明白,银行要的是些交易信息,基于这些消费习惯,进而推算信用,理财产品,还有房贷风险。   这些信息需要个人消费记录。   花呗和借呗,正是把这层数据给截胡了,让银行只有一个数据的快照(snapshot)。丢失了数据消费的帧数,自然用户画像就会模糊。   支付宝就像一个银行,有完整的收入和支出体系。比如做淘宝店家,收入不再存银行,存支付宝,用来支出各类消费。   本文中提到,银行依靠这些数据,对“私营企业主”做理财产品,而“花呗,借呗”直接吃了他们,此时银行,完全失控。业务萎缩,自然而然!   我知道,银行是在乎的,怎么办? 银行怎么挽回看似失宠的市场? 这是留给每个数据人的难题,充满想象空间!   大数据杀熟,是数据营销利器,对数据工人,是好事。学而有用武之地,但数据工人,也是消费者,那把杀熟刀,正是自己亲手递上的。想想,挺无奈!   --完--    
SQL数据库性能胯了,换 SSD硬盘就能解决问题?
  2010年,上海张江,传奇广场。 这里汇集了各路公交车,是张江男女必争之地。打工人最怕的是什么,不是996,不是007,最怕挤公交。 背着5斤重的Dell, 顶着烈日,狂风,暴雨,满怀希望冲向车站,换来的却是,屡次被无情抛弃,这种感觉,谁经历,谁泪目。   BMW, Bus + Metro + Walk 这种通勤方式,贯穿着张江男女的 30岁!   “让一让,排好队!” 车站调度员每次总要对落下的我们补上一句。语气略有生硬,但嗓门绝对让每个幻想插队的人,都听得到! 这个车站不大,总共有 4 个车道,停歇了 7-8路车。618,636,张江1路,张南专线,张江环路,孙桥1路,等等   调度员拿着硬板本,按时走过 4个车道,招呼该发车的司机出发,并叮嘱下一班次车准备。每次看到有调度员过来,一些略显颓废的男女,立刻就来了精神。因为占座,在这里也是门学问。 有时,该发车的车道上,没有找到司机,调度员还会大骂一声,气哄哄跑去下一车道招呼其他司机。   现今,基本上已经看不到调度员的身影了,即使看到,也是他偶尔出来抽根烟。随手还带着对讲机。 没错,现在的调度口令,不需要口口相传,对讲机一开,司机就跳上车了。调度员手里的硬板本,也换成了电子公告牌,就像华泰证券交易所的红绿看板一样,哪个车道,下一班发什么车,走马灯似的亮着。 扯远了,拉回来,你们没有走错片场,我不是在写小说! 上面这个转变,看似是电子信息化转型,实际上也胜似机械硬盘过度到SSD(固态硬盘)。 (摘自网络)   这是机械硬盘的构造图。 磁盘从里到外,分出很多磁道。磁道上挨个排满了扇区,每个扇区的存储空间固定。 磁头围绕着磁道转一圈,该磁道上所有的扇区数据就会被读到内存里。这样的读叫做顺序读(顺序IO)   如果一个磁道把同一张表的数据都存储起来,这样的顺序读,效率是最高的,只需一圈,读到所有的数据。 但事情并不那么简单。首先,数据的写入,是无序的。比如一张表有100万条数据,这100万条数据,并不是连续地写入,而是每天写一点或者每过一段时间写一点。写入的同时,还会有表B,表C的数据,挨着表A的某些数据,一起写入到相邻的扇区中   那么,同一个磁道上,就会出现既有表A的数据,还有表B,表C的数据。这样磁道上的数据,就会有了断层。表A的数据自然也就不会连续存在一个磁道上 有可能磁道1存上那么2万条,磁道2存上那么10万条,磁道3存上那么5万条,零零散散分布在几十条不同的磁道上   所以,与理论上完美的顺序读,不一样的是,实际中,大部分表的数据是随机读(随机IO) 完全读取表A 100万的数据,可能需要在几十条不同磁道间来回切换。而读取时,把其他表数据读取出来,貌似做的都是无用功。   因此,传统的优化,有一个方法就是磁盘碎片整理,目的就是提高顺序读的机会。当然,建立索引也是提高顺序读的好方法。但OLTP系统中,充满了随机写,索引也必须进行碎片整理。   以上是传统磁盘的工作方式,也列举了对数据库操作的影响。那么SSD的出现,又对数据库的优化产生了哪些影响呢?   同样,先要讲下 SSD 的结构,正是由于这个结构才对数据库产生了深远的影响,而且这个影响不仅仅有正面的,还有负面的。 SSD不再是简单的一个存储介质,而是一整套微小的系统。包含了内置的芯片,缓存还有存储介质。 (摘自网络)   当操作系统发送读写命令时,SSD就像是另外一台计算机,判断内存是否有缓存,根据FTL提供的映射表,从介质上读取/写入数据。 这些组件里最有意思的是 FTL(Flash Translation Layer), 即闪存译层。它的主要功能是提供一份映射表,从闪存(Flash Memory)的物理地址映射到逻辑地址(Logical Block Address)   那么为什么SSD的FTL这个组件能力那么强,它对比传统的磁盘到底快在哪里?   再来回忆下,传统的磁盘,一次随机读,耗时较长的两个步骤,一是寻道;二是等待 寻道,即寻找到特定数据的磁道; 等待,在磁头定位到磁道后,等待磁盘将扇区旋转移动到磁头下,这个延迟时间称之为等待时间。 正是由于这两个限定因素,导致机械磁盘的效率,顶配了也就那么高了。   那么SSD的电气化闪存,为什么就会比机械磁盘高那么多呢? 这个原理,开头的小故事,已经说得很明白了。 调度员一个个车道走过来,这是寻道,万一哪个道,到点了,但还没有车辆就绪,就得跨道,找下一个可以发车的道;走到当时可以发车的车道,呼叫可以发车的司机,叮嘱司机可以发车了,这是旋转磁盘时的必要等待。 这一来一回,中间出点叉子,都会无数的等待时间。所以经常晚点,当时那叫一个痛苦。   感谢电子化时代的到来。现在的发车远比那时简单高效。 每个司机都配备对讲机,时间一到,调度根据排班(FTL)呼叫司机发车,省掉了寻道和等待时间(都靠人工)。调度员借用电子信息手段,瞬间就能传达发车指令。   但是,SSD就全是优点吗,绝对不是。它有个巨大的缺陷,就是写入放大。   这个缺陷和数据库的预读能力,其实有些类似。数据库的读取,一次读取并不是一条一条的读数据,而是一页乃至一个数据区的读。明明只要一条数据,对不起,存储引擎会读取上百条数据给你 所以有时看到数据库内存使用量极大,总想着给它加点内存,其实完全没必要。它就是一贪嘴的小鬼,给多少就吃多少,不会嫌多。   SSD 也有类似的机理,不过不是在读取上,而是在写入中。读取可以完全做到一个页一个页的读,但写入就有分歧了。 当一块新的SSD硬盘拿过来,开始写入数据时,很快。见缝插针,哪儿凉快就哪儿呆着。不用思考。但是一旦SSD写完了,需要改写数据时,麻烦就来了。 它的改写原理是读取一个数据块的数据,而不是一页数据。这一个块上的数据,可能包含了几十页的数据,而且这些数据都不需要修改。等到这些数据读到内存后,就选择需要修改的数据,逐条修改,然后写回闪存。 写回闪存时,最大的延迟在于,需要把原先的那些读取的数据页都擦除,然后再写入。 这种情况,称之为“写入放大”   所以你家的SSD是不是等过了一段时间,发现明显的速度慢了?就是这个理,在等待垃圾块(脏块)的回收。   像这种情况,存在于早期的SSD中,那时,SSD确实不适合用来做日志盘,只能用来做无伤大雅的快盘。后来优化了FTL算法,比如进行了碎片整理,垃圾回收,并且容量也日渐扩大,慢慢SSD的应用场景也就多起来了   在OLAP领域,也就是数据分析,数据仓库和报表系统中,SSD的应用相当广泛。这类应用写入次数较少,但是读取的吞吐量极大。 有人说,SSD 的写入放大缺陷,不适合用来存储 redo log,其实也不是那么全面。有些 redo log不大,而 SSD 现在的容量极大,一次性写完 SSD 而引起性能抖动的概率并不大。而且有了FTL的优化,SSD完全有能力在日志写满前,就开始做垃圾回收和碎片整理。 日志文件的访问(主要是写入),通常可以看做是在环形跑道上跑马拉松。一圈跑下来,运动员丢弃的水瓶,补给堆满了跑道。为了可以让运动员顺利跑第二圈,第三圈,必须把这些水瓶等杂物清理干净,越早处理,对运动员就越有利,无感知最好 SSD也一样,在装有 redo log 的SSD写满之前,就对SSD做闪存块的整理,把空间都让出来,才能让数据写入更快   那么 SSD 是不是就一定比传统的存储系统优越呢,其实也不一定。 比如组RAID 磁盘阵列,拥有的读写能力就不比 SSD 差。 通过表分区,将数据打散,放在不同的硬盘上,这样读取数据,就能有效利用并行。 拿 SQL Server 来举例: c 盘:操作系统 D 盘:SQL Server 数据文件 E 盘:日志文件 F 盘:SQL Server 数据文件 G 盘:tempdb 文件 其中,D和F盘做了 RAID 10, 当100G的数据表做了分区,将其中的时间戳,按照奇偶年,分别存储在 D/F盘的数据文件中。 这样,当需要查询连续两年的数据,就可以充分发挥D/F盘并行查询的效率 只要成本可控,随着数据盘的增多,分区可以打得更散,并行的速度就越快。这对于 SSD 来说,成本上是不过关的 用SSD替换机械硬盘,现阶段有好处,但瓶颈也不少。更多应用,还是要在实际中做出一种平衡。 编辑于 2021-10-19 19:31
推荐几本数据库基础书
  这两天费劲心力,写了一篇技术文《SSD怎样影响数据库性能》。选这个主题时,我就预感,阅读量不会太好。结果也不出我所料,比平时低一半。   我一直佛系运营公众号,(圈里跟我差不多体量的小伙伴,靠号已年入过百万),我承认自己没有财商,所以倒也不是那么急的去赚这个收入。但看到辛苦写的文章,阅读量如此惨淡,还是不免有些伤感。   当然今天也不是来说矫情话的。把自己的惨说给读者听,以此来博取同情,向来都不是我的作风。我只管写文章,研究课题,分享出来。如果能帮到一些朋友,那是最好,若没有帮助,那至少也取悦了自己。   况且在写文章的过程中,我认为受益最多的,还是作者本人。   比如在写这篇《SSD》的时候,我大概花了10多小时找资料,反反复复对其中的细节问题推敲,不断问倒自己,再重来。期间一度产生了放弃的情绪。   好在这个时代,有太多资料可供查询,有深入浅出的书,还有扣细节的论文,还有很多热心的朋友做了动画,图解和视频。想学不好,都难!   熬过了这段时光,对其中很多原先理解不深的逻辑,有了更一步认识。所以技术文要写出来,才有进一步探索的冲动与激情!   说说我想写这篇《SSD》的由来。有天晚上,一位读者找我商量,要给他的小程序做一次秒杀和团购的活动。用户数大约在15万左右,套上团购估计会有30万的流量。   于是我就在网上开始找提高云数据库性能的办法。找着找着,有一篇论文《MixStore: Back-End Storage Based on Persistent Memory and SSD》引起了我注意。   该论文是国家重点研发计划项目,既然提到了SSD,那么我就忍不住自问,SSD到底怎样影响了数据库性能的呢。毕竟2013年的时候,淘宝的DBA团队对SSD有着不一样的看法,认为SSD在充当快速持久性存储(比如 redo log)时,还比较吃力。   所以就来了兴趣。主题有了,我该写成什么样呢。是一堆参数 + 对比曲线图? 还是画一些数学公式,看上去只有神才能看懂的原理解析。   都不是。写文章到如今,基础东西要写得那么公式化,那就只能劝退大家了。所以我还是尽量用大白话,写了一遍。但东西实在太多,对于文章结构是个不小的挑战,虽然几经修改,但很多地方还不是很满意。   写这么一个大的主题,乐趣在于探索。我拿到的第一本书是《大型网站技术架构-核心原理与案例分析》李智慧老师所著。   广告 大型网站技术架构 核心原理与案例分析(博文视点出品) 作者:李智慧 京东 购买 本书一大特色,就是全。你能想到的网站性能影响因子,李老师都替你想到了,你没有想到的,本书中也有。   比如网站的基本架构模式,怎么做分层,缓存怎么配置,安全策略怎么做;前端性能怎么优化,应用服务器与存储怎么优化,高可用该怎么搭建。   可以说,本书包罗万象,绝对打开你的眼界。但也有遗憾的地方,那就是面面俱到了,细节就丢帧。比如SSD,基本没有渗透到原理细节。   但,没事。我就是书囤的多。这本书没讲,《操作系统概念》总讲了吧。   广告 操作系统概念(原书第9版) 作者: 亚伯拉罕·西尔伯沙茨(Abraham Silberschatz) 彼得 B. 高尔文(Pet 京东 购买   翻到存储那章。好嘛,连磁盘运转物理图都给画出来了。喏,就是这张:     那这样的机械原理,是不是跟车站调度很相像?所以就有了《SSD》开头那一节。   但本书也有遗憾,没有讲到SSD的内部结构。于是我又拿起了《高性能 MySQL》   广告 高性能MySQL(第3版) 作者:Baron·Schwartz Peter·Zaitsev Vadim·Tkachenko 著,宁海元 周振兴 彭立勋 翟卫祥 译 当当 购买 这本书可以说是所有数据库人员的必备书了。如果你还没看到,强烈推荐读一读。   本书讲 SSD 与机械硬盘的对比那章,总算搭边了。对于 SSD的写入放大,垃圾回收,组RAID,都开始细节化的讲述了。   虽说这本书已经从原理上讲解的差不多了,但距离理工男的理解,还差点意思。因为它缺少了用实验来证明SSD的优越性。   于是,我把目光投向了《数据库索引设计与优化》.     本书最大的特点就是一切用数据说话。就算是每一次随机IO, 都能测到ms级。专业性非常强!可惜,现在各大网站都买不到了。如果你对用数字说话,非常感兴趣,建议读一读。   除开这些书本,我还读了一些论文。比如我朋友圈发的这份:       读这些论文的好处,就在于可以去了解业内对SSD的一些研究,比如 FTL 芯片算法的更新。图上这位美女,是老东家的一位同事,素未蒙面,但肯定一起在饭堂吃过饭,偶遇过。所以专门挑着放上来。   《SSD》这篇文章,我大约花了15个小时左右,10个小时看资料,5小时写作。   有图为证:       搜集资料最费时,你看,0622代表6月22日,一直到0703,都在查资料。同时也最快乐。了解那么多前沿知识,满足感爆棚!   文章是在这个环境下写出来的:     一个专门用来写字儿的地方,两张白纸就是选好的主题与研究进度。有时候没什么都不做,仅仅是蒙头写东西,放上一盘班得瑞的曲子,磨上一杯手冲,足够消磨一个下午了!     --完-- 编辑于 2021-10-14 10:54
数据仓库,就不是数据库了吗?
来源:https://www.guru99.com/database-vs-data-warehouse.html   本来摘自guru99, 粗略过了下,非常适合入门的读者阅读。对数据仓库好有更清晰 的了解。如果大家有需求翻译成中文的,可阅后留言。   What is Database? A database is a collection of related data which represents some elements of the real world. It is designed to be built and populated with data for a specific task. It is also a building block of your data solution. In this tutorial, you will learn   What is Database?   What is a Data Warehouse?   Why use a Database?   Why Use Data Warehouse?   Characteristics of Database   Characteristics of Data Warehouse   Difference between Database and Data Warehouse   Applications of Database   Applications of Data Warehousing   Disadvantages of Database   Disadvantages of Data Warehouse What is a Data Warehouse? A data warehouse is an information system which stores historical and commutative data from single or multiple sources. It is designed to analyze, report, integrate transaction data from different sources. Data Warehouse eases the analysis and reporting process of an organization. It is also a single version of truth for the organization for decision making and forecasting process. Why use a Database? Here, are prime reasons for using Database system: It offers the security of data and its access A database offers a variety of techniques to store and retrieve data. Database act as an efficient handler to balance the requirement of multiple applications using the same data A DBMS offers integrity constraints to get a high level of protection to prevent access to prohibited data. A database allows you to access concurrent data in such a way that only a single user can access the same data at a time.   Why Use Data Warehouse? Here, are Important reasons for using Data Warehouse:   Data warehouse helps business users to access critical data from some sources all in one place.   It provides consistent information on various cross-functional activities   Helps you to integrate many sources of data to reduce stress on the production system.   Data warehouse helps you to reduce TAT (total turnaround time) for analysis and reporting.   Data warehouse helps users to access critical data from different sources in a single place so, it saves user's time of retrieving data information from multiple sources. You can also access data from the cloud easily.   Data warehouse allows you to stores a large amount of historical data to analyze different periods and trends to make future predictions.   Enhances the value of operational business applications and customer relationship management systems   Separates analytics processing from transactional databases, improving the performance of both systems   Stakeholders and users may be overestimating the quality of data in the source systems. Data warehouse provides more accurate reports.   Characteristics of Database   Offers security and removes redundancy Allow multiple views of the data Database system follows the ACID compliance ( Atomicity, Consistency, Isolation, and Durability). Allows insulation between programs and data Sharing of data and multiuser transaction processing Relational Database support multi-user environment   Characteristics of Data Warehouse   A data warehouse is subject oriented as it offers information related to theme instead of companies' ongoing operations.   The data also needs to be stored in the Datawarehouse in common and unanimously acceptable manner.   The time horizon for the data warehouse is relatively extensive compared with other operational systems.   A data warehouse is non-volatile which means the previous data is not erased when new information is entered in it. Difference between Database and Data Warehouse   Parameter Database Data Warehouse Purpose Is designed to record Is designed to analyze Processing Method The database uses the Online Transactional Processing (OLTP) Data warehouse uses Online Analytical Processing (OLAP). Usage The database helps to perform fundamental operations for your business Data warehouse allows you to analyze your business. Tables and Joins Tables and joins of a database are complex as they are normalized. Table and joins are simple in a data warehouse because they are denormalized. Orientation Is an application-oriented collection of data It is a subject-oriented collection of data Storage limit Generally limited to a single application Stores data from any number of applications Availability Data is available real-time Data is refreshed from source systems as and when needed Usage ER modeling techniques are used for designing. Data modeling techniques are used for designing. Technique Capture data Analyze data Data Type Data stored in the Database is up to date. Current and Historical Data is stored in Data Warehouse. May not be up to date. Storage of data Flat Relational Approach method is used for data storage. Data Ware House uses dimensional and normalized approach for the data structure. Example: Star and snowflake schema. Query Type Simple transaction queries are used. Complex queries are used for analysis purpose. Data Summary Detailed Data is stored in a database. It stores highly summarized data.   Applications of Database   Sector Usage Banking Use in the banking sector for customer information, account-related activities, payments, deposits, loans, credit cards, etc. Airlines Use for reservations and schedule information. Universities To store student information, course registrations, colleges, and results. Telecommunication It helps to store call records, monthly bills, balance maintenance, etc. Finance Helps you to store information related stock, sales, and purchases of stocks and bonds. Sales & Production Use for storing customer, product and sales details. Manufacturing It is used for the data management of the supply chain and for tracking production of items, inventories status. HR Management Detail about employee's salaries, deduction, generation of paychecks, etc.   Applications of Data Warehousing   Sector Usage Airline It is used for airline system management operations like crew assignment, analyzes of route, frequent flyer program discount schemes for passenger, etc. Banking It is used in the banking sector to manage the resources available on the desk effectively. Healthcare sector Data warehouse used to strategize and predict outcomes, create patient's treatment reports, etc. Advanced machine learning, big data enable datawarehouse systems can predict ailments. Insurance sector Data warehouses are widely used to analyze data patterns, customer trends, and to track market movements quickly. Retain chain It helps you to track items, identify the buying pattern of the customer, promotions and also used for determining pricing policy. Telecommunication In this sector, data warehouse used for product promotions, sales decisions and to make distribution decisions.   Disadvantages of Database   Cost of Hardware and Software of an implementing Database system is high which can increase the budget of your organization.   Many DBMS systems are often complex systems, so the training for users to use the DBMS is required.   DBMS can't perform sophisticated calculations Issues regarding compatibility with systems which is already in place Data owners may lose control over their data, raising security, ownership, and privacy issues.   Disadvantages of Data Warehouse   Adding new data sources takes time, and it is associated with high cost.   Sometimes problems associated with the data warehouse may be undetected for many years.   Data warehouses are high maintenance systems. Extracting, loading, and cleaning data could be time-consuming. The data warehouse may look simple, but actually, it is too complicated for the average users. You need to provide training to end-users, who end up not using the data mining and warehouse. Despite best efforts at project management, the scope of data warehousing will always increase.   What Works Best for You? To sum up, we can say that the database helps to perform the fundamental operation of business while the data warehouse helps you to analyze your business. You choose either one of them based on your business goals. 编辑于 2021-10-12 17:01
BI, 数据仓库,ETL, 数据开发,有什么区别?
BI: Business Intelligence.   为商业提供一切智能化操作,判断与管理的手段,都可以归拢到 BI 范畴。即便是 AI (Artificial Intelligence)只要其能提供上述功能,依旧可以成为 BI 的一部分。   什么是智能化的商业操作,判断与管理?   “啤酒与尿布”的故事,在这个行业广为人知了。年轻的爸爸在购买尿布的同时,会捎带上一打啤酒,因此沃尔玛超市有意将两者放在同一货架上,提高了两者的销售量。   如果说这版本你没有切身体验,那么请你打开亚马逊 app 或者网站,你输入 data warehouse, 是不是会看到一连串的带有 data warehouse 标题的书,选择第一本点进去,你将看到如下的画面:     这就是最典型的一个应用案例。所有促使这一切生成的技术都可以归纳到 BI 中来。   BI 一个概念级的企业部门,要落实到具体工作,还是讲究其实现技术。   上面亚马逊购书案例,体现的是一个 BI 应用概念,即推荐。那么怎么实现这个应用,我们还是需要具体讲解其实现的技术细节。每个细节上将会安排特定的工程师来实现,每个工程师分配到了题中涉及到的这些职务。 (图摘自 Google, 版权原著所有)   亚马逊购书是个时尚的事儿,也给亚马逊提供了丰富的用户“轨迹”数据。通过记录这些用户行为数据,亚马逊整了一个数据仓库,将你我他的购书记录放在了一张表里,通过 SQL 技术,将购买组合当做特殊购买行为存储了下来。当网友购买我们买过的书时,通过先前存储的“特殊购买行为”向这些网友提供推荐的书。 整个图中所有负责技术实现的人,都可以叫做数据人(Data Engineer);更精确的从 ETL 往右开始称为 BI 人;更细化些, 实现 ETL 的称为 ETL 工程师, Data Warehouse 的模型设计者称为数据模型工程师(Data Modeler ), 负责可视化设计的叫做 BI Reporter. 早期的 BI 实现仅靠数据仓库。   实现数据仓库,得提到 2 个人物,Inmon 和 Kimball. 两人提出的数据仓库理论基本奠定了我们这个时代对数仓的理解。   (图来自 Google,版权原作者所有)   kimball 的数据仓库理论,倾向于大而统一的 Data Warehouse, 而 Inmon 则偏向专题性的 Data Mart, 主张将 Data Mart 中数据回流给应用,促成业务进行。前面的推荐系统则是 Data Mart 的原型应用。   现代的 BI 实现就丰富多了:小数据的玩法,大数据的玩法,统计报表,即席查询,分布式计算,流式计算,推荐系统,知识图谱,NLP,语音交互,模式识别。所以又添加了数据挖掘,数据科学家以及数据分析师,负责大数据技术实现的就叫大数据工程师了。   之所以切分了现代的 BI,主要也是数据爆发带来的分布式应用增多。   基于当前的 BI 技术在大数据背景焕发了第二春,实现架构也越发复杂。我这里选择常用的 Lamba Architecture 来主讲。   2010 年前后,大数据开始随着 Hadoop 进入人们的视野。这时数据仓库技术还方兴未艾,暂时还没有替换掉这种技术架构的打算,因为前期的投入和本身的价值继续发挥着预热。但新的数据需求不得不采取新的计算架构,所以很多公司采用了传统数据仓库技术以及以 Hadoop 为基础的分布式架构并存的策略。大致如下: (图来自网络,版权原作者所有)   粉红色的部分属于新兴的数据处理技术(以 Hadoop, Spark, Hive, Kafka,Machine Learning 等为主),绿色部分还是常规的数据应用。   到了后现代 BI 架构中,随着 Hadoop 等技术越发成熟,慢慢替换掉了原来传统的数据仓库技术,全部使用了分布式存储和计算,就形成了 Lambda Architecture. 即部分数据沿用了 Batch 处理,而另一部分数据用实时(Storm,Flink)和准实时(Spark)来处理,以完成对数据时效性的要求。 (图摘自 Google, 版权原作者所有)   可见,数据的搬运工还是 ETL, 我觉得这是一门长期热门的职位, 若能架构整个数据应用,那就更好,这就是炙手可热的 Data Architect (数据架构师).   人工智能   适当延展下,BI 中还会用到 AI 技术。亚马逊购书一例中就是如此。下面出给 Gantner 2018 在人工智能技术方向的成熟度曲线: 像 Machine Learning, NLP, Intelligence Application 都可以用来服务 BI. 所以 BI 的外延越来越难以清晰划分,越来越多职位都统称为 Data Engineer, Data Scientist 了.   一点个人看法,不喜勿喷,欢迎探讨。  
传说中 6 个月都未必能全解开的 3 道 SQL 题,来挑战下?
很久没怀疑过自己的智商了,直到遇见这 3 道题。   你也来试试!   这 3 道巨难的题目,来自 itpub 的 SQL 数据库编程大赛。说起 itpub 就不得不说它与 Oracle 的渊源,多少大师都在这里诞生。想成为 SQL 大师,有个最快的方法,就是刷题。如果能刷遍这里的题,Oracle 工作,十拿九稳。当年支付宝首席数据库架构师冯大-冯春培,就是典范。   好了,闲话不说,上题!     1,5X5方格棋盘难题 在5X5的方格棋盘中(如图),每行、列、斜线(斜线不仅仅包括对角线)最多可以放两个球,如何摆放才能放置最多的球,这样的摆法总共有几种?输出所有的摆法。   要求:用一句SQL实现。   输出格式: 从方格棋盘第一行至第5行,每行从第一列到第5列依次输出,0表示不放球,1表示放球。   例如: 1001000000000000000000000。   一行输出一个行号和一个解,按解所在的列字符串顺序从大到小排序。   详情:http://www.itpub.net/thread-1400067-1-1.html 答案:http://www.itpub.net/thread-1407072-1-1.html 我想说:尽量自己做,否则即使看了答案,对提高技能也无帮助 难理解的是,最长的对角线上也不能有 3 个 1     2,挖地雷之标出有地雷的格子     在M*N的矩阵中,单元格中的数字表示该单元格周围地雷的数目,有数字的单元格肯定不是地雷。其余的单元格要么是地雷,要么是空位而且四周都没有地雷。   周围的定义为紧挨着的单元格,例如:   若单元格在矩阵的内部,则周围有8个单元格,如图a所示 若单元格在矩阵的四边,则周围有5个单元格,如图b所示   若单元格在矩阵的四角,则周围有3个单元格,如图c所示     输入输出格式: 用3个变量v_height、v_width、v_cnt表示雷区的长度、宽度和地雷个数,其中v_height、v_width均为大于0且小于32的整数,v_cnt为大于0且小于或等于v_height*v_width的整数。                   var v_width NUMBER;EXEC :v_width := 4;var v_height NUMBER;EXEC :v_height := 4;var v_cnt NUMBER;EXEC :v_cnt := 3; 用1个字符串变量表示从矩阵第一行至最后一行,每行从第一列到最后一列依次输出 如矩阵(为明显起见,用下划线表示空格,实际做题的输入输出仍用空格)         1 1 1 _2 * 1 _* 3 2 11 2 * 1 的字符串表示为:       VAR v_str VARCHAR2(1000);exec :v_str :='111 2 1 32112 1'   正题 1):挖地雷之标出有地雷的格子  题目要求:用一句SQL实现   有若干地雷分布在图中,它们都有*标记,请把矩阵中的数字标出来。如输入字符串为:     VAR v_str VARCHAR2(1000);exec :v_str :=' * * * ' 输出格式:在输入字符串中有地雷的位置保留'*',同时对它周围的单元格标上地雷数,若单元格周围没有地雷,则保持空格,对上述输入,则输出:111 2*1 *32112*1正题 2):挖地雷之标出有地雷的格子 题目要求:用一句SQL实现或用一个PL/SQL函数实现。(如果用PL/SQL实现,则函数必须为                   create or replace function winmine(p_str varchar2,p_width NUMBER,p_height NUMBER,p_cnt NUMBER)return varchar2asbegin...return ...;end;/ 结果在sqlplus用     select winmine(:v_str,:v_width,:v_height,:v_cnt) from dual;     输出 如果有相应授权需要把grant语句一并给出,用户名为scott)   有C个地雷分布在图中,根据输入字符串提供的格子周围地雷数,把所有埋了地雷的格子标出来。如输入字符串为:     VAR v_str VARCHAR2(1000);exec :v_str :='111 2 1 32112 1' 输入地雷数为:     var v_cnt NUMBER;EXEC :v_cnt := 3; 输出格式:在输入字符串中有地雷的位置标上'*',同时保留它周围的单元格标的地雷数,若单元格周围没有地雷,则保持空格,对上述输入,则输出:111 2*1 *32112*1本题不需要考虑错误处理,如果输入错误(比如地雷数输入变量和实际不符、雷区不是矩形、字符串中标的地雷数字错误),就允许任何输出。数据库平台:Oracle 11g R2 版本(不能用12c,因为它有在sql语句中编写自定义函数功能) 详情:http://www.itpub.net/thread-1825024-1-1.html   3,井字棋     两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的井字格上打自己的符号,最先以任意一行、一列或对角线连成一线则为胜。规定X先手。   一个终局棋谱(MOVES)指的是从开始下子到一方获胜或者下完9个子出现平局,从头到尾的下子情况。一方获胜后,本局即终止。不得提前认输。   格子从上到下,从左到右,依次编号1-9   MOVES的第一位表示第一子位置,第二位表示第二子位置,......如果一方获胜,MOVES的长度有可能<9。   局面(BOARD)表示棋盘上呈现的局面,也是按照从上到下,从左到右排列。用X和0填入相应的格子。减号“-” 表示空位。   这里有个棋局:     表示出来是: MOVES=3175968, BOARD=O-X-OOXXX, WINNER=X   第一题 :求出所有可能终局棋谱和相应的局面,插入如下的表中:   CREATE TABLE TICTACTOE (MOVES VARCHAR2(9) PRIMARY KEY,BOARDVARCHAR2(9),WINNER VARCHAR2(1));     格式要求:   首先CREATE 上述TICTACTOE表。然后用一个能直接放在“insert into TICTACTOE ”后面成功运行的SQL查询语句,一次性插入所有满足标准的棋谱和相应的局面、胜者(WINNER=X或O或D,其中D表示平局)     注意:本题要求生成所有可能的终局棋谱,只要符合规则即可,哪怕其中有些走法可能看起来很愚蠢,也得包含进去。还没下完的棋谱不要列入。   如果两个终局的局面(BOARD)相同,但是其下子顺序(MOVES)不同,则视为不同棋谱,两个都必须出现在结果中。   如果两个棋谱的MOVES不同,但是其终局局面(BOARD)经过旋转、翻转后重合,仍然被视为不同棋谱,两个都必须出现在结果中。       第二题:给定一个局面,假定该局面一定为有效(不会出现一方比另一方多两子的情况,或者两方都有三子连线的情况),用SQL判断出哪一方有必胜策略,以及获胜方最多再下几子必定会获胜。比如输入:V_BOARD='X-0------' 则输出'X3',表示WINNER=X,下子数=3,因为不管对手怎么走,X最多再下3子一定获胜。(不计入O再下的子数)   如果O方有失误,也有可能X再下两子就取胜的;X方也有可能下错而输掉,但这些都不在本题考虑范围,假定双方都是完美棋手,即双方都尽可能取胜,不能取胜则尽可能求和,不能求和则尽可能多下几子。   如果给定的局面中一方已经获胜,则输出获胜方符号和子数0,例如输入:V_BOARD='OXX—XOOX', 则输出'X0'   如果不存在必胜策略(比如一个空局:V_BOARD='---------'),则输出'D'。   输出中都不含单引号。   格式要求:   首先在sqlplus中声明变量var v_BOARDvarchar2(9),再对变量赋值(如:exec :v_BOARD:='X-O------')。然后用一个包含变量v_BOARD的查询语句,返回对应此棋局的结果字符串(X数字,或O数字,或D表示平局)     第三题:m,n,k游戏是指两个对手在m*n的棋盘上轮流下子,谁先在纵、横、四十五度斜线上连续取得k个子就获胜的一种游戏,井字棋游戏其实就是3,3,3的一种特例。五子棋则为15,15,5游戏。   上述按第二题的要求,使得SQL能够适用于m,n,k大于等于3的情况 。   详情:http://www.itpub.net/thread-1943911-1-1.html
SQL Server 的备份与恢复
只要有可用的备份,一切都不晚。备份是故障出现后最重要的救命草。 Backup和restore: SQL Server的备份有三种形式:一是全备份(full backup), 这个备份里面包含的内容是值得商榷的,我们知道数据库有两种文件,数据文件与日志文件,全备份是不是将所有的数据文件与日志文件打包,备份成一个文件? 那么还原的时候是不是需要做恢复,将备份过后发生的事务接着备份时间点重新执行一边? 上面的问题细想都是肯定的。全备份做的事情,就是将所有的缓存先flush到磁盘上,不管在进行的事务是否提交,这样保证了日志的连续性,数据与日志的一致性,如果事务没提交 ,在日志文件上的标记是active的,这段日志也就不会被清空,下次恢复的时候,就从这段日志开始,接着使用新的日志执行。因此 全备份之前肯定会执行一次checkpoint;二是差异备份(differential backup), 这个备份会不会也重复full backup的过程,先执行checkpoint,然后再将上一次备份之后,发生数据页变化的这些数据页都备份起来,这部分备份就不会有日志。但是和全备份一样,备份的容积体量比较大,差异备份备份的是数据页,不管这一页是不是只有一条数据更改了,还是全部更改了;三是日志备份(transaction log backup), 日志备份中需要注意的就是对未提交事务的理解,没有提交的事务其实还是占用日志文件的VLF,shrink并不能回收日志空间;提交事务的日志如被备份之后,就会将日志VLF打上unactive或者truncated标记,这个时候执行shrink就可以回收这部分日志VLF了。日志备份体量小,比较适合频率高的执行,比如每5分钟执行一次。 全备份: 全备份用到的命令,涉及到两方面的参数,一是指定相应的备份设备,可以是磁盘,也可以是磁带;另一方面 就是备份可用的选项,比如是否压缩,是否加密。 BACKUP DATABASE database TO backup_device ] ;   备份设备很讲究,可以事先定义好逻辑设备,也可以直接指定物理设备。磁带备份机倒是没见过,但是常规的磁盘备份还是可以讨论一下的: 我们可以将一个本机带路径的物理文件名指定为备份设备: backup database lenistest  to  disk = 'E:\Data_BU\lenistest5__backup.bck';   也可以将网络上的一个带路径的物理文件名指定为备份设备: backup database AdventureWorks2012 to  disk = '\\BackupSystem\BackupDisk1\AW_backups\AdventureWorksData.Bak';   这里有个有趣的现象,如果我们在全备份之后 ,没有备份好日志,这个时候故障突然发生了,我们需要作恢复,但是恢复的时候因为会重写日志,这样就会丢失数据,如果不采取额外地措施,系统是会报错的:   restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup.bck'   Msg 3159, Level 16, State 1, Line 6 The tail of the log for the database “lenistest” has not been backed up. Use BACKUP LOG WITH NORECOVERY to backup the log if it contains work you do not want to lose. Use the WITH REPLACE or WITH STOPAT clause of the RESTORE statement to just overwrite the contents of the log. Msg 3013, Level 16, State 1, Line 6 RESTORE DATABASE is terminating abnormally.     所以如果对丢失的数据不关心或者认为不会丢失数据,可以采用with replace选项来重写原来的日志文件进行强制恢复。     restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup.bck' with replace;   差异备份: 差异备份相对全备份,优越的地方在于备份数据量少,但是有趣的是差异备份不能独立存在(日志备份也不能独立存在,他俩只能依附于全备份,也就是说在执行差异备份和日志备份的时候,必须先有一个全备份做好在那里), 差异备份必须以一个全备份做基准,在这基础之上再判断哪些数据页是有过更新的,这些更新的数据页计算出来并被备份起来。   use master;   go   backup database lenistest  to  disk = 'E:\Data_BU\lenistest5__backup.bck';   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup.bck' with differential;   假如我们没有事先做好全备份,就直接作差异备份了,那么这是不成功的:   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup2.bck' with differential;   Msg 3035, Level 16, State 1, Line 11 Cannot perform a differential backup for database “lenistest”, because a current database backup does not exist. Perform a full database backup by reissuing BACKUP DATABASE, omitting the WITH DIFFERENTIAL option. Msg 3013, Level 16, State 1, Line 11 BACKUP DATABASE is terminating abnormally.   日志备份: 日志备份相对差异备份来说,体量更小,同样它也需要全备份事先存在: backup log lenistestto disk = 'E:\Data_BU\lenistest5__backup.bck';   假如我没有事先做好全备份,我们看看直接备份日志会出现什么结果:   Msg 4214, Level 16, State 1, Line 15 BACKUP LOG cannot be performed because there is no current database backup. Msg 3013, Level 16, State 1, Line 15 BACKUP LOG is terminating abnormally.   提示先做全备份! 备份我们都讨论完了,接下来我们看看还原。还原通常有两个步骤,一是还原,二是恢复。当然我们也可以直接还原不恢复,但是可能会丢失数据,除非全备份之后 ,没有任何操作。假设我们一天一个全备份,每15分钟做一个差异备份 ,每5分钟做一个日志备份,我们该如何还原我们的数据库呢? 通常我们首先要知道我们的备份文件名或者物理路径,这个地方涉及到很多术语很难理解,比如说backup device, backupset, backup media, media set ,media family. MSDN上有一个解释,先看这个脚本:   backup database AdventureWorks2012 to  tape = '\\.\tape0'   , tape = '\\.\tape1'   , tape = '\\.\tape2' with format    , medianame = 'MyAdvWorks_MediaSet_1';     解释说到,这个备份操作产生了一个 media set, 这个media set就是命名为MyAdvWorks_MediaSet_1, 这个media set还有个media header, media header一旦生成,就可以往里面写入备份文件了。这段脚本也同时生成了一个横跨三个tape的备份文件, 他们统称为backup set. 当我们指定3个backup device作为backup set(备份集)并且执行第一次全备份的时候,接下来所有的备份都需要同时指定这3个backup device作为backup set:   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup02.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck' with format    , medianame = 'lenistestbackupset';   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck' with noinit    , differential    , medianame = 'lenistestbackupset';     Msg 3231, Level 16, State 1, Line 10 The media loaded on “E:\Data_BU\lenistest5__backup01.bck” is formatted to support 3 media families, but 2 media families are expected according to the backup device specification. Msg 3013, Level 16, State 1, Line 10 BACKUP DATABASE is terminating abnormally.   上面我先作了一次全备份,指定了三个backup device作为一份backup set, 接下来作差异备份的时候,我只指定了其中两个backup device作为backup set, 操作失败,提示就是少了一个backup device.   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck'   , disk = 'E:\Data_BU\lenistest5__backup02.bck' with noinit    , differential    , medianame = 'lenistestbackupset';   这次我们指定了同样个数的backup device,但backup device的顺序颠倒了一下,操作成功。 到目前为止,我们的脚本已经新建了 1 个media set,名为 lenistestbackupset , 2 个backup set, 第一个backup set是全备份的backup set,另外一个backup set是差异备份。所以每一次备份都会产生一个backup set. Media set产生的时间则是第一次给数据库作全备份的时候。 这个时候我们需要恢复数据库,那么第一步就是要先还原全备份,但是先不恢复,等全备份还原过后,再用差异备份做恢复: restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck' , disk = 'E:\Data_BU\lenistest5__backup03.bck' , disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 1    , replace    , norecovery;   restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck' , disk = 'E:\Data_BU\lenistest5__backup03.bck' , disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 2    , recovery;   这里一定是用replace来重写日志。   select mf.media_set_id      , isnull(ms.name, 'no media name') as media_name      , mf.physical_device_name      , mf.family_sequence_number      , mf.media_family_id      , bs.database_name      , bs.backup_start_date      , bs.backup_finish_date from backupmediafamily       mf     inner join backupset     bs         on mf.media_set_id = bs.media_set_id     left join backupmediaset ms         on bs.media_set_id = ms.media_set_id where bs.database_name = 'lenistest';   上面的脚本可以抓出来这些media family, media set, backup set的信息,如果像上面的例子一样, 我们用3个backup device来承载备份,那么这3个backup device组成了一个media family, 按照family_sequence_number来编排,1,2,3。 下面实现一个备份到恢复的全过程例子,分别在full backup, differential backup, log backup之前各出入同样的数据,看看是不是还原的时候,能正确还原过来:   insert into dbo.dataloading (     object_id   , object_name ) select object_id      , name as object_name from sys.objects;   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup02.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck' with format    , medianame = 'lenistestbackupset';     insert into dbo.dataloading (     object_id   , object_name ) select object_id      , name as object_name from sys.objects;   backup database lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck'   , disk = 'E:\Data_BU\lenistest5__backup02.bck' with noinit    , differential    , medianame = 'lenistestbackupset';     insert into dbo.dataloading (     object_id   , object_name ) select object_id      , name as object_name from sys.objects;   backup log lenistest to  disk = 'E:\Data_BU\lenistest5__backup01.bck'   , disk = 'E:\Data_BU\lenistest5__backup03.bck'   , disk = 'E:\Data_BU\lenistest5__backup02.bck' with noinit    , medianame = 'lenistestbackupset';   接着我们做还原与恢复:   restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck', disk = 'E:\Data_BU\lenistest5__backup03.bck', disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 1    , replace    , norecovery;   restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck', disk = 'E:\Data_BU\lenistest5__backup03.bck', disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 2    , norecovery;   restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck', disk = 'E:\Data_BU\lenistest5__backup03.bck', disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 3    , recovery;   这里的file选项就是backup set选项,表示第一个备份集,第二个备份集,第三个备份集。如果想还原到最新的故障发生时间点,前面的restore都不能recovery,只有在最后的时候才能作recovery. 如果我们只想恢复全备份的数据,只要执行recovery就可以了,但是数据肯定是少了: restore database lenistest from disk = 'E:\Data_BU\lenistest5__backup01.bck', disk = 'E:\Data_BU\lenistest5__backup03.bck', disk = 'E:\Data_BU\lenistest5__backup02.bck' with file = 1    , replace    , recovery;  
安装 SQL 失败问题归总
“ 安装 SQL Server 老是失败,求解决 ” “ 安装 SQL 失败后,重装出错,求方案!”   这些问题也看了好多回了。 SQL Server  的安装会有各种各样的问题。问题有时候来的很莫名其妙,归总这些问题的来源也比较复杂,有组件没有装好的,有磁盘空间不够的,有权限问题,也有依赖库没有装好的。 其实 SQL Server 并不是家用电器,插上插头,通电就可以使用的,连说明书都可以不用看。SQL Server 的官方罗列了一堆预装要求,其实是很需要认真去看,和排查你的机器是否已满足安装要求。 可能大家都认为SQL Server 比较简单,所以懒得去看了。换了 Oracle, 我相信大家在 Linux/Unix 上安装的时候,就不会选择 Next->Next->Next 这种操作了。 说个我常用的方法吧,我不会直接去物理机器上安装 SQL Server, 因为装一次失败之后,会留下很多的残留文件在注册表,导致下次安装的时候,就不能很友好的安装成功。 做法就是新建一台虚拟机,在这虚拟机创建完毕之后,做一个Snapshot, 方便下次安装错误之后回滚。 接着看 SQL Server 的安装文档,将该设置的账户,该安装的依赖库,都建好。接着就是拿正版的或者官网下载的安装文件,安装。装完之后再做一个 Snapshot,以防启动之后,参数的修改带来的无法启动等错误。 如此做过几遍之后,我们就知道大概 SQL Server 安装的过程中,会出现哪些问题了,等到在生产环境安装的时候,才能大概率上保证安装成功。   安装 MySQL, Oracle, TereData 也都大抵如此!  
外企一道 SQL 面试题,刷掉 494 名候选人
叮叮... 清脆的微信声,把我从梦中唤醒。早已习惯起床之后,回复下公众号信息,爬一下微信群里的楼。 只是今天,特别热闹。红色的小圆点,已经提示,微信群有 128+ 的未读消息了。 好奇打开了下,这次讨论的,是一道 SQL 面试题。 我对代码有洁癖,看到扭捏成一坨的 SQL, 忍不住拿到 Visual Studio Code 里,用 poorman's sql formatter 给 PS 下。 就像发朋友圈,任何不经滤镜,美颜的图,从不发。女孩子们都懂的! 以下是整理后的代码,颜值高很多: 面试官提问,把第一段 SQL 改为第二段后,为什么性能会有如此之大的提高,优化逻辑是什么。 这一题,让我想到特别恶毒的一个段子: 你愿意躺在宝马车里哭,还是喜欢坐在自行车上笑? 没有限定的条件,回答自然千奇百怪,甚至大相径庭。 如果有条件,为啥我不能躺在宝马里笑;又或者坐在自行车上,难道就不会哭了? 汇总了下,大家对这道题的优化逻辑: user 表上有 age 的索引 user 表上有 age 的索引,还有 id 覆盖索引 第二段的子查询不用回表 第一段 SQL 执行了全表扫描 更有朋友质疑了第二段的性能提高: 没有 order by,结果乱序,易产 bug 第一段 SQL 重跑下,应该 0 秒就出结果 除非是查询缓存,第二段效率未必高 MySQL优化器真笨,为什么不直接跳到第 100000 条,白白浪费读取那么多数据 回答都很精彩,质疑也都有理有据。可以看得出来,能回答出一二的朋友,数据库功底都很棒。 插一句:没有基础的小白,你肯定很烦讨论这样的问题。这就是为什么,我的群里,读者都需要有基础。当然,你若真感兴趣,态度友好,有颗求知红心,那非常欢迎你。 回到题目上来,要回答好这道 SQL,特别考验数据库的底层认知。仅仅从语法角度,这一题不难,无非是子查询 + inner join 的考察。 从数据库体系结构上回答,这一题就比较复杂。还要考虑到 MySQL 的产品特性,比如 MySQL 8 , 有些关系型数据库的理论,在这里就行不通,比如查询缓存。   据我有限的认识,这道题考察了这些方面: 数据库的数据页结构 数据库的索引页结构 查询优化器的原理 数据页,索引页访问算法 数据库缓存设计 数据库并发控制 数据库引导优化器的方法 数据库执行计划 在继续阅读之前,请各位看官,自备清茶或咖啡,以免读到干渴而放弃。准备好了,咱们就开始。 数据库的数据页结构 数据页是数据库的底层存储单元。我知道,很多初学者听到底层,就头大。认为和 c/c++ 一样无聊,甚至像是看到了汇编,天然想着要逃避。 我曾经也这样。甚至幻想,像虚竹一样,头顶头,获取无崖子一甲子功力。但靠YY,解决不了任何实际问题。纯靠与数据库大V,握个手,喝个咖啡,是不会获得任何技巧的。 接受了这个事实,我就开始死磕市面上能买到的数据库相关书籍了。   于是我发现,数据库的数据页结构,并非想象的那么难。用作业本来比喻,就很好理解。 小时候写作业,大家都用的本子,应该都还印象挺深吧。 田字格语文本与数学用作业本 这种本子的每一页,都记载着我们难忘的童年。 有被罚抄留下的课文段落;也有正儿八经写下的作文;还有写的小纸条,通常那一页写上一两句就撕了,现在想想够浪费吧。 在这样的本子里写字,你写得字大,还是小;或者写一行空一行,都会 影响这一页的信息密度。明明可以用一页纸写完,写的字儿大了,写的稀疏了,行与行之间还有空一两行,就会加大信息密度的间隙。 数据库的数据页也一样。它从上到下,写满了byte(字节),或者为了 insert 速度和减少行溢出,中间空几行。 这样的数据页,组合起来,就成了存储一张表的结构。数据量越大,数据页也越多。 如果没有很好的设计字段长度,存储的时候,也没有安排的紧密些,那么原本存储1万行的数据,就有可能需要10万行的空间。 上两图就很好的解释了,空间安排的重要性。 这和在作业本上写作业一个道理。 写作业讲完了,我们来讲讲读。 如果你打算从头到尾去读你的作业本,想必会花很多时间,才能找到你想要的曾经写过的特别佩服自己的那段话,或者公式推理。 此时,有两种方法,可以帮你: 一种,一开始写作业,就把字儿写得小一些,把空行都写上字儿,这样把 14 页的作业本,浓缩成 2 页,自然翻的页数少了; 二是,另外拿一个本子,把每页的关键字记下来,比如螃蟹在第1,3,5页;冰激淋在第2,4,6页;游戏机在7,8,9页。这样,找起来就少翻几页。   聪明如你,读到这里,一定想到些什么。没错,第一段 SQL 和第二段 SQL,在没有索引的情况下(假设你没有索引的概念),那么第二种写法,反而更慢一些。 大家都是在寻找 age=10 的数据,而第二段SQL,找完之后,还要再找这 10 条数据所在数据页上的其他数据。 相当于,你翻遍作业本,好不容易找到你想到的那段话,和数学公式,发现老师还要求你把那一页上的其他段落或者应用例子,都找出来。这样你需要重复去读,耗时会更多   事实上,经过实验,也的确如此。 在 MySQL 5.5 中,emp_info 有588万数据,没有任何索引和主键。 这儿,我用 employees 库代替。我并没有原问题一模一样的数据。 细心的朋友会发现,两段 SQL 中都加了 SQL_NO_CAHE. 这是为了防止 Query Cache 的发生,增加说服力。MySQL 5.6 及以下版本都支持 Query Cache, 也就是查询缓存。 解释下为什么要设计 Query Cache. 当二段 SQL 一模一样,连续执行两次时,第二次查询耗时为0. 这是因为,优化器充分利用第一次的缓存数据,秒出结果。 这是怎么做到的? 简单来说,第一次执行的某条 SQL 会被优化器编译为一段 hash 文本,且它的执行结果,会被存储在内存中。 当一模一样的 SQL 再次发送到优化器时,会和存储的 hash 值做个对比,如果一样,就直接返回内存中的结果,而不需要再次执行。 这功能,想想都兴奋。但,也有弊端。能重复利用缓存,必须是底层数据没有变化,一旦变化了,那么结果就会不对,对于第二次发送 SQL 命令的用户来说,就产生了数据不一致。 在一个非常繁忙的 OLTP 应用中,数据更新出乎你想象的快,查询缓存往往顷刻间就会失效。与其维护这么段失效的内存,不如不维护,空出来干点别的事,多好。 于是 MySQL 8 就废弃了它。   在本例中,加上 SQL_NO_CACHE 这样的 hint 后,就是要排除利用查询缓存带来优化的可能。这样,每次执行都重新走一遍解析,优化到取数。保证实验的公平性。 我把这 588万数据,导入 MySQL 8 版本中,同样执行上面的 SQL,奇迹就来了: 没想到 MySQL 8 在默认配置下,比 MySQL 5.5 还 “健忘”。翻过的作业,居然一点都不记得。 看执行计划知晓,子查询和外层查询,虽然访问同一个表,但却当成两个表来处理。 至此,大家可以清楚的看到,第二种 SQL 不经优化,性能还不如第一种写法。 数据库的索引页结构 刚刚,在讲述提高查询效率的时候,用到了 2 个方法。这两个方法,在数据库中,用索引来实现了。 假设,在作业本上,每一页都写了一篇小散文。我用另外一个本儿,按照关键字,记录这些关键字在作业本中对应出现的页码和行号: 螃蟹: 第 1 页,第 4 行; 第 3 页,第 6 行; 第 5 页,第 8 行 冰激淋: 第 2 页,第 1 行; 第 4 页,第 9 行; 第 6 页,第 5 行 于是,原本按照从作业本,一页页寻找螃蟹,需要翻完所有页,才能找全,现在有了索引本,一页3行,就搞定。 回到面试题来,看第二段 SQL,要找100010 行数据,在索引中找,和在全表中找,消耗的时间,就不在同一个数量级了。   具体来细说。 在作业本上,写的小作文,除了螃蟹,冰激淋等关键字,肯定还有很多很多其他词汇,比如"小妹生日那天,我送给她 2 盒冰激凌,6 只螃蟹,还有 10 多玫瑰"。 这样一来,一页上只出现一个螃蟹,翻完整个本儿,才知道有 5 页是包含螃蟹两字的。   那 user 这张表,也一样,可能有 10 个字段,每个数据页能存上 100 条数据,而每过 10 页,才有一个 age=20 的用户,那么 100000 条数据,可能就被稀释在 1000000 个数据页中。 但索引页就不一样了,100000 个 age=10, 就在 100000行上,每个索引页能存 1000 条,那么 1000 页索引页也就存完了。 通过对比,至少有 1000000/1000 即 1000 倍的时间节省了。 以上只是假设,真实情况,要复杂的多。 有索引的地方,并不简单。因为索引最大的风险,在于回表。 什么是回表? 根据关键字"螃蟹",去找哪一页出现过它,这是索引干的活。但依据"螃蟹"这个关键字,进一步找到作文中的主角,比如"小妹",那索引就做不到了。只能翻开作业本,去每一页包含"螃蟹"的作文中,去找。这种情况,就是回表。 可见,回表又增加了一次操作,会增加耗时。 而第一段 sql, 比起第二段,增加了回表的次数。因为并没指定按照什么去排序,这就是优化器矛盾的地方了。假如加上按照 id 排序,就和第二段一样了。 举个例子: 看来 MySQL 5.5 优化器在这里做了判断,以 age 为排序,这样最大的消耗在索引访问上。 假设要以 employees_info 其中另外的 from_date 来排序,看下结果: 这样一来,不仅仅要把索引 age=20 的数据全部找遍,还需回表抓下 from_date 的值。这就是回表的代价。 在 MySQL 8 上,这段 SQL 已经无法跑了,52s 才出结果。 回到写法的对比上来: 比起 63ms, 快1倍。 于是,第二种写法,在有索引的情况下,优势就来了。 无论在 MySQL 5.5 还是 MySQL 8, 第二种写法,都具有性能优势。   但是,这道题,是具有歧义的。没有 Order By, Limit 的意义在这两种写法中,就不同。 改成这样,就有对比性了: 这样,第二段 SQL 的优势才能说得清楚。相信看完上面的解释,原理就很清晰了。 但这里还涉及到优化器的成本模型计算,为什么第一段 SQL 没有被优化,看上去放弃了索引,而采用了全表扫描?  下回再讲!
有关SQL | 写不好 SQL? 送你一个大招
这两天一直在看《推荐系统实战》。书中提到基于用户行为推荐算法的时候,提到一个场景,其实用 SQL 来解,也非常容易。 已知场景是这样的,某视频网站收集了用户观影数据: 网站需要根据品味和观影历史,向用户推荐其他影片。 两个人的品味是否相近,依据年龄是否相仿,并不能最好地做出判断。但如果两人观影记录重叠,品味相近的概率就大很多。基于此,就可以互相推荐对方还未看过的电影了。 这个时候,用什么样的编程方式,来计算观影重叠,就值得商榷了。 有人说用Python, 轻便简易;有人说用 Java,库多不愁;还有人说c++, 性能贼快。 作为 SQL 博主,当然推荐 SQL, 这种集合类计算,SQL 是把快刃。对于举棋不定的朋友,你一定是缺少数据建模思维,正所谓:心中有模型,则SQL自然成! 为什么我一直推荐金融,财会,产品的朋友,都要学一学SQL, 学一学集合理论,原因就在这。SQL 理念有助于你理解现实中的思维逻辑,成为5分钟看透世界本质的人。 所有工作中遇到的逻辑分析难题,都可以借助SQL来完成。下面这段话,建议你读三遍: SQL 在手,人无我有。数据再大,我用SQL! 注意:SQL 在这里,一定要读 "色扣"。 那么怎么培养自己的数据建模思维呢?如下详细来说。 依据上面的观影日志记录,经过 ETL 裁剪,可得到观影记录如下: ETL(Extract Transform Load) , 负责把数据转换成SQL可操作的格式。 此时,推荐问题就转换成了:哪两个人的观影重叠次数最多。 最终,问题就化解为简单的SQL题, Group by .... Order By... 模式: 由此可推导,与他/她同好的人,可能还喜欢对方的其他爱好。 那么,怎么才能生成如下两两组合,求观影重叠次数的数据模型呢? 很显然,要把原始数据打散,打平,破除原先不符合三范式的结构: 这个时候,最小粒度的数据模型就出来了,影片 + 观看者,没有有重复记录,也没有粘连的记录。 实现这一步,最常规的思维就是拆字符串, 可以自定义函数实现,也可以利用系统自带的函数。比如SQL Server中就有 string_split函数: 最后一步,是真正揭开本次算法的关键,也是我平常运用最多的一个思维,无中生有。 单列观影者,怎么才能组合成双列观影者呢? 很多教材,都有涉及 Join 这个主题。大多数一直在强调相等性 join, 对于不等性和半等性 Join, 重视不多。所以很多初学者自然不知道,Join 其实可以用 <> 来连接: 这里着重注意不等性 Join 的表达: 这个例子在平时工作中,非常具有典型性。用图再展示下一步步的思考流程: 在这里,有两步模型的转换值得记录:打破范式约束(打散粘连的字符串)和 不等性 Join. 每一步模型的转换,都可以沉淀出来一个套路,累积这些套路,你将会有一个强大的兵器库,来拆解各类逻辑问题。 工作刚开始的那几年,我一直热衷于编码,求多没求精,想来也是唏嘘。刷题固然重要,但刻意积累,才是最快地精通技艺的方法呢!真希望20岁时有人告诉我这些。
有关SQL | 如何让SQL中的COUNT(*)飞起来
COUNT(*)是每个初学者的最爱,但凡漂亮的按下回车时,看着转啊转的进度条,总是有种莫名的喜感。平时总被老板催着干这干那,现在我也能指挥下电脑帮我跑跑数据! 虽说平时面试官总爱问 COUNT(*) 有什么坏处啊,为什么要避免使用 COUNT(*) 这类怪问题。真要说起来,他们也是一脸懵圈,因为面试题都有可能是网上随便摘的。 至于原理,多少人真正懂呢,真正在乎呢? 那么,COUNT(*)的性能真那么差吗?怎么才能提高性能呢!今天就盘它 已知 SQL Server 中有这样张表 (其他数据库也适用): 笨拙的堆表(Heap Table) 这张表没有索引,是张堆表(Heap Table). 总共有4000多万条数据。 第一次,运行 count(*) 可以看到运行大约花了 3 秒时间 执行计划也简单,走了全表扫描 万能的性能杀-索引 数据是存在数据页上的。这个数据页可以看做是一页纸。在纸上把字写得越紧凑,得到的信息越多。反之,如果你把字写得够大,行与行之间又很松散,每页纸能容纳的信息量也就少了。 于是,像这样全表扫描的效率就很低,理论上,只要把每页上,每一行的第一个字段统计下,就能知道有多少行了。于是索引就排上用场了。 第一个提高性能的方案就出来了,建一个索引: 总耗时2.036s 比刚才 3s 好上一丢丢。 经常看到网上有贴发表,count 单列(如 count(user_id) )会比 count(*) 有优势,果真如此吗? 2.813s 对 2.036s , 并无优势。 快上加快-压缩 那么按照刚才的思路,现在已经取 user_id , item_id 作为统计基数了,那么是不是还有办法可以更小?对,那就是压缩 ALTER INDEX IDX_USR_ITEM ON dbo.MobileLink REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE); 依旧在2s级徘徊! 可见,  COUNT(USER_ID) 并无优势! SQL Server: 我还可以更快 还有更快的方法,列式索引。它的优点除了节省空间外,还外加压缩,双重优化。 已经破1s 级。在列式索引面前,其他索引都得让道! 列式索引的结构比较复杂,详细可见这篇(SQL Server Storage)。在这里提到列式索引,旨在分享,列式索引的存储和压缩优势。 对数据库各项特性了解越多,对待同一问题可用的方法也就越多。所以,我找不到理由,不去通读数据库体系类的书。
有关SQL | 数据分析师的SQL功底该学到什么程度?
常有朋友问,数据分析师的SQL功底该学到什么程度。今天就先谈谈 T-SQL 中的 Window Function. Window Function 包含了 4 个大类。分别是: 1 - Rank Function 2 - Aggregate Function 3 - Offset Function 4 - Distribution Function. 1 - Rank Function 平常用到最多 1.1    Rank() Over() 1.2    Row_Number() Over() 1.3    Dense_Rank() Over() 1.4    NTILE(N) Over() 这四个函数,要注意的地方有两点: a.    Rank() Over() 与 Row_Number() Over() : 两者唯一的区别,就在于Row_Number() Over() 真正实现了相同条件的两条或者多条记录是用唯一值来区别的 b.    Rank() Over() 与 Dense_Rank() Over() : 这两者的区别,在于他们对位于相同排名之后的名次,是接着相同排名的连续数(Dense_Rank) 还是相隔 N 个相同记录个数之后的连续数(Rank)。 所以 Dense_Rank 出来的结果都是连续数字,而 非Dense_Rank 出来的结果有可能有跳格数。 c. 除了有用法上的区别外,顺带说说分页的实现: 第一种,我们平常用 Row_Number() 加 Top (N) 来实现 : 第二种,SQL Server 2012 之后的新功能: 按照量的大小倒序排,取第 2000 条后的记录中前 100 条。 2 - Aggregate Function. 聚合数据 2.1 - Sum() Over() 2.2 - Count() Over() 2.3 - AVG() Over() 2.4 - MIN() Over() 2.5 - MAX() Over() 在使用 Aggregation 函数的时候,唯一要注意的地方就是 Order 子句。 Over:: ::窗口中的窗口   举一个例子: 3 - Offset Function:定位记录 3.1 Lead() 3.2 LAG() 3.3 First_Value() 3.4 Last_Value() 3.5 Nth_Value() 这一类比较好理解,根据当前的记录,获取前后 N 条数据。 4 - Distribution Function: 分布函数 4.1- PERCENT_RANK() 4.2 - CUME_DIST() 4.3 - PERCENT_COUNT()- 4.4 - PERCENT_DISC() 这一类应用,到目前为止,未用过。适用于财会类的统计。    
个人成就
内容被浏览36,633
加入社区2年234天
返回顶部