HIT工程师必修课《SQL实践》
今天很荣信,受董世新主任邀请来山东美丽的泰安市,感谢山东医信会给我这样一个机会与山东的医信同行们做分享交流!
一、我的工作感受
因为今天是拓导课,我首先谈一下工作感受。我是从2009年进入HIT行业,至今也有10年了,人们常说“台上一分钟,台下十年功”,这句话告诉我们医信人如果没有经历十年功夫磨练,成为不了真正的行业专家。根据《刻意练习》这本书里面所描述的,一个普通人要想从菜鸟成长为行业技术专家,大概需要经过一万小时刻意练习,我计算一下10000小时是多少年, 10000小时/每天2小时/365天 = 13.69年,为什么是计算每天2个小时,很多时候我们的工作只是重复过去(为什么许多保安干了一辈子也没有变成专家,因为他们只是简单重复过去),如果我们每天真的能花2小时,真正去学习一门新知识、改进一下自己的工作方式、总结一下工作方法,那是一件了不得的事。这样日积月累,量变就会产生质的变化,就会变成行业专家。按照这个标准,我今天不用谦虚了,也算半个专家。
改进一下自己的工作方式举例:比如我们his系统里面经常出错处理方法,进行半自化处理,所以我开发了“智能维护”功能模块,提高工作效率;
智能维护对于一些常用的统计,做成报表给到临床科室,我们科室一共做了150张报表;
信息科制作报表对于上面我的这2个问题,其实很多人都想到了,很多时候为什么没有去做,我觉得原因有4个。第一个原因:因为懒,这是人的天性,原来有一种解决方法,我们不会去寻找另外的解决方法;第二个原因:因为没有能力去做,我不会用pb、delphi,也不会写存储过程,这个时候需要我们走出舒适区,进一步提升一下自己的技术能力;第三个原因:我太忙,没时间去做,那要看你忙什么,你是在开发软件、上线项目?还是修打印机、重装系统等。效率的本质是用最少的时间做最多的事,且尽量避免重复劳动及无技术含量的工作,怎样减少工作就是一门技术。所以我们不要用战术上的勤奋掩盖战略上的懒惰。第四个原因:觉得我“亏”了,为什么他不做,叫我做,我做了这件事情,又没有回报,面对这种问题我有时候会对自己说是技术能力重要还是“亏”重要。技术水平在原地踏步,想干一点事,发现自己已经变成中年老油条,技术落伍干不了,最后碌碌无为,在这里我只想提一个问题,这是你真的想要的人生吗?这个问题大家可以多问自己几遍,我们每个人需要对自己人生负责,我们怎么看待我们自己,就决定我们怎么去做!
学习一门新知识举例:比如我最近在学习python语言半年,我发现python十分简单易用,将来如果开发大数据应用,我要与时俱进。
总结一下工作方法举例:比如维护软件、维护硬件、开发软件的经验总结。今天我讲的这个课其实就是在整理过去10年写sql语句经验的总结;
我们学习HIT知识路径,首先获取(别人告诉我的、向别人请教来的、自己看书来、看教程视频的等等)、然后去实践(学了pb以后先去写个小程序)得到经验、再总结(把重点再捋一下,相当于第二次学习),在你脑子里面就形成一个知识地图(知识闭环)。
二、为什么讲sql
为什么今天我今天要说sql,这个不是一个医信工程师基本功吗?是的,没有错,虽然sql语句是基本功,但是要精通就很难,精通只有极少数人掌握了。就好像《算法与数据结构》这种基本功,我和在场各位只要大学上的计算机专业基本都学过,但是未必拍胸脯说自己掌握的很好。sql语句发明至今,一直很稳定,sql掌握的好不好,对于我们医信工程师来说是顺利的开展工作是一个关键。将下来我要分享我这里10年经验总结。
在这里我分享最近遇到的一个坑,在deepin下面安装 mysql,花了2星期才弄好。我们眼里所谓的“高手”,只不过是面对一个个困难解决不了的时候,第二天早上醒来的时候,对自己说try again(再试一次)。
我建议大家学习一门新知识的时候,使用虚拟机环境来做,因为虚拟机有一个快照功能,如果测试的过程发现了问题,直接回滚正常状态,还样十分方便。
三、sql重要几个概念
1、集合
SQL 以关系代数为基础发展出来的一门语言,关系代数主要是“集合”,也是sql最核心的概念
sql语句形式:select .... from ....
集合在sql当中的表现形式:每一个select语句都是一个集合,写在from后面的每个表、子查询、视图可以算作一个集合
第1种情况 第二种情况 第三种情况2、笛卡尔积
select * from table1,table2,table1存在a列m行,table12存在b列n行,最后形成的集合是(a+b)列,(m*n)行记录
“笛卡尔积”是sql语句最重要的理论基础,任何sql语句其实到最后都是回归一个“笛卡尔积”里面捞数据,就像计算机最后回归到0和1执行。我为什么这样说,一个最简单的sql语句就是一个 “笛卡尔积“,后面使用where来筛选数据。为什么我们有时候要使用select distinct去重,当你的sql语句复杂以后,即可你已经使用where条件保证每个表之间都是内连接(一对一),但是有时候也很难保证最后集合结果是你期望的形式展现出来(可能存在相同的记录)。
3、集合之间的关系
集合之间的关系 一对一、一对多(最频繁)、多对多(几乎不用),在数据库表中通常用FOREIGN KEY表示一对多的关系 。
一对多的应用场景:
a)、批量插入某工号到每个病区的权限;
insert into 操作员表
select * from 员工表,病区表 where 员工表.员工号=‘111’
b)、打印贴瓶单,根据次数生成多条记录
select * from 医嘱 ,次数表 where 医嘱.次数代码 = 次数表.代码
c)、一个检验组合对应多个检验项目,比如:肝功五项,肾功六项
d)、一个医保编码对应药监码,比如:X-L01XE-P135-E001(哌柏西利胶囊)对应86978724001194,86978724001200
4、null和空字串之间的区别
null指的是理论意义上的空和绝对的空,空字串、空数组、空对象指有含义的空。
所以null一般使用身份运算符 is,以示区分,is null或者is not null。像python是用is判断,但是java,js中用==来判断,有些编程语言对这个问题并没有严格区分。
四、写sql语句需要解决的几个问题
1、集合之间连接
2个集合之间需要连接,我们需要思考2个问题,关联字段和它们的关系(一对一、一对多、多对多)
当sql语句复杂以后了,很可能出现记录重复的现象,与你的预期不符,我们可以distinct去重。
内连接比较简单,不再介绍。
左连接LEFT JOIN左表全记录,右表符合条件
select * from t_A a left join t_B b on a.id = b.id;
Select * from t_A a,t_B b where a.id=b.id(+);
用(+)来实现, 这个+号可以这样来理解: + 表示补充,即哪个表有加号,这个表就是匹配表。如果加号写在右表,左表就是全部显示,所以是左连接。
右连接RIGHT JOIN右表全记录,左表符合条件,操作与左连接相反
2、对集合数据字段的转换
oracle环境下举例:
decode(t1.xb, '1', '男', '2', '女', '不详') sex, 性别转换
decode(t.cyqk, '1','治愈','2','好转','3','未愈','4','死亡','5','24h内死亡','6','未治','7','其他') 出院情况转换
nvl(t.cyrq,sysdate) cyrq为null转换为当前日期,否则就是cyrq原来的值
substr(a.fpatno, 6) hisid, --门诊流水号
to_char(floor(b.pat_age)) || b.age_unit patient_age, --年龄
mysql环境下举例:
datediff(NOW(), t1.create_rq) + 1 计算两个日期相差天数
IF (t.`status` = '已完成', 1, 0)
3、where条件集合数据
mysql环境下举例:
WHERE t.create_rq >= '2018-08-01‘ AND t.create_rq < '2018-09-01' 限定时间
where a1.`status` <> '已完成' AND a1.create_deptid LIKE 'V%'
where条件优先级别
4、集合进行数据聚合(类似于excel分类汇总)
group by 用法的概述
“Group By” 就是根据“By”指定的规则对数据进行分组,所谓的分组就是将一个“数据集”划分成若干个“小区域”,然后针对若干个“小区域”进行数据处理。
聚合函数的用法
Max 求最大
Min 求最小
Sum 求总和
Avg 求平均
Count 计算总行数
5、集合之间的计算
合并查询结果是将多个SELECT语句的查询结果合并到一起。因为某种情况需要将几个SELECT语句查询出来的结果合并起来显示。使用UNION关键字时,数据库系统会将所有的查询结果合并到一起,然后去除掉相同的记录。而UNION ALL关键字则只是简单的合并到一起。其语法规则如下:
SELECT语句 1
UNION | UNION ALL
SELECT语句 2
SELECT语句 1
minus
SELECT语句 2
举例说明:
select * from a_xm t where t.xmdm = '8200842'
union
select * from a_xm t where t.xmdm = '8200842';
select * from a_xm t where t.xmdm = '8200842'
union all
select * from a_xm t where t.xmdm = '8200842';
举例说明:
select * from a_xm t where t.xmdm in ('8200842','8200951','8200952')
minus
select * from a_xm t where t.xmdm = '8200951';
五、SQL经典统计(oracle环境)
假设存在一个统计需求,统计某个时间段的物品采购量,如下表格所示:
第一步:先列出涉及后台业务数据表;
--1)采购单主表
select t.*, t.rowid from fh.a_cgd_m t;
--2)采购单从表
select t.*, t.rowid from fh.a_cgd_d t;
--3)项目基础表
select t.*, t.rowid from fh.a_xm t;
--4)生产厂家表
select t.*, t.rowid from fh.a_sccj t;
第二步:书写select语句基本格式,先各个表关联起来;
SELECT 属性列表 FROM 集合
WHERE 条件表达式 1
GROUP BY属性列表 HAVING 条件表达式 2
ORDER BY属性名 ASC | DESC
/*查询2014-01-01至2014-12-31采购物品汇总*/
Select ' ' "物品代码",
' ' "物品名称",
' ' "规格",
' ' "物品分类名称",
' ' "采购总数量",
' ' "单位",
' ' "批发单价",
' ' "批发总金额"
from fh.a_cgd_m t1, fh.a_cgd_d t2, fh.a_xm t3
Where ...
第三步:从表中补齐对应字段,补齐不了的就留空,将相应的表关联起来
Select t2.xmdm "物品代码",
t2.xmmc "物品名称",
t3.guige "规格",
t3.xmfl "物品分类名称",
t2.sl "采购总数量",
t2.xmdw "单位",
t2.pfdj "批发单价",
' ' "批发总金额"
from fh.a_cgd_m t1, fh.a_cgd_d t2, fh.a_xm t3, fh.a_sccj t4
Where t1.id = t2.id
and t2.xmdm = t3.xmdm
and t2.sccj = t4.dm
and t1.ssysdate >= to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
and t1.ssysdate <= to_date('2014-12-31 23:59:59','yyyy-mm-dd hh24:mi:ss');
第四步:要对查询结果进行分类汇总。(使用集合函数查询,例如COUNT(), AVG(), MAX(), MIN(), SUM()等)。
Select t2.xmdm "物品代码",
t2.xmmc "物品名称",
t3.guige "规格",
t3.xmfl "物品分类名称",
sum(t2.sl) "采购总数量",
t2.xmdw "单位",
t2.pfdj "批发单价",
sum(t2.sl * t2.pfdj) "批发总金额"
from fh.a_cgd_m t1, fh.a_cgd_d t2, fh.a_xm t3, fh.a_sccj t4
Where t1.id = t2.id
and t2.xmdm = t3.xmdm
and t2.sccj = t4.dm
and t1.ssysdate >= to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
and t1.ssysdate <= to_date('2014-12-31 23:59:59','yyyy-mm-dd hh24:mi:ss')
group by t2.xmdm, t2.xmmc, t3.guige, t3.xmfl, t2.xmdw, t2.pfdj
order by t2.xmdm;
六、sql实践举例 (oracle环境)
1、decode和case when end与sum配合使用,可以简化sql语句的书写难度
举例说明:同时统计采购物品A、B、C、D分类的小计
2、case when使用
举例说明:同时统计某个科室的出库给中心药房的基数、节约的小计
sql语句部份截图3、字段列表使用select进行转换
select ....,(select decode(t.cyqk, '1','治愈','2','好转','3','未愈','4','死亡','5','24h内死亡','6','未治','7','其他') from ndns.ycy t
where t.jzh = t1.jzh) OutRemark from ....
作用:1、1、简化sql的书写难度;2、避免集合产生一对多或多对多的关系
4、使用with关键字书写子查询
with
aa1 as ( select ... from ...),
aa2 as ( select ... from ...),
...
aan as ( select ... from ...)
select * from aa1,aa2,...,aan
这种方式类似编程里面的“上下文”写法。什么是上下文,举例说明:今天来到泰安,假设我现在跑到大街上,随便找个人跟他说,“先生/女士,等一下我们一起爬泰山”,别人会觉得很诧异,为什么,因为你和他之间缺少一个对话情境,如果今天我在这个会议现场,“有空我们一起爬泰山”,你会说“我考虑一下”,因为你们之间建立一种“我们都是医信同仁”对话情境,这样的对话才意义,这个对话情境就可以理解为上下文。
七、总结
今天和大家一起回顾总结sql比较重要的东西,希望大家有所收获。另外我个人简书上还有2篇文章介绍了与sql相关的知识,《首届2019医信工程师创新大会参会感及重要分享补充》和《如何看待合理的HIT数据库表结构设计》,大家有兴趣可以去阅读一下。
大家可以在微信公众号搜索关注“医信大讲堂”,这个是医信人务实分享交流的一个平台。还有我个人简书账号,我会不定期发布一些文章与大家分享,我们每个HIT工程师不断把经验分享出来回馈到HIT行业,我相信HIT行业将会越来越好。
医信大讲堂公众号 我的简书账号