写给新员工的十点SQL开发建议
1、建立自己的知识体系
摘抄一句话你所拥有的知识并不取决于你记得多少,而在于它们能否在恰当的时候被回忆起来;
做笔记;
把笔记放在可以随时被找到的地方。个人的笔记可以放在印象笔记之类工具上,单位上的笔记,本地保存一份后,建议同步到码云
少数核心的内容要被熟练记忆,数据仓库常见表,常见表的常见字段,常见表的关联条件;
2、知己知彼
2.1、使用前查看数据,做数据探索
use db
go
--查看一下内容;
select top 10 * from XXXXX
select * from XXXXX limit 10;
xxxx.head(10);
--查看B表的数据字典
select XXXXX.* from sys.talbes a,sys.columns b where a.object_id=b.object_id and a.name='XXXXX'
describe xxxx
--大小
select count(1),max(日期),min(日期),len(日期) from XXXXX
summary()
--看索引
sp_helpindex XXXXX
--看存储过程
2.2、注意数据的类型
日期:YYYYMMDD
交易表:YYYY-MM-DD
1
2
3、高危操作
无论何时,用你自己的用户登陆数据仓库
3.1、drop
--使用合理权限的用户,避免做以下的事
drop dbname
drop tablename
truncate table tablename
rm -rf xxxxx
--还有一种非常危险的操作,你不能保证表1里有没有数据,数据重不重要
if exists(表1) drop table 表1 create 表1
如果要求改表,最好这样:alter table 表1
3.2、Delete
---如果你对tablea有权限,需要执行以下的操作;
delete from tablea where 日期>='20180101' and 状态='d'
--直接做是非常危险的,一定记住,所有的delete 之前,先select出来看看是不是可以删除!!!
--select * from tablea where 日期>='20180101' and 状态='d'
delete from tablea where 日期>='20180101' and 状态='d'
3.3、用落后的版本更新存储过程
直接打开本地目录一个存储过程的代码
–开始修改这段代码
–alter proc xxx 提交
–发现前天张三修改过了,服务器上的代码实际并不是你之前本地保存的,现在无法回退。呵呵。。。
--受到目前生产环境的网络等影响,生产环境上并没有代码管理工具。一个存储过程,通常由多个人在生产上维护,本地不是最新的版本。不管你有多么确定,修改存储过程前都找到服务器端最新的代码,再修改。修改完后记得在本地更新保存一次
use db
go
--查看存储过程实际版本
sp_helptext procedurename
--复制出来在新窗口修改
4、好的结构
标准的存储过程结构:
1、头部说明
2、备注
3、出错处理
4、返回值
下面举例三种常见类型的存储过程说明
4.1、一个查询的存储过程
use mytemp
go
--========================================
--功能:新员工培训,查询示例代码
--作者:kk
--时间:20181024
--说明:返回行部信息
/*
exec mytemp.dbo.PRpTblRpt '125506','20180930'
*/
--========================================
create proc dbo.PRpTblRpt(
@BrnNbr varchar(20), --行部
@RptDat varchar(10) --统计月份
)as
begin
select brnbrnnbr 行部代码,brnbrnnam 行部名称
from cmbnjis..pbtempinf,cmbnjis..pbtbrnnew
where empbrnnew=brnbrnnbr --关联条件
and (brnbrnnbr=@brnnbr or @brnnbr='全部')
and (brnbrndat=@RptDat or @RptDat='全部')
4.2、一个增删改的存储过程
use mytemp
go
--========================================
--功能:新员工培训,数据增删改示例代码,
--作者:kk
--时间:20181024
--说明:1、修改表1的序列为1的行的电话号码字段
-- 2、表1里的客户经理字段记录了可以维护这条记录的用户工号,如果更新的用户和工号不匹配,则抛出错误
-- 3、能够捕获系统错误
/*
declare @rc int
exec @rc =mytemp.dbo.PRpTblMng '1','18915990062','01029488'
select @rc
*/
--========================================
create proc dbo.PRpTblMng(
@IddNbr varchar(10), --序号
@MblNbr varchar(20), --号码
@UsrNbr varchar(20) --维护人工号
)as
begin
declare @rc int,
@erro int
select @rc=0,@erro=0
--判断传入的维护人工号是否可以维护该条记录
--把所有的错误判断放在正确的判断前面
--尽量不要在前面reutrn
if not exists(select * from 表1 where 序列=@IddNbr and 工号=@UsrNbr)
select @rc=-120
--
if @rc=0
begin
update 表1
set 手机号=@MblNbr,
维护人=@UsrNbr, --后面这两个,把维护的非功能性信息记录下来
维护日期=getdate()
where 序列=@IddNbr and 工号=@UsrNbr
select @erro=@@error --捕获系统错误
if @erro!=0
select @rc=-121
end
return @rc --返回值
/*
返回值:增:-101~-109
删:-110~-119
改:-120~-129
4.3、一个数据清洗的存储过程
编写每日定时调度的数据清洗之前,一定要记得,千万不要相信你所依赖的表都正常生成了!
use mytemp
go
--========================================
--功能:新员工培训,数据清洗示例代码
--作者:kk
--时间:20181024
--说明:1、把交易表里日期大于当前表1最大日期的数据插入到表1中
-- 2、在做数据清洗的时候,要充分考虑到你所依赖的数据不一定生成了
/*
declare @rc int
exec @rc =mytemp.dbo.PRpTblRfh ''
select @rc
*/
--========================================
create proc dbo.PRpTblRfh(
@Dat varchar(20) --分行所有的清洗调度都需要加入一个日期参数,不用到没有关系
)as
begin
declare @rc int,
@erro int,
@maxRptdat varchar(10)
@maxTrxDat varchar(10)
select @rc=0,@erro=0
select @maxRptdat=max(日期) from 表1
select @maxTrxDat=www.ysyl157.com max(trx_Dte) from 交易表
--如果交易表日期不是最新的,则不执行清洗脚本
if @maxRptdat>=@maxTrxDat
begin
select @rc=999001
end
if @rc=0
begin
insert 表1
select * from 交易表 where 日期>=@maxRptda
select @erro=@@error --捕获系统错误
if @erro!=0
select @rc=-101
end
return @rc --返回值
end
5、保留所有的数据
先看一下这张表的定义
create table dbo.表1(
序号 int idenlity,
工号 varchar(10) default '',
姓名 varchar(50) default '',
电话 varchar(20) default '',
状态 char(1) default 'A',
添加日期 datetime default getdate(www.tygj178.com),
添加用户 varchar(10) default '',
更新日期 datetime default getdate(),
更新用户 varchar(10) default '',
备注1 varchar(100) default '',
备注2 varchar(100) default ''
5.1 应用系统delete?
看看下面的代码
delete from 表1 where 序号=1
1
这并没有什么问题,你所看到的很多应用系统都是这样写的。mybatis连过去直接删除一条记录就OK了。然而,覆水难收,真正安全的系统,所有的数据都是可以回朔的。不建议在前台的应用系统里直接编写delete ,除非你又设计了一个日志表记录这一切。建议你这样写
update 表1
set 状态='C',更新日期=getdate(),更新用户='111111'
where 序号=1
这需要你在查询的时候,select脚本里加上一条where 状态=‘A’ .过程数据都被保存下来了。
5.2 非功能性的字段
注意上表从添加日期开始的字段,其实都不是这张表必须存在的字段。但是建议你在所有的表设计里都把它们加上。这样什么时候数据被第一次加入,什么时候被维护的,都很清楚
6、君子不立危墙之下(NULL值处理)
运算列中有NULL值,结果变成NULL。关联列中有NULL,记录会神秘消失。查询条件有NULL,有些记录会被查不到。所有的NULL值都需要转换
在建立表的时候,字段类型not null defaul ‘’
查询的时候,select isnull(a,’’)
在实际的处理过程中,一定要时时注意有没有空,举一个实际的案例:
--#temp 这张表有业务人员提供的卡号和姓名
-- vbscus.dbo.cseasdtaprv 这张表为短户口表,有系统内正确的卡号和姓名和客户号
--现在我在#temp里判断下卡号姓名是否一致,并把客户号加进去
select a.*,b.客户号,case when a.姓名=b.姓名 then 'ok' else 'no' end as tag
from #temp a
left join vbscus.dbo.cseasdtaprv b
on a.卡号=b.卡号
这段话看起来没有问题,但是,实际上#temp表里有些卡号是完全错误,根本不在vbscus.dbo.cseasdtaprv 这张表里的.这一步关联,如果#temp表有100条记录,只有90条卡号是存在的,剩下10条不存在,左关联后100条记录,tag字段除了OK,和NO之外,还有一个你没有意料出来的NULL…后续在使用的时候,就会出现问题,有些问题会导致严重的后果。应该这样写
select a.*,b.客户号,isnull(www.mcyllpt.com(case when a.姓名=b.姓名 then 'ok' else 'no' end),'not exists') as tag
from #temp a
left join vbscus.dbo.cseasdtaprv b
on a.卡号=b.卡号
--你也可以单独找出这些卡号不为空的字段
select a.*,b.客户号,'not exists' as tag
from #temp a
left join vbscus.dbo.cseasdtaprv b
on a.卡号=b.卡号
where b.姓名 is null
7、消失和多出的数据
7.1表关联后数据变多了
假设表a和表b 关联。关联条件是客户号 where a.客户号=b.客户号
1:1关联
Insert a(客户号,姓名,电话) values(www.dfgjpt.com/ ,zhangsan,'139139139')
Insert b(客户号,资产类型,金额) values(1,"存款",100)
Insert b(客户号,资产类型,金额) values(2,"存款",200)
select a.客户号,姓名,资产类型,金额 from a,b where a.客户号=b.客户号
--结果:
1,zhangsang,"存款",100
1:N关联
Insert a(客户号,姓名,电话) values(1,zhangsan,'139139139')
Insert b(客户号,资产类型,金额) values(1,"存款",100)
Insert b(客户号,资产类型,金额) values(1,"基金",200)
Insert b(客户号,资产类型,金额) values(2,"存款",200)
select a.客户号,姓名,资产类型,金额 from a,b where a.客户号=b.客户号
--结果:
1,zhangsang,"存款",100
1,zhangsang,"基金",200
M:N关联
特别注意这种情况的数据!
--注意此时a表客户1有两条记录
Insert a(客户号,姓名,电话) values(1,zhangsan,'139139139')
Insert a(客户号,姓名,电话) values(1,zhangsan,'189189189')
Insert b(客户号,资产类型,金额) values(1,"存款",100)
Insert b(客户号,资产类型,金额) values(1,"基金",200)
Insert b(客户号,资产类型,金额) values(2,"存款",200)
select a.客户号,姓名,资产类型,金额 from a,b where a.客户号=b.客户号
--结果,此时数据出现了翻倍现象!
1,zhangsang,"存款",100
1,zhangsang,"基金",200
1,zhangsang,"存款",100
1,zhangsang,"基金",200
--如果你在写代码之前没有判断两个表的关联条件是否会有重复,然后写出了下面一段代码;
select a.客户号,姓名,sum(金额) as 总资产
from a,b
where a.客户号=b.客户号
客户1实际的总资产只有300块,此时在你的协助下,翻了一倍变成600快。所以,当关联的条件是1:1,1:N时,关联的结果不会出现什么问题,但如果关联的条件时多对多,如上例M:N的情况时,结果集会翻M倍。这是严重的数据错误。所以,在关联前,检查数据的重复是很重要的,检查的方法如下:
--如果有值,说明a表有重复的数据
select 客户号,count(1) cnt
from a
group by 客户号
having count(www.yongshiyule178.com)>1
--假设上面查出来客户号为11111的客户出现了重复,去原始表里看下这个客户是什么情况
select *
from a
where 客户号='1'
7.2表关联后数据变少了
看一下关联的条件,是否出现了NULL!!!还有关联的表字段内容是否一致,比如,拿日期关联,一张表的日期是YYYYMMDD 格式,另外一张表的格式是YYYY-MM-DD等等。
8、索引和效率
1、小表不建议用索引(几十万行且列不多)
2、大表尽量用索引 用sp_helpindex 表名可以查看索引,然后想办法去用它。下面举一个例子,先看两张表:
--有一张10个亿的交易表a,结构如下,索引是 卡号+日期
create table a(
客户号
卡号
日期
交易类型
金额
)
--现在有一张表 b ,有一万个客户号和姓名,结构如下
create table b(
客户号
姓名
)
现在想查询表b客户在10月交易类型为"取款"的交易金额汇总。一般你能想到的写法是
select b.客户号,b.姓名,sum(金额) 金额
from a,b
where a.客户号=b.客户号 and a.日期 between '' and '' and a. 交易类型='取款'
group by 金额
然而这个写法没有用到索引,速度非常慢。推荐一个做法是:
--先找到B表客户的所有卡号,因为一个客户可能有多张卡号,此时可能一万条记录变成2万条
select b.客户号,卡号
into #x
from b,vbscus..cseasdtaprv c
where b.客户号=c.客户号
--再把临时表和交易表做关联,此时使用到了索引,虽然从关联1万条到关联2万条记录,但是效率翻了NNN倍
select a.客户号,sum(金额) 金额
into #y
from #x,a
where #x.卡号=a.卡号 and a.日期 between '' and '' and a. 交易类型='取款'
group by a.客户号
--番外话,B表中的有些客户可能在10月没有发生取款交易,需要再添加一步使返回的数据完整.这一步1万条记录OK
select b.客户号,b.姓名,isnull(金额,0) 金额
from b
left join #y
on b.客户号=#y.客户号
-4、 联合索引如果用不到第一个字段,索引没有用 。假设表的索引为卡号,日期,交易类型,使用卡号,卡号+日期,卡号+交易类型 都是用到了索引,而日期+交易类型 就然并卵
5、没有区分度的索引不要建立,比如一张一千万的表,按照性别建立索引
9、简洁
1、减少扫描大表的次数
看以下的表,有一亿条记录,索引是客户号+日期
--有一张1个亿的交易表c
create table c(
客户号
日期
交易类型
金额
)
--b表有一万个客户,统计它们在10月份交易类型为取款、转账、三方存管、代发等的交易金额和笔数,
create table b(
客户号
姓名
你会怎么设计呢?推荐一种实现方案
--这样写的好处是,一个亿的大表,只扫描了一遍,就把该取到的数据全部取走了,生成了一张小表
select b.客户号,
sum(case when 交易类型='取款' then 金额 else 0 end) 取款金额,
sum(case when 交易类型='取款' then 1 else 0 end) 取款次数,
sum(case when 交易类型='转账' then 金额 else 0 end) 转账金额,
sum(case when 交易类型='转账' then 1 else 0 end) 转账次数,
sum(case when 交易类型='三方存管' then 金额 else 0 end) 三方存管金额,
sum(case when 交易类型='三方存管' then 1 else 0 end) 三方存管次数,
sum(case when 交易类型='代发' then 金额 else 0 end) 代发金额,
sum(case when 交易类型='代发' then 1 else 0 end) 代发次数
into #x
from c,b
where c.客户号=b.客户号 and c.交易类型 in ('','','','','') and 日期 between '' and ''、
group by b.客户号
--防止b表里有些客户在统计日期一笔交易都没有,这里左关联处理下
select b.客户号,b.姓名,
isnull(取款金额,0) 取款金额,isnull(取款次数,0) 取款次数,
isnull(转账金额,0) 转账金额,isnull(转账次数,0) 转账次数,
isnull(三方存管金额,0) 三方存管金额,isnull(三方存管次数,0) 三方存管次数,
isnull(代发金额,0) 代发金额,isnull(代发次数,0) 代发次数
from b
left join #x
一定要用OR吗?
看下需求:统计下客户号为"1111111",和"22222"的客户10月交易笔数。把条件写成 :
where 客户号 ="1111111" or 客户号 "22222" and 日期 between '' and ''
1
这种写法是错误的,一旦条件中出现OR,OR一定要包起来,否则会数据会翻上去,下面这个写法是正确的:
where (客户号 ="1111111" or 客户号 "22222" ) and 日期 between '' and ''
1
但是这是不好的,第一OR会影响查询效率,有可能扫描多次表,第二为什么你们会这样考虑问题呢?建议这样写
where 客户号 in("1111111", "22222" ) and 日期 between '' and ''
1
10、其他好习惯
指定模式名 create table dbo.xxxx,create proc dbo.yyy, select from dbo.xxx ,update dbo.xxx
where条件少用函数
不要相信业务人员给你的数据数据质量
Insert 时指定列(insert table1 (a,b,c) select ‘a’,‘b’,‘c’ from table2)
随手保存,建立你的文件保存体系
遵循命名规范
写给新员工的十点SQL开发建议的更多相关文章
- SQL开发技巧(二) 【转】感觉他写的很好
本文转自: http://www.cnblogs.com/marvin/p/DevelopSQLSkill_2.html 本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列 ...
- C++入职学习篇--新员工入职(持续更新)
C++入职学习篇--新员工入职(持续更新) 本人菜鸟一枚,刚刚结束学业生涯,入职C++软件开发岗位,之前对C++一窍不通,刚刚入职,亚历山大,但为祖国和平发展,本人励志为中华崛起而奋斗,学不好C++誓 ...
- SQL开发技巧(二)
本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列文章基于SQLServer系列,且版本为SQLServer2005及以上-- 文章系列目录 SQL开发技巧(一) SQL开 ...
- SQL开发中容易忽视的一些小地方(五)
原文:SQL开发中容易忽视的一些小地方(五) 背景: 索引分类:众所周知,索引分为聚集索引和非聚集索引. 索引优点:加速数据查询. 问题:然而我们真的清楚索引的应用吗?你写的查询语句是否能充分应用上索 ...
- SQL开发中容易忽视的一些小地方(一)
原文:SQL开发中容易忽视的一些小地方(一) 写此系列文章缘由: 做开发三年来(B/S),发现基于web 架构的项目技术主要分两大方面: 第一:C#,它是程序的基础,也可是其它开发语言,没有开发语言也 ...
- SQL开发中容易忽视的一些小地方(二)
原文:SQL开发中容易忽视的一些小地方(二) 目的:继上一篇:SQL开发中容易忽视的一些小地方(一) 总结SQL中的null用法后,本文我将说说表联接查询. 为了说明问题,我创建了两个表,分别是学生信 ...
- 最强最全面的Hive SQL开发指南,超四万字全面解析
本文整体分为两部分,第一部分是简写,如果能看懂会用,就直接从此部分查,方便快捷,如果不是很理解此SQL的用法,则查看第二部分,是详细说明,当然第二部分语句也会更全一些! 第一部分: hive模糊搜索表 ...
- 分享:写了一个 java 调用 C语言 开发的动态库的范例
分享:写了一个 java 调用 C语言 开发的动态库的范例 cfunction.h 代码#pragma once#ifdef __cplusplusextern "C" {#e ...
- 如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;)。
1.如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;). 2.select查询的多个字段之间要用逗号“,”分割,如果查询涉及多个表,那多个表之 ...
随机推荐
- Ubuntu16.04 + cuda8.0 + GTX1080安装教程
1. 安装Ubuntu16.04 不考虑双系统,直接安装 Ubuntu16.04,从 ubuntu官方 下载64位版本: ubuntu-16.04-desktop-amd64.iso . 在MAC下制 ...
- uvm_marcos——UVM宏定义
I programmed all night.Through the window, on my screen,The rising sun shined. 编程一整夜,透过窗户,照在屏幕上.初升的太 ...
- Azure 进阶攻略 | 关于Java 和事件中心的那不得不说的事
物联网技术辣么火,虽然之前有说过不少,但今天,仍有一个憋在我心里已久,不得不说的话题:基于Azure 的物联网平台必不可少,你可能已经在使用,但也许并没有意识到的服务:Azure 事件中心. 啊?事件 ...
- 状态压缩---区间dp第一题
标签: ACM 题目 Gappu has a very busy weekend ahead of him. Because, next weekend is Halloween, and he is ...
- SniperOj-shorter-shellcode-x86
shell-storm 这里可以有一些可以用的shellcode,不过自己写才是biner的骄傲 /奋斗 0x00 不会写shellcode(和一条咸鱼有什么区别/哭) 0x01 这题目前有俩种解法 ...
- HibernateDaoSupport类的底层中hql操作使用
spring的ApplicationContex.xml 中配置 sql 查询方法: 加载数据源的两种方式: <!--方式一:使用 c3p0 连接池 加载数据源 --> <bean ...
- baidumap demo(二)
接口说明 百度地图API提供的搜索服务包括:POI检索,多关键字检索,公交方案检索,驾车路线检索,步行路线检索,地理编码,反地理编码,公交详情检索,在线建议查询,短串分享. 所有检索请求接口均为异步接 ...
- 初涉trie
trie:字符串算法中的重要“数据结构” 什么是trie trie就是利用字符串的公共前缀所建成的树. 众所周知树是有很多很好的性质的,于是trie可以结合其他知识点做一些有趣的事情. trie的例题 ...
- 一、Numpy库与多维数组
# Author:Zhang Yuan import numpy as np '''重点摘录: 轴的索引axis=i可以理解成是根据[]层数来判断的,0表示[],1表示[[]]... Numpy广播的 ...
- 【C#】【数据结构】002-线性表:单链表
C#数据结构:单链表 1.自定义单链表结构: 单链节点类 using System.Collections; using System.Collections.Generic; using Unity ...