当前位置:首页 » 编程语言 » sql实践

sql实践

发布时间: 2022-12-27 02:28:17

① 用sql语句完成实践课所要求的水费计算数据库及其关联表的创建

用inner join on
举例说明一下吧:
--------------
下面四种情况的一种。不是INNER JOIN的连接就是其他三种情况之一。
具体举个例子
SELECT a.xh, a.yw, b.xx FROM a INNER JOIN b ON a.xh =b.xh
其中a.xh, a.yw, b.xx是显示的结果中包含的字段,FROM a INNER JOIN b 是结果中的字段来自哪两个怎么关联的表,ON a.xh =b.xh是两个表关联的关键字段。这是最简单的两个表的连接查询,必须要有这三个基本内容。至于排序、分组等功能的实现再另外加条件就行了。
_____________________________
两表连接有四种情况:
INNER JOIN 只有在其他表中包含对应记录(一个或多个)的记录才出现在查询结果中。即JOIN左右两则所有的匹配记录。
LEFT [OUTER] JOIN 在查询结果中包含:JOIN 左侧表中的所有记录,以及JOIN 右侧表中匹配的记录。OUTER 关键字可被省略;包含 OUTER 强调这是一个外连接 (outer join)。
RIGHT [OUTER] JOIN 在查询结果中包含:JOIN 右侧表中的所有记录,以及 JOIN 左侧表中匹配的记录。OUTER 关键字可被省略;包含 OUTER 强调这是一个外连接接 (outer join)。
FULL [OUTER] JOIN 在查询结果中包含:JOIN 两侧所有的匹配记录,和不匹配的记录;包含 OUTER 强调这是一个外连接 (outer join)。
比如两表A为语文成绩,B为数学成绩,通过学号XH连接,显示A.XH,A.YW,B.XH,B.XX:
A: B:
xh yw xh xx
1 80 1 90
2 85 3 70
3 60 4 50
1)、inner join 的结果为
1 80 1 90
3 60 3 70

2)、right join的结果为
1 80 1 90
3 60 3 70
.null. .null. 4 50

3)、left joinr的结果为
xh yw xh xx
1 80 1 90
2 85 .null. .null
3 60 3 70

4)、full joinr的结果为
xh yw xh xx
1 80 1 90
2 85 .null. .null.
3 60 3 70
.null. .null. 4 50

② t-sql的实践

创建一个表的时候,必须决定字段定义所要使用的数据类型。数据类型定义了可以存储在一个字段中的数据种类。DBA可以使用数据类型来定义变量和存储过程的输入和输出参数。你必须为每个字段或变量选择一个数据类型以适配于存储在相应字段或变量中的数据。另外,还需要考虑存储需求并选择高效率存储的数据类型。举个例子,想要存储介于0到255的正数时通常要用tinyint替代smallint,int或bigint。这是因为tinyint是一个固定的1字节字段,而smallint为2字节,int为4字节还有bigint为一个8字节的固定字段。
选择正确的数据类型还可以改善数据完整性。例如,如果为一个日期字段使用datetime数据类型,那么只有日期才能存储在此字段中。然而,如果为此字段使用字符或数字数据类型,那么最终的结果就可以在此字段中存储任何字符和数字类型的数据值,而它们并不代表一个日期。
最后,选择正确的数据类型会带来正确的执行计划,从而改善数据库性能。

③ 技术干货:SQL on Hadoop在快手大数据平台的实践与优化

快手大数据架构工程师钟靓近日在 A2M 人工智能与机器学习创新峰会分享了题为《SQL on Hadoop 在快手大数据平台的实践与优化》的演讲,主要从 SQL on Hadoop 介绍、快手 SQL on Hadoop 平台概述、SQL on Hadoop 在快手的使用经验和改进分析、快手 SQL on Hadoop 的未来计划四方面介绍了 SQL on Hadoop 架构。

SQL on Hadoop,顾名思义它是基于 Hadoop 生态的一个 SQL 引擎架构,我们其实常常听到 Hive、SparkSQL、Presto、Impala 架构。接下来,我会简单的描述一下常用的架构情况。

HIVE,一个数据仓库系统。它将数据结构映射到存储的数据中,通过 SQL 对大规模的分布式存储数据进行读、写、管理。

根据定义的数据模式,以及输出 Storage,它会对输入的 SQL 经过编译、优化,生成对应引擎的任务,然后调度执行生成的任务。

HIVE 当前支持的引擎类型有:MR、SPARK、TEZ。

基于 HIVE 本身的架构,还有一些额外的服务提供方式,比如 HiveServer2 与 MetaStoreServer 都是 Thrift 架构。

此外,HiveServer2 提供远程客户端提交 SQL 任务的功能,MetaStoreServer 则提供远程客户端操作元数据的功能。

Spark,一个快速、易用,以 DAG 作为执行模式的大规模数据处理的统一分析引擎,主要模块分为 SQL 引擎、流式处理 、机器学习、图处理。

SPARKSQL 基于 SPARK 的计算引擎,做到了统一数据访问,集成 Hive,支持标准 JDBC 连接。SPARKSQL 常用于数据交互分析的场景。

SPARKSQL 的主要执行逻辑,首先是将 SQL 解析为语法树,然后语义分析生成逻辑执行计划,接着与元数据交互,进行逻辑执行计划的优化,最后,将逻辑执行翻译为物理执行计划,即 RDD lineage,并执行任务。

PRESTO,一个交互式分析查询的开源分布式 SQL 查询引擎。

因为基于内存计算,PRESTO 的计算性能大于有大量 IO 操作的 MR 和 SPARK 引擎。它有易于弹性扩展,支持可插拔连接的特点。

业内的使用案例很多,包括 FaceBook、AirBnb、美团等都有大规模的使用。

我们看到这么多的 SQL on Hadoop 架构,它侧面地说明了这种架构比较实用且成熟。利用 SQL on Hadoop 架构,我们可以实现支持海量数据处理的需求。

查询平台每日 SQL 总量在 70 万左右,DQL 的总量在 18 万左右。AdHoc 集群主要用于交互分析及机器查询,DQL 平均耗时为 300s;AdHoc 在内部有 Loacl 任务及加速引擎应用,所以查询要求耗时较低。

ETL 集群主要用于 ETL 处理以及报表的生成。DQL 平均耗时为 1000s,DQL P50 耗时为 100s,DQL P90 耗时为 4000s,除上述两大集群外,其它小的集群主要用于提供给单独的业务来使用。

服务层是对上层进行应用的。在上层有四个模块,这其中包括同步服务、ETL 平台、AdHoc 平台以及用户程序。在调度上层,同样也有四方面的数据,例如服务端日志,对它进行处理后,它会直接接入到 HDFS 里,我们后续会再对它进行清洗处理;服务打点的数据以及数据库信息,则会通过同步服务入到对应的数据源里,且我们会将元数据信息存在后端元数据系统中。

网页爬取的数据会存入 hbase,后续也会进行清洗与处理。

HUE、NoteBook 主要提供的是交互式查询的系统。报表系统、BI 系统主要是 ETL 处理以及常见的报表生成,额外的元数据系统是对外进行服务的。快手现在的引擎支持 MR、Presto 及 Spark。

管理系统主要用于管理我们当前的集群。HiveServer2 集群路由系统,主要用于引擎的选择。监控系统以及运维系统,主要是对于 HiveServer2 引擎进行运维。

我们在使用 HiveServer2 过程中,遇到过很多问题。接下来,我会详细的为大家阐述快手是如何进行优化及实践的。

当前有多个 HiveServer2 集群,分别是 AdHoc 与 ETL 两大集群,以及其他小集群。不同集群有对应的连接 ZK,客户端可通过 ZK 连接 HiveServer2 集群。

为了保证核心任务的稳定性,将 ETL 集群进行了分级,分为核心集群和一般集群。在客户端连接 HS2 的时候,我们会对任务优先级判定,高优先级的任务会被路由到核心集群,低优先级的任务会被路由到一般集群。

BeaconServer 服务为后端 Hook Server 服务,配合 HS2 中的 Hook,在 HS2 服务之外实现了所需的功能。当前支持的模块包括路由、审计、SQL 重写、任务控制、错误分析、优化建议等。

•无状态,BeaconServer 服务支持水平扩展。基于请求量的大小,可弹性调整服务的规模。

•配置动态加载,BeaconServer 服务支持动态配置加载。各个模块支持开关,服务可动态加载配置实现上下线。比如路由模块,可根据后端加速引擎集群资源情况,进行路由比率调整甚至熔断。

•无缝升级,BeaconServer 服务的后端模块可单独进行下线升级操作,不会影响 Hook 端 HS2 服务。

•Hive 支持 SPARK 与 TEZ 引擎,但不适用于生产环境。

•SQL on Hadoop 的 SQL 引擎各有优缺点,用户学习和使用的门槛较高。

•不同 SQL 引擎之间的语法和功能支持上存在差异,需要大量的测试和兼容工作,完全兼容的成本较高。

•不同 SQL 引擎各自提供服务会给数仓的血缘管理、权限控制、运维管理、资源利用都带来不便。

•在 Hive 中,自定义实现引擎。

•自动路由功能,不需要设置引擎,自动选择适合的加速引擎。

•根绝规则匹配 SQL,只将兼容的 SQL 推给加速引擎。

•复用 HiveServer2 集群架构。

基于 HiveServer2,有两种实现方式。JDBC 方式是通过 JDBC 接口,将 SQL 发送至后端加速引擎启动的集群上。PROXY 方式是将 SQL 下推给本地的加速引擎启动的 Client。

JDBC 方式启动的后端集群,均是基于 YARN,可以实现资源的分时复用。比如 AdHoc 集群的资源在夜间会自动回收,作为报表系统的资源进行复用。

路由方案基于 HS2 的 Hook 架构,在 HS2 端实现对应 Hook,用于引擎切换;后端 BeaconServer 服务中实现路由 服务,用于 SQL 的路由规则的匹配处理。不同集群可配置不同的路由规则。

为了保证后算路由服务的稳定性,团队还设计了 Rewrite Hook,用于重写 AdHoc 集群中的 SQL,自动添加 LIMIT 上限,防止大数据量的 SCAN。

•易于集成,当前主流的 SQL 引擎都可以方便的实现 JDBC 与 PROXY 方式。再通过配置,能简单的集成新的查询引擎,比如 impala、drill 等。

•自动选择引擎,减少了用户的引擎使用成本,同时也让迁移变得更简单。并且在加速引擎过载 的情况下,可以动态调整比例,防止因过载 对加速性能的影响。

•自动降级,保证了运行的可靠性。SQL 路由支持 failback 模块,可以根据配置选择是否再路由引擎执行失败后,回滚到 MR 运行。

•模块复用,对于新增的引擎,都可以复用 HiveServer2 定制的血缘采集、权限认证、并发锁控制等方案,大大降低了使用成本。

•资源复用,对于 adhoc 查询占用资源可以分时动态调整,有效保证集群资源的利用率。

当查询完成后,本地会轮询结果文件,一直获取到 LIMIT 大小,然后返回。这种情况下,当有大量的小文件存在,而大文件在后端的时候,会导致 Bad Case,不停与 HDFS 交互,获取文件信息以及文件数据,大大拉长运行时间。

在 Fetch 之前,对结果文件的大小进行预排序,可以有数百倍的性能提升。

示例:当前有 200 个文件。199 个小文件一条记录 a,1 个大文件混合记录 a 与 test 共 200 条,大文件名 index 在小文件之后。

Hive 中有一个 SimpleFetchOptimizer 优化器,会直接生成 FetchTask,减小资源申请时间与调度时间。但这个优化会出现瓶颈。如果数据量小,但是文件数多,需要返回的条数多,存在能大量筛掉结果数据的 Filter 条件。这时候串行读取输入文件,导致查询延迟大,反而没起到加速效果。

在 SimpleFetchOptimizer 优化器中,新增文件数的判断条件,最后将任务提交到集群环境,通过提高并发来实现加速。

示例:读取当前 500 个文件的分区。优化后的文件数阈值为 100。

一个表有大量的子分区,它的 DESC 过程会与元数据交互,获取所有的分区。但最后返回的结果,只有跟表相关的信息。

与元数据交互的时候,延迟了整个 DESC 的查询,当元数据压力大的时候甚至无法返回结果。

针对于 TABLE 的 DESC 过程,直接去掉了跟元数据交互获取分区的过程,加速时间跟子分区数量成正比。

示例:desc 十万分区的大表。

•复用 split 计算的数据,跳过 rece 估算重复统计输入过程。输入数据量大的任务,调度速率提升 50%。

•parquetSerde init 加速,跳过同一表的重复列剪枝优化,防止 map task op init 时间超时。

•新增 LazyOutputFormat,有 record 输出再创建文件,避免空文件的产生,导致下游读取大量空文件消耗时间。

•statsTask 支持多线程聚合统计信息,防止中间文件过多导致聚合过慢,增大运行时间。

•AdHoc 需要打开并行编译,防止 SQL 串行编译导致整体延迟时间增大的问题。

HS2 启动时会对物化视图功能进行初始化,轮询整个元数据库,导致 HS2 的启动时间非常长,从下线状态到重新上线间隔过大,可用性很差。

将物化视图功能修改为延迟懒加载,单独线程加载,不影响 HS2 的服务启动。物化视图支持加载中获取已缓存信息,保证功能的可用性。

HS2 启动时间从 5min+提升至<5s。

HS2 本身上下线成本较高,需要保证服务上的任务全部执行完成才能进行操作。配置的修改可作为较高频率的操作,且需要做到热加载。

在 HS2 的 ThriftServer 层我们增加了接口,与运维系统打通后,配置下推更新的时候自动调用,可实现配置的热加载生效。

HiveServer2 的 scratchdir 主要用于运行过程中的临时文件存储。当 HS2 中的会话创建时,便会创建 scratchdir。在 HDFS 压力大的时候,大量的会话会阻塞在创建 scratchdir 过程,导致连接数堆积至上限,最终 HS2 服务无法再连入新连接,影响服务可用性。

对此,我们先分离了一般查询与 create temporay table 查询的 scratch 目录,并支持 create temporay table 查询的 scratch 的懒创建。当 create temporay table 大量创建临时文件,便会影响 HDFS NameNode 延迟时间的时候,一般查询的 scratchdir HDFS NameNode 可以正常响应。

此外,HS2 还支持配置多 scratch,不同的 scratch 能设置加载比率,从而实现 HDFS 的均衡负载。

Hive 调度其中存在两个问题。

一、子 Task 非执行状态为完成情况的时候,若有多轮父 Task 包含子 Task,导致子 Task 被重复加入调度队列。这种 Case,需要将非执行状态修改成初始化状态。

二、当判断子 Task 是否可执行的过程中,会因为状态检测异常,无法正常加入需要调度的子 Task,从而致使查询丢失 Stage。而这种 Case,我们的做法是在执行完成后,加入一轮 Stage 的执行结果状态检查,一旦发现有下游 Stage 没有完成,直接抛出错误,实现查询结果状态的完备性检查。

•HS2 实现了接口终止查询 SQL。利用这个功能,可以及时终止异常 SQL。

•metastore JDOQuery 查询优化,关键字异常跳过,防止元数据长时间卡顿或者部分异常查询影响元数据。

•增加开关控制,强制覆盖外表目录,解决 insert overwrite 外表,文件 rename 报错的问题。

•hive parquet 下推增加关闭配置,避免 parquet 异常地下推 OR 条件,导致结果不正确。

•executeForArray 函数 join 超大字符串导致 OOM,增加限制优化。

•增加根据 table 的 schema 读取分区数据的功能,避免未级联修改分区 schema 导致读取数据异常。

•部分用户并没有开发经验,无法处理处理引擎返回的报错。

•有些错误的报错信息不明确,用户无法正确了解错误原因。

•失败的任务排查成本高,需要对 Hadoop 整套系统非常熟悉。

•用户的错误 SQL、以及需要优化的 SQL,大量具有共通性。人力维护成本高,但系统分析成本低。

SQL 专家系统基于 HS2 的 Hook 架构,在 BeaconServer 后端实现了三个主要的模块,分别是 SQL 规则控制模块、SQL 错误分析模块,与 SQL 优化建议模块。SQL 专家系统的知识库,包含关键字、原因说明、处理方案等几项主要信息,存于后端数据库中,并一直积累。

通过 SQL 专家系统,后端可以进行查询 SQL 的异常控制,避免异常 SQL 的资源浪费或者影响集群稳定。用户在遇到问题时,能直接获取问题的处理方案,减少了使用成本。

示例:空分区查询控制。

SQL 专家系统能解决一部分 HS2 的任务执行的错误诊断需求,但是比如作业 健康 度、任务执行异常等问题原因的判断,需要专门的系统来解决,为此我们设计了作业诊断系统。

作业诊断系统在 YARN 的层面,针对不同的执行引擎,对搜集的 Counter 和配置进行分析。在执行层面,提出相关的优化建议。

作业诊断系统的数据也能通过 API 提供给 SQL 专家系统,补充用于分析的问题原因。

作业诊断系统提供了查询页面来查询运行的任务。以下是命中 map 输入过多规则的任务查询过程:

④ SQL如何快速处理海量数据

在以下的文章中,我将以“办公自动化”系统为例,探讨如何在有着1000万条数据的MS SQL SERVER数据库中实现快速的数据提取和数据分页。以下代码说明了我们实例中数据库的“红头文件”一表的部分数据结构:

CREATE TABLE [dbo].[TGongwen] ( --TGongwen是红头文件表名

[Gid] [int] IDENTITY (1, 1) NOT NULL ,
--本表的id号,也是主键

[title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,
--红头文件的标题

[fariqi] [datetime] NULL ,
--发布日期

[neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL ,
--发布用户

[reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL ,

--需要浏览的用户。每个用户中间用分隔符“,”分开

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

下面,我们来往数据库中添加1000万条数据:

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,治安支队,外事科','这是最先的25万条记录')

set @i=@i+1

end

GO

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','办公室','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,外事科','这是中间的25万条记录')

set @i=@i+1

end

GO

declare @h int

set @h=1

while @h<=100

begin

declare @i int

set @i=2002

while @i<=2003

begin

declare @j int

set @j=0

while @j<50

begin

declare @k int

set @k=0

while @k<50

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as varchar(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通信科','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,外事科','这是最后的50万条记录')

set @k=@k+1

end

set @j=@j+1

end

set @i=@i+1

end

set @h=@h+1

end

GO

declare @i int

set @i=1

while @i<=9000000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,治安支队,外事科','这是最后添加的900万条记录')

set @i=@i+1000000

end

GO

通过以上语句,我们创建了25万条由通信科于2004年2月5日发布的记录,25万条由办公室于2004年9月6日发布的记录,2002年和2003年各100个2500条相同日期、不同分秒的由通信科发布的记录(共50万条),还有由通信科于2004年5月5日发布的900万条记录,合计1000万条。

一、因情制宜,建立“适当”的索引

建立“适当”的索引是实现查询优化的首要前提。

索引(index)是除表之外另一重要的、用户定义的存储在物理介质上的数据结构。当根据索引码的值搜索数据时,索引提供了对数据的快速访问。事实上,没有索引,数据库也能根据SELECT语句成功地检索到结果,但随着表变得越来越大,使用“适当”的索引的效果就越来越明显。注意,在这句话中,我们用了“适当”这个词,这是因为,如果使用索引时不认真考虑其实现过程,索引既可以提高也会破坏数据库的工作性能。

(一)深入浅出理解索引结构

实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:

其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。

我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。

我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。

进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。

(二)何时使用聚集索引或非聚集索引

下面的表总结了何时使用聚集索引或非聚集索引(很重要)。

动作描述

使用聚集索引

使用非聚集索引

列经常被分组排序





返回某范围内的数据



不应

一个或极少不同值

不应

不应

小数目的不同值



不应

大数目的不同值

不应



频繁更新的列

不应



外键列





主键列





频繁修改索引列

不应



事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。

(三)结合实际,谈索引使用的误区

理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。

1、主键就是聚集索引

这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。

通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。

显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。

从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。

在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。

通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。

在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条):

(1)仅在主键上建立聚集索引,并且不划分时间段:

Select gid,fariqi,neibuyonghu,title from tgongwen

用时:128470毫秒(即:128秒)

(2)在主键上建立聚集索引,在fariq上建立非聚集索引:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用时:53763毫秒(54秒)

(3)将聚合索引建立在日期列(fariqi)上:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用时:2423毫秒(2秒)

虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。

得出以上速度的方法是:在各个select语句前加:declare @d datetime

set @d=getdate()

并在select语句后加:

select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

2、只要建立索引就能显着提高查询速度

事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。

从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。

3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度

上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。

很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列)

(1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5'

查询速度:2513毫秒

(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='办公室'

查询速度:2516毫秒

(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='办公室'

查询速度:60280毫秒

从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。

(四)其他书上没有的索引使用经验总结

1、用聚合索引比用不是聚合索引的主键速度快

下面是实例语句:(都是提取25万条数据)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

使用时间:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000

使用时间:4470毫秒

这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。

2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi

用时:12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid

用时:18843

这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。

3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1'

用时:6343毫秒(提取100万条)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-6-6'

用时:3170毫秒(提取50万条)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

用时:3326毫秒(和上句的结果一模一样。如果采集的数量一样,那么用大于号和等于号是一样的)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' and fariqi<'2004-6-6'

用时:3280毫秒

4 、日期列不会因为有分秒的输入而减慢查询速度

下面的例子中,共有100万条数据,2004年1月1日以后的数据有50万条,但只有两个不同的日期,日期精确到日;之前有数据50万条,有5000个不同的日期,日期精确到秒。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' order by fariqi

用时:6390毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<'2004-1-1' order by fariqi

用时:6453毫秒

(五)其他注意事项

“水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片。

所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥。

当然,在实践中,作为一个尽职的数据库管理员,您还要多测试一些方案,找出哪种方案效率最高、最为有效。

二、改善SQL语句

很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:

select * from table1 where name='zhangsan' and tID > 10000

和执行:

select * from table1 where tID > 10000 and name='zhangsan'

一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name='zhangsan'的,而后再根据限制条件条件tID>10000来提出查询结果。

事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。

虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,如非这样,有时查询优化器就会不按照您的本意进行快速查询。

在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。

SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。形式如下:

列名 操作符 <常数 或 变量>



<常数 或 变量> 操作符列名

列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如:

Name=’张三’

价格>5000

5000<价格

Name=’张三’ and 价格>5000

如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。

介绍完SARG后,我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验:

1、Like语句是否属于SARG取决于所使用的通配符的类型

如:name like ‘张%’ ,这就属于SARG

而:name like ‘%张’ ,就不属于SARG。

原因是通配符%在字符串的开通使得索引无法使用。

2、or 会引起全表扫描

Name=’张三’ and 价格>5000 符号SARG,而:Name=’张三’ or 价格>5000 则不符合SARG。使用or会引起全表扫描。

3、非操作符、函数引起的不满足SARG形式的语句

不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数。下面就是几个不满足SARG形式的例子:

ABS(价格)<5000

Name like ‘%三’

有些表达式,如:

WHERE 价格*2>5000

SQL SERVER也会认为是SARG,SQL SERVER会将此式转化为:

WHERE 价格>2500/2

但我们不推荐这样使用,因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。

4、IN 的作用相当与OR

语句:

Select * from table1 where tid in (2,3)



Select * from table1 where tid=2 or tid=3

是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。

5、尽量少用NOT

6、exists 和 in 的执行效率是一样的

很多资料上都显示说,exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,我试验了一下,发现二者无论是前面带不带not,二者之间的执行效率都是一样的。因为涉及子查询,我们试验这次用SQL SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开。

(1)select title,price from titles where title_id in (select title_id from sales where qty>30)

该句的执行结果为:

表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

第二句的执行结果为:

表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

我们从此可以看到用exists和用in的执行效率是一样的。

7、用函数charindex()和前面加通配符%的LIKE执行效率一样

前面,我们谈到,如果在LIKE前面加上通配符%,那么将会引起全表扫描,所以其执行效率是低下的。但有的资料介绍说,用函数charindex()来代替LIKE速度会有大的提升,经我试验,发现这种说明也是错误的:

select gid,title,fariqi,reader from tgongwen where charindex('刑侦支队',reader)>0 and fariqi>'2004-5-5'

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑侦支队' + '%' and fariqi>'2004-5-5'

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

8、union并不绝对比or的执行效率高

我们前面已经谈到了在where子句中使用or会引起全表扫描,一般的,我所见过的资料都是推荐这里用union来代替or。事实证明,这种说法对于大部分都是适用的。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or gid>9990000

用时:68秒。扫描计数 1,逻辑读 404008 次,物理读 283 次,预读 392163 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

用时:9秒。扫描计数 8,逻辑读 67489 次,物理读 216 次,预读 7499 次。

看来,用union在通常情况下比用or的效率要高的多。

但经过试验,笔者发现如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多,虽然这里union扫描的是索引,而or扫描的是全表。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or fariqi='2004-2-5'

用时:6423毫秒。扫描计数 2,逻辑读 14726 次,物理读 1 次,预读 7176 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-2-5'

用时:11640毫秒。扫描计数 8,逻辑读 14806 次,物理读 108 次,预读 1144 次。

9、字段提取要按照“需多少、提多少”的原则,避免“select *”

我们来做一个试验:

select top 10000 gid,fariqi,reader,title from tgongwen ord

⑤ 数据库基础篇(二)—— SQL之数据查询

接下来,我们将使用员工相关的四张样本数据表,来学习SQL,建议你在学习过程中多动手练习,理解才会更深刻。表和字段含义,如下图:

如何利用SQL语句来操作以上数据呢?我们必须将样本数据导入MySQL客户端(如:Navicat)中。可以在客户端操作数据,或者在终端窗口。工作中经常在客户端操作,所以本文所有SQL语句将在Navicat中学习。
首先将sql脚本保存到桌面(获取方式:关注"Python之每日一课"公众号,后来回复"sql基础数据",即可。),导入SQL脚本的具体操作流程如下:

现在数据准备完成。这里是导入sql脚本;导出同理,选择”转储SQL“文件。当然了,Navicat也支持将当前表或查询结果导出Excel、CSV等文件类型。

下面可以写SQL语句了(每个sql脚本可以保存,下次直接使用),如下:

类似于Python中 :print(要打印的东西)

①通过select查询完的结果 ,是一个虚拟的表格,不是真实存在

② 要查询的东西 可以是常量值、表达式、字段、也可以是函数

补充:可以给字段起别名,好处是提高可读性,更方便理解;多表连接时,区分字段。用AS 或 空格来实现。如下:

2、 条件查询

条件查询:根据条件过滤原始表的数据,查询到想要的数据

1)语法

2)分类

①条件表达式

②逻辑表达式

③模糊查询

⭐ 注意:where 一定要放到 from 后面。NULL 不是假,也不是真,而是"空";任何运算符,判断符碰到NULL,都得NULL;NULL的判断只能用is null,is not null;NULL 影响查询速度,一般避免使值为NULL。exists查询可以与in型子查询互换,它们之间区别以后语句优化时会详细讲解。

3、 排序查询

1)语法

2)举栗

⭐ 注意:order by 一定要放到 语句最后(limit前面)

4、分组查询
1)语法

2)特点

①可以按单个字段分组

②和分组函数一同查询的字段最好是分组后的字段

③分组筛选(where 和 having区别)

④可以按多个字段分组,字段之间用逗号隔开

⑤可以支持排序

⑥having后可以支持别名

3)举栗

⭐ 注意:关键字顺序是where —>group by—>having—>order by—>limit( having不能单独使用,需结合group by ,表示对分组后的结果进行筛选;而 group by 必须结合分组聚合函数一起使用 ,比如:count()、max()等)

5、 常见函数

1)单行函数

2)分组函数

3)分组函数特点

①以上五个分组函数都忽略null值,除了count(*)

②sum和avg一般处理数值型,max、min、count可以处理任何数据类型

③都可以搭配distinct使用,用于统计去重后的结果

④count的参数可以支持:字段、*、常量值,一般放1

6、连接查询(多表查询)

单个表不能满足需求时,需要结合多张表,去除有关联的数据。这时就需要用连接查询,连接查询有三种,通常join使用的最多。

①等值连接的结果 = 多个表的交集

②多个表不分主次,没有顺序要求

③一般为表起别名,提高阅读性和性能

①语法

②好处

语句上,连接条件和筛选条件实现了分离,简洁。

⭐ 注意:左右连接可互换 A left join B 等价于B right join A;内连接是左</pre>

右连接的交集;mysql没有外连接。

自连接相当于等值连接,但是等值连接涉及多个表,而自连接仅仅是它自己。如下:在员工信息表里,查询员工名和直接上级的名。

7、子查询

一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询。在外面的查询语句,称为主查询或外查询。

①子查询都放在小括号内

②子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧

③子查询优先于主查询执行,主查询使用了子查询的执行结果

④子查询根据查询结果的行数不同分为以下两类:

2)举栗

8、分页查询 (可选)
实际web开发中,当显示的数据,一页显示不完时,需要分页提交sql请求。

2)特点

①起始条目索引默认从0开始

②limit子句放在查询语句的最后

③公式:select * from 表 limit (page-1)*sizePerPage,

3)举栗

9、union联合查询

union用于把涉及多个表的SELECT语句的结果组合到一个结果集合中。适用于查询条件较多,多个表之间没有连接关系的场景。</pre>

2)特点

①多条查询语句的查询的列数必须是一致的

②多条查询语句的查询的列的类型几乎相同

③union 代表去重,union all 代表不去重

3)举栗

UNION 和 UNION ALL 运行结果的区别如下:

⭐ 注意:在多个 SELECT 语句中,第一个 SELECT 语句中被使用的字段名称将被用于结果的字段名称。当使用 UNION 时,MySQL 会把结果集中重复的记录删掉,而使用 UNION ALL ,MySQL 会把所有的记录返回,且效率高于 UNION

好,今天学习到这里。工作中用的最多就是查询。如果能消化本文涉及到的所有内容,大概能解决80%的工作需求。本文更多的是原理介绍,例子不多,只有先知道是什么,才能知道怎么学。那么,接下来最重要的是要多练习实践。因为实际的业务场景要复杂很多,给大家推荐两个刷题的网站,力扣和牛客网,里面有大量的sql面试题。能进一步提高我们sql的水平。这篇文章主要是SQL的常用查询。明天继续学习SQL的DML增删改。一起加油!

⑥ PL/SQL最差实践

超长的PL/SQL代码

影响 可维护性 性能

症状

在复杂的企业应用中 存在动辄成百上千行的存储过程或上万行的包

为什么是最差

太长的PL/SQL代码不利于阅读 第三方工具在调试时也会出现代码行混乱等问题 PL/SQL存储对象(存储过程 包 函数 触发器等)行数上限约为 行 但实际工作中 当包大小超过 行就会出现调试问题

解决之道

PL/SQL代码在执行前会被加载到shared pool中 shared pool以字节为单位 UNIX下为 K 桌面环境下为 K 可以通过查询数据字典USER_OBJECT_SIZE的PARSED_SIZE字段查看对象大小 对于较大的包 应采用拆包策略 抽取复用部分 减少重复代码 对于较大的存储过程 应将存储过程组织到包中 易于管理 对于较大的匿名块 应将匿名块重新定义成子过程保存在数据库中

脱离控制的全局变量

影响 可维护性

症状 在包中使用了全局变量 在多个位置对全局变量进行操作

CREATE OR REPLACE PACKAGE BODY PKG_TEST IS

GN_全局变量 NUMBER( );

PROCEDURE 过程A IS

BEGIN

GN_全局变量:= ;

END;

PROCEDURE 过程B IS

BEGIN

GN_全局变量:= ; 这里对全局变量进行了另外的操作

END;

为什么是最差

全局变量可以在整个包范围内被访问到 因此对全局变量的跟踪和调试会比较困难 如果变量是在package中定义的 变量还可以被其他包访问 这将会更为危险

解决之道

减少或取缔全局变量的使用 对于要在过程间交互的变量 通过参数传递来实现 如果必须使用全局变量 应对全局变量进行get/set函数封装 规范对全局变量的访问

PL/SQL中嵌入复杂SQL语句

影响 可维护性

症状

在PL/SQL代码中嵌入SQL语句 如

PROCEDURE 过程A IS

BEGIN

UPDATE T_A SET COL = ;

END;

PROCEDURE 过程B IS

BEGIN

DELETE FROM T_A WHERE COL = ;

END;

为什么是最差

? PL/SQL代码中嵌入SQL语句使得代码含义变得难于阅读和理解

? 在多个位置对表进行访问 不利于SQL优化

解决之道

? 将分散SQL语句进行封装 例如上例中的删除语句 可以封装为 prc_删除T_A() 过程参数为T_A的type类型 对T_A的删除操作都委托此过程处理 当T_A表增加或删除字段时 主要的变化都集中在这些过程中 对其他逻辑影响较少

? 对SQL的优化集中在封装的过程中

异常 的异常处理

影响 可维护性 健壮性

症状 我们来看下面的代码

PROCEDURE 过程A(错误代码 out varchar 错误信息 out varchar ) IS

BEGIN

UPDATE T_A SET COL = ;

SELECT FROM T_A WHERE ;

DELETE FROM T_A WHERE COL = ;

EXCEPTION

WHEN OTHERS THEN

END;

为什么是最差

整个过程只有一个WHEN OTHERS 的异常段 示例中的三个语句发生的异常只能被最外层捕捉 无法区分发生异常的种类和位置

解决之道

? 不使用WHEN OTHERS捕捉所有异常 例如不应该捕捉NO_DATA_FOUND异常 使用专用的Exception来捕捉特定的异常

? 声明自己的异常处理机制 处理与业务相关的异常 将业务异常与系统运行期异常分开处理

? 自定义完整的异常信息 异常信息中包含异常发生时的场景

固定的变量长度和变量类型

影响 可维护性

症状 当声明基于字段类型的变量时 尤其是varchar 类型 直接使用固定长度声明

为什么是最差

? 这种硬编码的变量大小很可能与数据库中实际大小不符

? 如果字段的类型 大小等发生变化 还需要到PL/SQL中调整变量

解决之道

使用%Type声明与字段类型相关的变量

不做单元测试

影响 健壮性

症状 PL/SQL代码中蕴含大量的业务逻辑 这些逻辑编写完毕后 没有提供合适的单元测试用例用于验证

为什么是最差 不做单元测试的危害这里就不再废话了

解决之道

PL/SQL并没有提供诸如JUnit之类易用的单元测试工具 现在有一些开源工具可以使用 使用utPLSQL()工具进行单元测试 或DBUnit进行二次开发 满足不同应用的需要

使用代码值而不使用代码名称

影响 可维护性

症状 我们看下面的代码

方法

V_sex:= ; 男

方法

CONST_MALE CONSTANT VARCHAR ( ) := ; 定义常量 男

V_sex:=CONST_MALE;

为什么是最差

? 从例子中可以看出 同样是使用性别 方法 是直接使用代码值 方法 是使用常量 看上去似乎方法 要比方法 麻烦一些 但方法 比方法 更为直观 代码的可读性也更好 代码的阅读者不需要关注 代表什么含义

? 当其他项目男性性别定义修改为 时 采用方法 编码的程序需要仔细查找每一段代码 容易产生错误 而采用方法 编码的程序只修改常量定义即可

解决之道

将常量定义放入到公共的代码包中 供其他程序共享 所有涉及到代码值的比较 引用等都必须使用常量名 而不能直接书写代码值 对于一些复杂的代码值间的关系可以进一步封装 以函数的方式提供调用

不对PL/SQL对象进行配置管理

影响 可维护性

症状 PL/SQL对象(package package body trigger procere type type body 函数等)的代码没有使用配置管理工具进行维护和更新

为什么是最差

因为Oracle内部结构的差异 对象的管理具有一定的难度 尤其是在并行开发的情况下

? 对象职责划分不清 造成多人同时修改一个对象 在编译时 如果后来者没有获取最新的代码 会造成前一个开发人员修改的代码被覆盖

? Oracle对象不能追溯既往 数据库中只能保存最新

解决之道

? 规范开发过程 以配置管理工具上的PL/SQL代码为最新

? 使用第三方插件减少同步工作量 如PL/SQL Developer下的VCS版本控制插件

IF … ELSE …的坏味道

影响 可维护性

症状 大量使用IF … ELSE

为什么是最差

大量存在IF/ELSE 造成代码逻辑混乱 不易修改 无论是PL/SQL还是其他编程语言 这种代码都已经飘着 bad *** ell 了

解决之道

? 使用Oracle数据库的继承特性 通过type实现对象的继承 利用策略模式封装差异 对外提供统一的调用接口

? 将频繁使用的IF/ELSE代码重构为单独的过程或函数 供其他代码复用

在非自治事务中控制事务

影响 数据一致性

症状

在PL/SQL非自治事务代码中控制事务 例如

PROCEDURE 过程A(错误代码 out varchar 错误信息 out varchar ) IS

BEGIN

SAVEPOINT A;

UPDATE T_A SET COL = ;

MIT;

DELETE FROM T_A WHERE COL = ;

ROLLBACK TO A;

EXCEPTION

WHEN OTHERS THEN

END;

为什么是最差

这种行为是我认为最差实践中危害最大的一种 随处可见的事务控制代码会造成数据不一致 引发的问题难于跟踪和调试

解决之道

? 由调用者决定何时提交或回滚事务

? 对于需要特殊事务管理的过程如记载日志 使用自治事务

不使用绑定变量

影响 性能

症状 直接使用值而不使用绑定变量进行查询 尤其是在拼写sql的程序中 这种情况更突出

为什么是最差

这是一个常见问题 当代码中大量充斥固定的代码值时 数据库引擎每次都需要重新解析 不能使用既有的执行计划

解决之道 对于这种经常执行的语句 使用绑定变量而非实际参数值执行

慎用ROWNUM=

影响 可维护性 数据一致性

症状 在读取数据时 有时只需要取一行 这时WHERE条件中就会用到ROWNUM=

为什么是最差

之所以将这个实践评成最差 是因为笔者在实际工作中曾经遇到过这类问题 跟踪和调试都很困难 ROWNUM本身的处理顺序是在ORDER BY 之前 所以当ROWNUM= 时产生的结果很可能是随机的

解决之道 了解要查询数据的含义 使用其他条件限制结果集

灵活的动态SQL

影响 可维护性 性能

症状 EXECUTE IMMEDIATE SELECT A FROM TAB INTO v_a;

为什么是最差

动态SQL失去了编译期检查能力 将发生问题的可能性推迟到运行期 动态SQL也不利于优化 因为只有在运行期才能得到完整的SQL语句

解决之道 尽量避免使用动态SQL 对于易变的业务逻辑可以抽取到中间层实现

对ROWID进行访问

影响 数据一致性

症状 使用ROWID作为数据更新 删除的WHERE条件

为什么是最差

ROWID属于Oracle底层存储结构 会随着数据的迁移 导入 导出发生变化 而业务逻辑则不应依赖底层存储结构

lishixin/Article/program/Oracle/201311/17942

⑦ SQLServer · 最佳实践 · 如何将SQL Server 2012降级到2008 R2

SQL
Server数据库是不支持降级还原的,如果你要将高版本的数据库转换为低版本的数据库,可以采取以下方法:
在高版本的数据库(比如SQL
Server
2012)中生成数据库的脚本文件,在生成脚本文件的高级选项中选择目标服务器的版本(比如SQL
Server
2008),数据类型为"架构和数据".
脚本生成后在目标服务器中运行脚本文件即可.

⑧ SQL如何实践字符串拼接:

--建表
createtableM
(
idint,
dictvarchar(10)
)
CreateTableN
(
billnoint,
dictdocvarchar(100)
)

--插入数据
insertintoMvalues(1,'A')
insertintoMvalues(2,'B')
insertintoMvalues(3,'C')
insertintoMvalues(4,'D')

insertintoNvalues(1,'1,2,3')
insertintoNvalues(2,'2,3')
insertintoNvalues(3,'1,2,3,4')

--创建函数
CreateFunctionfn_myStr(@StrVarchar(8000))
returnsVarchar(8000)
As
Begin
Declare@RStVarchar(8000)=@Str
Select@RSt=replace(@RSt,id,dict)
FromM
Return@RSt
End

--查询
Select*fromM
selectbillno,dbo.fn_myStr(dictdoc)AsdictdocfromN

⑨ 怎么将sql高级查询灵活运用~!要实践~!哪里有大量的案例给我去实践拜托各位了 3Q

恩~! 你可以试试编写一个ATM取款机的数据库~! 或着做个某某公司的财务管理~~~等等~~~生活中的还有很多~! 如果你还学了.NET之类的话~~还可以试着把界面一起给做出来~!

⑩ SQL操作实践之范围分区间统计的使用

最近有接触到一个统计的需求,要求输出数值各个范围的计数。举个例子,一个班有N个人,要求输出60以下,60-70,70-80,80,100各个分段的人数。像这种范围比较少的情况,可以使用如下的第一种方式即case。在MySQL和Hive中都是支持的。但是如果所要统计的范围很多,或者说不是确定的,如果使用case的方式,可以想见,将非常繁琐。这时候,就可以用到下面的第二种方式,巧妙利用计算来完成对应的统计。

可以很明显看到是比较繁琐的。

如上,通过 floor 函数,先除以500再乘以500,这样就将数据按照500做了分割,再将这个范围计数使用 group by 聚合,完成了实际的统计。 因此得到的数据0,即表示0-499, 1表示500-999,范围可以随实际数据而变化,比较方便简洁。当然,如果需要的范围不是分散,需要定制的,在少量的情况下, case between 可能更适合些。

以上就是本期的内容,作为一篇操作备忘。

1. Hive SQL 分区间统计问题

热点内容
数据库access2003 发布:2024-05-19 02:49:39 浏览:619
碧蓝航线pc挂机脚本 发布:2024-05-19 02:30:03 浏览:588
脚本fir 发布:2024-05-19 02:28:57 浏览:260
阿里云独享服务器 发布:2024-05-19 02:23:54 浏览:253
织梦源码ga 发布:2024-05-19 02:23:20 浏览:571
java文件名后缀 发布:2024-05-19 02:14:39 浏览:956
快手点榜脚本 发布:2024-05-19 02:08:44 浏览:163
pythonforinkeys 发布:2024-05-19 01:55:44 浏览:793
电脑如何局域网共享文件夹 发布:2024-05-19 01:25:01 浏览:69
手机存储越大性能越好吗 发布:2024-05-19 01:14:28 浏览:177