文档库 最新最全的文档下载
当前位置:文档库 › FFMpeg 库中比较重要的函数以及数据结构

FFMpeg 库中比较重要的函数以及数据结构

FFMpeg 库中比较重要的函数以及数据结构
FFMpeg 库中比较重要的函数以及数据结构

FFMpeg 中比较重要的函数以及数据结构如下:1. 数据结构:

(1) AVFormatContext

(2) AVOutputFormat

(3) AVInputFormat

(4) AVCodecContext

(5) AVCodec

(6) AVFrame

(7) AVPacket

(8) AVPicture

(9) AVStream

2. 初始化函数:

(1) av_register_all()

(2) avcodec_open()

(3) avcodec_close()

(4) av_open_input_file()

(5) av_find_input_format()

(6) av_find_stream_info()

(7) av_close_input_file()

3. 音视频编解码函数:

(1) avcodec_find_decoder()

(2) avcodec_alloc_frame()

(3) avpicture_get_size()

(4) avpicture_fill()

(5) img_convert()

(6) avcodec_alloc_context()

(7) avcodec_decode_video()

(8) av_free_packet()

(9) av_free()

4. 文件操作:

(1) avnew_steam()

(2) av_read_frame()

(3) av_write_frame()

(4) dump_format()

5. 其他函数:

(1) avpicture_deinterlace()

(2) ImgReSampleContext()

以下就根据,以上数据结构及函数在ffmpeg测试代码output_example.c中出现的前后顺进行分析。在此之前还是先谈一下

ffmpeg的编译问题。在linux下的编译比较简单,这里不多说了。在windows下的编译可以参考以下网页:

https://www.wendangku.net/doc/e32699066.html,/viewthread.php?tid=1897&extra=page%3D1

值得一提的是,在使用编译后的sdk进行测试时(用到ffmpeg目录下的output_example.c)编译过程中可能会有以下两个问

题:

1.Output_example.c用到了snprintf.h这个头文件。然而这个头文件在win下和linux下有所不同。具体在win下

可以用以下方法解决:

http://www.ijs.si/software/snprintf/

2.如果使用vc6,或是vc6的命令行进行编译,inline可能不认。错误会出现在common.h 文件中,可以在common.h中加入

#ifdef _MSC_VAR

#define inline __inline

#endif

交待完毕进入正题。

一.FFMpeg 中的数据结构:

I. AVFormatContext

一般在使用ffmpeg sdk的代码中AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。FFmpeg代码

中对这个数据结构的注释是:format I/O context

此结构包含了一个视频流的格式内容。其中存有了AVInputFormat(or AVOutputFormat同一时间AVFormatContext内只能存

在其中一个),和AVStream、AVPacket这几个重要的数据结构以及一些其他的相关信息,比如title,author,copyright等。

还有一些可能在编解码中会用到的信息,诸如:duration, file_size, bit_rate等。参考avformat.h

Useage:

声明:

AVFormatContext *oc; (1)

初始化:由于AVFormatConext结构包含许多信息因此初始化过程是分步完成,而且有些变量如果没有值可用,也可不初始

化。但是由于一般声明都是用指针因此一个分配内存过程不可少:

oc = av_alloc_format_context(); (2)

结构中的AVInputFormat*(或AVOutputFormat*)是一定要初始化的,基本上这是编译码要使用什么codec的依据所在:

oc->oformat = fmt; or oc->iformat = fmt; (3)

(AVInputFormat and AVOutputFormat的初其中AVOutputFormat* fmt或AVInputFormat* fmt。

始化在后面介绍。随后在参

考代码output_example.c中有一行:

snprintf(oc-filename, sizeof(oc->filename), “%s”, filename); (4)

还不是十分清楚有什么作用,估计是先要在输出文件中写一些头信息。

在完成以上步骤後,(初始化完毕AVInputFormat*(或AVOutputFormat*)以及AVFormatContext)接下来就是要利用oc初始

化本节开始讲到的AVFormatContext中的第二个重要结构。AVStream(假设已经有了声明AVStream *video_st。参考代码中

用了一个函数来完成初始化,当然也可以在主函数中做,传递进函数的参数是oc 和fmt->video_codec(这个在下一节介绍

vdeo_st = add_video_stream(oc, fmt->video_codec); (5)

此函数会在后面讲到AVStream结构时分析。

AVFormatContext最后的一个设置工作是:

if( av_set_paramters(oc,NULL) < 0){ (6)

//handle error;

}

dump_format(oc, 0, filename, 1); (7)

作用就是看看先前的初始化过程中设置的参数是否符合规范,否则将报错。

上面讲的都是初始化的过程,包括AVFormatContext本身的和利用AVFormatContext初始化其他数据结构的。接下来要讲讲整

个的编解码过程。我想先将ouput_example.c中main函数内的编解码函数框架描述一下。这样比较清晰,而且编码者为了结

构清晰,在写ouput_example.c的过程中也基本上在main函数中只保持AVFormatContext和AVStream两个数据结构

(AVOutputFormat其实也在但是包含在AVFormatContext中了)。

// open video codec and allocate the necessary encode buffers

if(video_st)

open_video(oc, video_st); (8)

// write the stream header, if any

av_write_header(oc); (9)

// encode and decode process

write_video_frame(oc, video_st); (10)

// break condition…here

}

//close codec

if(video_st)

close_video(oc, video_st); (11)

//write the trailer , if any

av_write_trailer(oc); (12)

// free the streams

for(i=0; ib_streams; i++){

av_freep(&oc->streams[i]->codec); (13)

av_freep(&oc->streams[i]); (14)

}

//close the ouput file

if(!(fmt->flags & AVFMT_NOFILE)){

url_fclose(&oc->pb); (15)

}

av_free(oc); (16)

通过以上的一串代码,就可以清晰地看出AVFormatContex* oc和AVStream* video_st是在使用ffmpeg SDK开发时贯穿始终的

两个数据结构。以下,简要介绍一下三个标为红色的函数,他们是参考代码output_example.c 开发者自行定义的函数。这样

可以使整个代码结构清晰,当然你在使用ffmpeg SDK时也可以在主函数中完成对应的功能。

在后面我们会专门针对这三个函

数做分析。

1. open_video(oc, video_st);

此函数主要是对视频编码器(或解码器)的初始化过程。初始化的数据结构为AVCodec* codec 和AVCodecContext* c包括用

到了的SDK函数有:

c = st->codec;

codec = avcodec_find_encoder(c->codec_id); //编码时,找编码器(17)

codec = avcodec_find_decoder(c->codec_id); //解码时,找解码器(18)

AVCodecContex是结构AVStream中的一个数据结构,因此在AVStream初始化後(5)直接复值给c。

// internal open video codec

avcodec_open(c,codec); (19)

// allocate video stream buffer

// AVFrame *picture

// uint8_t *video_outbuf

video_outbuf_size=200000;

video_outbuf = av_maloc(video_outbuf_size); (20)

// allocate video frame buffer

picture = alloc_picture(c->pix_fmt, c->width, c->height); (21)

上述三步比较容易理解,打开视频编解码codec、分配输出流缓存大小、分配每一帧图像缓存大小。其中AVFrame也是ffmpeg

中主要数据结构之一。这一步(8)是对编解码器的初始化过程。

2. write_video_frame(AVFormatContext *oc, AVStream *st)

这个函数中做了真正的编解码工作,其中的函数比较复杂先列出来慢慢分析。

用到的数据结构有AVCodecContext *c, SwsContext *img_convert_ctx。其中SwsContext是用来变换图像格式的。比如

yuv422变到yuv420等,当然也用到函数,见下面列表。

fill_yuv_image(tmp_picture, frame_count, c->width, c->height); (22)

sws_scale(img_convert_ctx, tmp_picture->, tmp_picture->linesize,

0, c->height, picture->data, picture->linesize); (23)

img_convert_ctx = sws_getContxt(c->width, c->height, PIX_FMT_YUV420P, (24)

c->width, c->heigth, c->pix_fmt, sws_flags, NULL, NULL, NULL);

由于参考代码中做的是一个编码。因此,它总是要求编码器输入的是yuv文件,而且是yuv420格式的。就会有了以上一些处

理过程。接下来调用编码器编码,数据规则化(打包)用到AVPacket,这也是ffmpeg中一个比较不好理解的地方。

out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); (25)

AVPacket pkt;

av_init_packet(&pkt); (26)

//……handle pkt process, we will analyze later

ret = av_write_frame(oc, &pkt); (27)

有encode就一定会有decode。而且ffmpeg专为解码而生,但是为什么在参考代码中只用了encoder呢?个人猜想是因为

encode只是用yuv420来编码,这样的yuv420生成比较容易,要是用到解码的化,还要在代码中附带一个其他格式的音视频文

件。在源代码libavcodec文件夹中有一个apiexample.c的参考代码,其中就做了编解码。有空的化我会分析一下。

3. close_video(AVFormatContext *oc, AVStream *st)

avcodec_close(st->codec);

av_free(picture->data[0]);

av_free(picture);

av_free(video_outbuf);

比较容易理解,不多说了。

以上一大段虽然名为介绍AVFormatContext。但基本上把ouput_example.c的视频编码部分的框架走了一遍,其一是想说明结

构AVFormatContext的重要性,另一方面也是希望对使用FFMpeg SDK开发者有一个大致的框架。

其实,真正的一些编码函数,内存分配函数在SDK中都已经封装好了,只要搞清楚结构就能用了。而开发者要做的就是一些

初始化的过程,基本上就是针对数据结构1的初始化。

II. AVOutputFormat

虽然简单(初始化)但是十分重要,他是编解码器将要使用哪个codec的“指示”。在其成员数据中最重要的就是关于视频

codec的了:enum CodecID video_codec;

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename, NULL); (28)

根据filename来判断文件格式,同时也初始化了用什么编码器。当然,如果是用AVInputFormat *fmt的化,就是fix用什么

解码器。(指定输出序列->fix编码器,指定输入序列->fix解码器?)

III. AVStream

AVStream作为继AVFormatContext後第二个贯穿始终的结构是有其理由的。他的成员数据中有AVCodecContext这基本的上是

对所使用的Video Codec的参数进行设定的(包括bit rate、分辨率等重要信息)。同时作为“Stream”,它包含了“流”

这个概念中的一些数据,比如:帧率(r_frame_rate)、基本时间计量单位(time_base)、(需要编解码的)首帧位置

(start_time)、持续时间(duration)、帧数(nb_frames)以及一些ip信息。当然后面的这些信息中有些不是必须要初

始化的,但是AVCodecContex是一定要初始化的,而且就是作为初始化AVStream最重要的一个部分。我们在前面就谈到了

AVStream的初始化函数(5),现在来看看他是怎么做的:

// declaration

AVStream *video_st;

video_st = add_video_stream(oc, fmt->video_codec);

static AVStream *add_video_stream(AVFormatContex *oc, int codec_id){ (29) AVCodecContext *c; // member of AVStream, which will be initialized here

AVStream *st; // temporary data, will be returned

st = av_new_stream(oc, 0); (30)

c = st->codec;

// 以下基本是针对c的初始化过程。包括比特率、分辨率、GOP大小等。

……

// 以下的两行需要注意一下,特别是使用MP4的

if(!strcmp(oc->oformat->name, “mp4”) || !strcmp(oc->oformat->name, “mov”) || !strcmp(oc->oformat->name,

“3gp”))

c->flags |= CODEC_FLAG_GLOBAL_HEADER;

// 将st传给video_st;

return st;

以上代码中,有几点需要注意的。一个是(30)和c = st->codec是一定要做的,当然这是编程中最基本的问题,(30)是将st

这个AVSteam绑定到AVFormatContext* oc上。后面的c = st->codec是将c绑定到st的AVCodecContext上。其二是对c的初始

化过程中,ouput_example.c里做的是一些基本的配置,当然作为使用者的你还希望对codec 加入其他的一些编解码的条件。

可以参考avcodec.h里关于AVCodecContext结构的介绍,注释比较详细的。

关于AVStream的使用在前面介绍AVFormatContext时已有所涉及,在主函数中三个编解码函数中(8)、(10)和(11)中。观察相

关的代码,可以发现主要还是将AVStream中的AVCodecContext提取出来,再从中提取出AVCodec结构如在(8)中:

// open_video(oc, video_st);

// AVFormatContext *oc, AVStream *st

AVCodec *codec;

AVCodecContext *c;

c = st->codec;

codec = avcodec_find_encoder(c->codec_id); (31)

// open the codec

avcodec_open(c, codec); (32)

同样,我们可以看到在(10)(write_video_frame())中AVFrame也是做为传递AVCodecContext 结构的载体而存在。(11)

(close_video())比较简单,不熬述。

IV. AVCodecContext

此结构在Ffmpeg SDK中的注释是:main external api structure其重要性可见一斑。而且在avcodec它的定义处,对其每个

成员变量,都给出了十分详细的介绍。应该说AVCodecContext的初始化是Codec使用中最重要的一环。虽然在前面的

AVStream中已经有所提及,但是这里还是要在说一遍。AVCodecContext作为Avstream的一个成员结构,必须要在Avstream初

始化後(30)再对其初始化(AVStream的初始化用到AVFormatContex)。虽然成员变量比较多,但是这里只说一下在

output_example.c中用到了,其他的请查阅avcodec.h文件中介绍。

// static AVStream *add_video_stream(AVFormatContext *oc, int codec_id)

AVCodecContext *c;

st = av_new_stream(oc, 0);

c = st->codec;

c->codec_id = codec_id;

c->codec_type = CODEC_TYPE_VIDEO;

c->bit_rate = 400000; // 400 kbits/s

c->width = 352;

c->height = 288; // CIF

// 帧率做分母,秒做分子,那么time_base也就是一帧所用时间。(时间基!)

c->time_base.den = STREAM_FRAME_RATE;

c->time_base.num = 1;

c->gop_size =12;

// here define:

// #define STREAM_PIX_FMT PIX_FMT_YUV420P

// pixel format, see PIX_FMT_xxx

// -encoding: set by user.

// -decoding: set by lavc.

c->pix_fmt = STREAM_PIX_FMT;

除了以上列出了的。还有诸如指定运动估计算法的: me_method。量化参数、最大b帧数:max_b_frames。码率控制的参数、

差错掩盖error_concealment、模式判断模式:mb_decision (这个参数蛮有意思的,可以看看avcodec.h 1566行)、

Lagrange multipler参数:lmin & lmax 和宏块级Lagrange multipler参数:mb_lmin & mb_lmax、constant

quantization parameter rate control method: cqp等。

值得一提的是在AVCodecContext中有两个成员数据结构:AVCodec、AVFrame。AVCodec记录了所要使用的Codec信息并且含有

5个函数:init、encoder、close、decode、flush来完成编解码工作(参见avcode.h 2072行)。AVFrame中主要是包含了编

码後的帧信息,包括本帧是否是key frame、*data[4]定义的Y、Cb和Cr信息等,随后详细介绍。

初始化後,可以说AVCodecContext在(8)&(10)中大显身手。先在(8)open_video()中初始化AVCodec *codec以及AVFrame*

picture:

// AVCodecContext *c;

codec = avcodec_find_encoder(c->codec_id);

……

picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

後在writer_video_frame(AVFormatContext *oc, AVStream *st)中作为一个编解码器的主要参数被利用:

AVCodecContext *c;

c = st->codec;

……

out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);

V.AVCodec

结构AVCodec中成员变量和成员函数比较少,但是很重要。他包含了CodecID,也就是用哪个Codec、

像素格式信息。还有前面提到过的5个函数(init、encode、close、decoder、flush)。顺便提一下,虽然在参考代码

output_example.c中的编码函数用的是avcodec_encode_video(),我怀疑在其中就是调用了AVCodec的encode函数,他们

传递的参数和返回值都是一致的,当然还没有得到确认,有兴趣可以看看ffmpeg源代码。在参考代码中,AVCodec的初始化

後的使用都是依附于AVCodecContex,前者是后者的成员。在AVCodecContext初始化後(add_video_stream()),AVCodec也

就能很好的初始化了:

//初始化

codec = avcodec_find_encoder(c->codec_id); (33)

//打开Codec

avcodec_open(c, codec)(34)

VI. AVFrame

AVFrame是个很有意思的结构,它本身是这样定义的:

typedef struct AVFrame {

FF_COMMON_FRAME

}AVFrame;

其中,FF_COMMON_FRAME是以一个宏出现的。由于在编解码过程中AVFrame中的数据是要经常存取的。为了加速,要采取这样

的代码手段。

AVFrame是作为一个描述“原始图像”(也就是YUV或是RGB…还有其他的吗?)的结构,他的头两个成员数据,uint8_t

*data[4],int linesize[4],第一个存放的是Y、Cb、Cr(yuv格式),linesize是啥?由这两个数据还能提取处另外一个

数据结构:

typedef struct AVPicture {

uint8_t *data[4];

int linesize[4]; // number of bytes per line

}AVPicture ;

此外,AVFrame还含有其他一些成员数据,比如。是否key_frame、已编码图像书coded_picture_number、是否作为参考帧

reference、宏块类型*mb_type等等(avcodec.h 446行)。

AVFrame的初始化并没有他结构上看上去的那么简单。由于AVFrame还有一个承载图像数据的任务(data[4])因此,对他分

配内存应该要小心完成。output_example.c中提供了alloc_picute()来完成这项工作。参考代码中定义了两个全局变量:

AVFrame *picture,*tmp_picture。(如果使用yuv420格式的那么只用到前一个数据picture 就行了,将图像信息放入

picture中。如果是其他格式,那么先要将yuv420格式初始化后放到tmp_picture中在转到需求格式放入picture中。)在

open_video()打开编解码器后初始化AVFrame:

picture = alloc_picture(c->pix_fmt, c->width, c->height);

tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

static AVFrame *alloc_picture(int pix_fmt, int width, int height){

AVFrame *picture;

uint8_t *picture_buf; // think about why use uint8_t? a byte!

picture = avcodec_alloc_frame(); (35)

if(!picture)

return NULL;

size = avpicture_get_size(pix_fmt, width, height); (36)

picture_buf = av_malloc(size); (37)

if(!picture_buf){

av_free(picture); (38)

return NULL;

}

avpicture_fill ( (AVPicture *)picture, picture_buf, pix_fmt, width, height); (39)

return picture;

}

从以上代码可以看出,完成对一个AVFrame的初始化(其实也就是内存分配),基本上是有这样一个固定模式的。至于(35)

(39)分别完成了那些工作,以及为什么有这样两步,还没有搞清楚,需要看原代码。我的猜测是(35)对AVFrame做了基本的

内存分配,保留了对可以提取出AVPicture的前两个数据的内存分配到(39)来完成。

说到这里,我们观察到在(39)中有一个(AVPicture *)picture,AVPicture这个结构也很有用。基本上他的大小也就是要在

网络上传输的包大小,我们在后面可以看到AVPacket跟AVPicture有密切的关系。VII.AVPicture

AVPicture在参考代码中没有自己本身的申明和初始化过程。出现了的两次都是作为强制类型转换由AVFrame中提取出来的:

// open_video() 中

avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height); (40)

//write_video_frame 中

// AVPacket pkt;

if(oc->oformat->flags & AVFMT_RAWPICTURE){

……

pkt.size = sizeof(AVPicture); (41)

}

在(40)中,实际上是对AVFrame的data[4]、linesize[4]分配内存。由于这两个数据大小如何分配确实需要有pix_fmt、

width、height来确定。如果输出文件格式就是RAW 图片(如YUV和RGB),AVPacket作为将编码后数据写入文件的基本数据

单元,他的单元大小、数据都是由AVPacket来的。

总结起来就是,AVPicture的存在有以下原因,AVPicture将Picture的概念从Frame中提取出来,就只由Picture(图片)本

身的信息,亮度、色度和行大小。而Frame还有如是否是key frame之类的信息。这样的类似“分级”是整个概念更加清晰。

VIII.AVPacket

AVPacket的存在是作为写入文件的基本单元而存在的。我们可能会认为直接把编码后的比特流写入文件不就可以了,为什么

还要麻烦设置一个AVPacket结构。在我看来这样的编码设置是十分有必要的,特别是在做视频实时传输,同步、边界问题可

以通过AVPacket来解决。AVPacket的成员数据有两个时间戳、数据data(通常是编码后数据)、大小size等等(参见

avformat.h 48行)。讲AVPacket的用法就不得不提到编解码函数,因为AVPacket的好些信息只有在编解码后才能的知。在

参考代码中(ouput_example.c 从362到394行),做的一个判断分支。如果输出文件格式是RAW图像(即YUV或RGB)那么就

没有编码函数,直接写入文件(因为程序本身生成一个YUV文件),这里的代码虽然在此看来没什么价值,但是如果是解码

函数解出yuv文件(或rgb)那么基本的写文件操作就是这样的:

if(oc->oformat->flags & AVFMT_RAWPICTURE) {

AVPacket pkt; // 这里没有用指针!

av_init_packet(&pkt);

pkt.flags |= PKT_FLAG_KEY // raw picture 中,每帧都是key frame?

pkt.stream_index = st->index;

pkt.data = (uint8_t *)picture;

pkt.size = sizeof(AVPicture);

ret = av_write_frame(oc, &pkt);

}

输出非raw picture,编码后:

else{

// video_outbuf & video_outbuf_size在open_video() 中初始化

out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); (42)

if(out_size > 0){

AVPacket pkt;

av_init_packet(&pkt); (43)

pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); (44)

if(c->coded_frame->key_frame)

pkt.flags |= PKT_FLAG_KEY;

pkt.stream_index= st->index;

pkt.data= video_outbuf;

pkt.size= out_size;

/* write the compressed frame in the media file */

ret = av_write_frame(oc, &pkt); (45)

} else {

ret = 0;

}

if (ret != 0) {

fprintf(stderr, "Error while writing video frame\n");

exit(1);

}

其中video_outbuf和video_outbuf_size在open_video()里的初始化是这样的:video_outbuf = NULL;

// 输出不是raw picture,而确实用到编码codec

if( !(oc->oformat->flags & AVFMT_RAWPICTURE)){

video_outbuf_size = 200000;

video_outbuf = av_malloc(video_outbuf_size);

}

(43)是AVPacket结构的初始化函数。(44)比较难理解,而且为什么会有这样的一些时间戳我也没有搞明白。其他的AVPacket

成员数据的赋值比较容易理解,要注意的是video_outbuf和video_outbuf_size的初始化问题,由于在参考代码中初始化和

使用不在同一函数中,所以比较容易忽视。(45)是写文件函数,AVFormatContext* oc中含有文件名等信息,返回值ret因该

是一共写了多少数据信息,如果返回0则说明写失败。(42)和(45)作为比较重要的SDK函数,后面还会介绍的。.

IX. Conclusion

以上分析了FFMpeg中比较重要的数据结构。下面的这个生成关系理一下思路:(->表示派生出)

AVFormatContext->AVStream->AVCodecContext->AVCodec

|

AVOutputFormat or AVInputFormat

AVFrame->AVPicture….>AVPacket

二.FFMpeg 中的函数:

在前一部分的分析中我们已经看到FFMpeg SDK提供了许多初始化函数和编码函数。我们要做的就是对主要数据结构正确的初

始化,以及正确使用相应的编解码函数以及读写(I/O)操作函数。作为一个整体化的代码SDK,FFMpeg有一些他自己的标准

化使用过程。比如函数av_register_all(); 就是一个最开始就该调用的“注册函数”,他初始化了libavcodec,“注册”

了所有的的codec和视频文件格式(format)。下面,我沿着参考代码(ouput_example.c)的脉络,介绍一下相关函数。

/******************************************************************

main()

******************************************************************/

关于日期的各种函数

//刚开始写的,有些函数名很糟糕,自己看着改改,我是不想动了#include int lea(int n)//判断是否为闰年,是返回1,不是返回0 { int leap; if(n%4==0) { if(n%100==0) { if(n%400==0) leap=1; else leap=0; } else leap=1; } else leap=0; return leap; } int mouth (inty,int m)//判断某年某月的天数并输出 { int i; int a1[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; i=lea(y); a1[2]=i?29:28; printf("%d年的第%d月是%d天\n",y,m,a1[m]); return 0; } int days(intfrom,int to)//计算从某年到某年共多少天 { inti,sun=0; for(i=from;i

return i; } void aaa(inty,int n)//判断某年的第N天是是当年的几月几日{ int i=0; int a1[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; i=lea(y); a1[2]=i?29:28; i=1; while(n>a1[2]) n-=a1[i++]; printf("这是%d年的第%d月的第%d天\n",y,i,n); } intbbb(inty,intm,int n)//计算某年某月某日是当年的第几天{ int i=0; int a1[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; i=lea(y); a1[2]=i?29:28; for(i=1;i

数据库索引的优缺点及使用时的注意事项

本文介绍了数据库索引,及其优、缺点。针对MySQL索引的特点、应用进行了详细的描述。分析了如何避免MySQL无法使用,如何使用EXPLAIN分析查询语句,如何优化MySQL索引的应用。 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它 们包含着对数据表里所有记录的引用指针。 注:[1]索引不是万能的!索引可以加快数据检索操作,但会使数据修改操作变慢。每修改数据记录,索引就必须刷新一次。为了在某种程序上弥补这一缺陷,许多SQL命令都有一个DELAY_KEY_WRITE项。这个选项的作用是暂时制止MySQL 在该命令每插入一条新记录和每修改一条现有之后立刻对索引进行刷新,对索引的刷新将等到全部记录插入/修改完毕之后再进行。在需要把许多新记录插入某个数据表的场合,DELAY_KEY_WRITE 选项的作用将非常明显。[2]另外,索引还会在硬盘上占用相当大的空间。因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。 从理论上讲,完全可以为数据表里的每个字段分别建一个索引,但MySQL把同一个数据表里的索引总数限制为16个。 1. InnoDB数据表的索引 与MyISAM数据表相比,索引对InnoDB数据的重要性要大得多。在InnoDB数据表上,索引对InnoDB数据表的重要性要在得多。在InnoDB数据表上,索引不仅会在搜索数据记录时发挥作用,还是数据行级锁定机制的苊、基础。"数据行级锁定"的意思是指在事务操作的执行过程中锁定正在被处理的个别记录,不让其他用户进行访问。这种锁定将影响到(但不限于)SELECT...LOCK IN SHARE MODE、SELECT...FOR UPDATE命令以及INSERT、UPDATE和DELETE命令。 出于效率方面的考虑,InnoDB数据表的数据行级锁定实际发生在它们的索引上,而不是数据表自身上。显然,数据行级锁定机制只有在有关的数据表有一个合适的索引可供锁定的时候才能发挥效力。 2. 限制 如果WEHERE子句的查询条件里有不等号(WHERE coloum != ...),MySQL将无法使用索引。 类似地,如果WHERE子句的查询条件里使用了函数(WHERE DAY(column) = ...),MySQL也将无法使用索引。 在JOIN操作中(需要从多个数据表提取数据时),MySQL只有在主键和外键的数 据类型相同时才能使用索引。

数据库管理系统的设计与实现

数据库管理系统的设计与实现 1.DBMS的目标 (1)用户界面友好对一个实用DBMS来说,用户界面的质量直接影响其生命力。DBMS的用户接口应面向应用,采用适合最终用户的交互式、表格式、菜单式、窗口式等界面形式,以方便使用和保持灵活性。一般地说,用户界面应具有可靠性、简单性、灵活性和立即反馈等特性。 (2)功能完备DBMS功能随系统的规模的大小而异。大型DBMS功能齐全,小型DBMS功能弱一些。DBMS主要功能包括数据定义、数据库数据存取、事务控制、数据库组织和存储管理、数据库安全保护等等。我们在下面讨论这些功能的内容。 (3)效率高系统效率包括三个方面:一是计算机系统内部资源的使用效率。能充分利用资源(包括存储空间、设备、CPU等),并注意使各种资源负载均衡以提高整个系统的效率,二是DBMS本身的运行效率。三是用户的生产率。这是指用户学习、使用DBMS和在DBMS基础上开发的应用系统的效率。 2.DBMS的基本功能 (1)数据库定义对数据库的结构进行描述,包括外模式、模式、内模式的定义;数据库完整性的定义;安全保密定义(如用户口令、级别、存取权限);存取路径(如索引)的定义。这些定义存储在数据

字典(亦称为系统目录)中,是DBMS运行的基本依据。为此,提供数据定义语言DDL。 (2)数据存取提供用户对数据的操纵功能,实现对数据库数据的检索、插入、修改和删除。一个好的DBMS应该提供功能强易学易用的数据操纵语言(DML)、方便的操作方式和较高的数据存取效率。DML有两类:一类是宿主型语言,一类是自含型语言。前者的语句不能独立使用而必须嵌入某种主语言,如C语言、COBOL语言中使用。而后者可以独立使用,通常以供终端用户交互使用和批处理方式两种形式使用。 (3)数据库运行管理这是指DBMS运行控制、管理功能。包括多用户环境下的并发控制、安全性检查和存取权限控制、完整性检查和执行、数据加密、运行日志的组织管理、事务的管理和自动恢复(保证事务的正确性),这些功能保证了数据库系统的正常运行。 (4)数据组织、存储和管理DBMS要分门别类地组织、存储各类数据,包括数据字典(亦称系统目录)、用户数据、存取路径等等。要确定以何种文件结构和存取方式在存储级上组织这些数据,如何实现数据之间的联系。数据组织和存储的基本目标是提高存储空间利用率,选择合适的存取方法确保较高存取(如随机查找、顺序查找、增、删、改)效率。 (5)数据库的建立和维护包括数据库的初始建立、数据的转换、数据库的转储和恢复、数据库的重组织和重构造以及有性能监测分析等功能。

整理的SQL常用函数

create table test (id int, value varchar(10)) insertinto test values('1','aa') insertinto test values('1','bb') insertinto test values('2','aaa') insertinto test values('2','bbb') insertinto test values('2','ccc') insertinto test values('3','aa') insertinto test values('4','bb') select*from test select id, [values] =stuff((select','+ [values] from test t where id = test.id forxmlpath('')), 1 , 1 ,'') from test groupby id stuff(param1,startIndex,length, param2) 说明:将param1中自startIndex(SQL中都是从1开始,而非0)起,删除length个字符,然后用param2替换删掉的字符。*/

COUNT()函数用于返回一个列内所有非空值的个数,这是一个整型值。 由于COUNT(*)函数会忽略NULL值,所以这个查询的结果是2。 三、SUM()函数 SUM()函数是最常用的聚合函数之一,它的功能很容易理解:和AVG()函数一样,它用于数值数据类型,返回一个列范围内所有非空值的总和。 四、CAST()函数 CAST()函数的参数是一个表达式,它包括用AS关键字分隔的源值和目标数据类型。 以下例子用于将文本字符串'123'转换为整型: SELECT CAST('123' AS int) 返回值是整型值123。 如果试图将一个代表小数的字符串转换为整型值,又会出现什么情况呢? SELECT CAST('123.4' AS int) CAST()函数和CONVERT()函数都不能执行四舍五入或截断操作。由于123.4不能用int数据类型来表示,所以对这个函数调用将产生一个错误。 Server: Msg 245, Level 16, State 1, Line 1 Syntax error converting the varchar value '123.4' to a column of data type int. 在将varchar值'123.4' 转换成数据类型int时失败。 要返回一个合法的数值,就必须使用能处理这个值的数据类型。对于这个例子,存在多个可用的数据类型。如果通过CAST()函数将这个值转换为decimal类型,需要首先定义decimal 值的精度与小数位数。在本例中,精度与小数位数分别为9与2。精度是总的数字位数,包括小数点左边和右边位数的总和。而小数位数是小数点右边的位数。这表示本例能够支持的最大的整数值是9999999,而最小的小数是0.01。 SELECT CAST('123.4' AS decimal(9,2)) decimal数据类型在结果网格中将显示有效小数位:123.40 精度和小数位数的默认值分别是18与0。如果在decimal类型中不提供这两个值,SQL Server 将截断数字的小数部分,而不会产生错误。 SELECT CAST('123.4' AS decimal) 结果是一个整数值:123 五、CONVERT()函数 对于简单类型转换,CONVERT()函数和CAST()函数的功能相同,只是语法不同。 CAST()函数一般更容易使用,其功能也更简单。 CONVERT()函数的优点是可以格式化日期和数值,它需要两个参数:第1个是目标数据类型,第2个是源数据。 CONVERT()函数还具有一些改进的功能,它可以返回经过格式化的字符串值,且可以把日期值格式化成很多形式。有28种预定义的符合各种国际和特殊要求的日期与时间输出格式。 六、STR()函数 这是一个将数字转换为字符串的快捷函数。这个函数有3个参数:数值、总长度和小数位数。如果数字的整数位数和小数位数(要加上小数点占用的一个字符)的总和小于总长度,对结果中左边的字符将用空格填充。在下面第1个例子中,包括小数点在内一共是5个字符。结果

数据库索引概论及详解

记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中. 使用索引,在一般情况下,将能明显提高查询的性能,但系统为维护索引,也必将增加许多额外的开销。所以,何时应建立索引,查询时是否使用索引,对系统性能的影响将是非常大的。在这里,我想对这个问题谈一下自己的认识。 首先,在下列情况下,不适合建立索引: 1、表的规模不大,在这种情况下,直接查找表的开销比搜索索引 再定位的开销要小。 2、表被频繁更新,在这种情况下,维护索引的开销要大于使用索 引所带来的性能提高。 3、表上已经建立了许多索引。 4、用户的查询方式经常发生变化。 上述这些情况都是比较直观的,但是,即使建立了索引,在具体查 询时,系统也未必会使用该索引。 不管是何种数据库系统,其查询优化过程由两个层次构成:代数优 化(或称基于规则的优化)和物理优化(或称基于代价的优化)(部分 数据库系统可能不含物理优化过程)。 代数优化是使用一组预定义的规则来对查询进行优化,在这种优化 方式下,如果表上建有索引,系统将使用该索引。 物理优化是在代数优化的基础上,根据物理统计信息,来估计各种 执行方案的执行代价,从中选取一种最优(代价最小)的执行方案。在 这种优化方式下,如果表上建有索引,是否使用索引,将取决于查询的 “选中度”(selectivity)。 什么是选中度?举个例子,假设表中有一名为“年龄”的字段,有 一查询需要查出该表中所有“年龄”不超过50岁的记录,如果表中有70% 的记录满足这一条件,则称该查询的选中度为70。 当选中度超过某一预先给定的值P(P的大小取决于系统的具体实现) 时,遍历整个表的开销比搜索索引再定位的开销要小,此时系统将不使 用索引。 通过统计字段的值分布,可以估计查询的选中度,如果它大于P,系 统将不使用索引,直接遍历表。这是一种非常重要的统计信息,它还可 用于估计连接操作结果集的大小。 当然,当查询比较固定时,用户也可以根据自己对应用的理解预先估

C#中的日期时间函数

DateTime dt = DateTime.Now; Label1.Text = dt.ToString();//2005-11-5 13:21:25 Label2.Text = dt.ToFileTime().ToString();//127756416859912816 Label3.Text = dt.ToFileTimeUtc().ToString();//127756704859912816 Label4.Text = dt.ToLocalTime().ToString();//2005-11-5 21:21:25 Label5.Text = dt.ToLongDateString().ToString();//2005年11月5日Label6.Text = dt.ToLongTimeString().ToString();//13:21:25 Label7.Text = dt.ToOADate().ToString();//38661.5565508218 Label8.Text = dt.ToShortDateString().ToString();//2005-11-5 Label9.Text = dt.ToShortTimeString().ToString();//13:21 Label10.Text = dt.ToUniversalTime().ToString();//2005-11-5 5:21:25 ?2005-11-5 13:30:28.4412864 Label1.Text = dt.Year.ToString();//2005 Label2.Text = dt.Date.ToString();//2005-11-5 0:00:00 Label3.Text = dt.DayOfWeek.ToString();//Saturday Label4.Text = dt.DayOfYear.ToString();//309 Label5.Text = dt.Hour.ToString();//13 Label6.Text = https://www.wendangku.net/doc/e32699066.html,lisecond.ToString();//441 Label7.Text = dt.Minute.ToString();//30 Label8.Text = dt.Month.ToString();//11 Label9.Text = dt.Second.ToString();//28 Label10.Text = dt.Ticks.ToString();//632667942284412864 Label11.Text = dt.TimeOfDay.ToString();//13:30:28.4412864 Label1.Text = dt.ToString();//2005-11-5 13:47:04 Label2.Text = dt.AddYears(1).ToString();//2006-11-5 13:47:04 Label3.Text = dt.AddDays(1.1).ToString();//2005-11-6 16:11:04 Label4.Text = dt.AddHours(1.1).ToString();//2005-11-5 14:53:04 Label5.Text = dt.AddMilliseconds(1.1).ToString();//2005-11-5 13:47:04 Label6.Text = dt.AddMonths(1).ToString();//2005-12-5 13:47:04 Label7.Text = dt.AddSeconds(1.1).ToString();//2005-11-5 13:47:05

数据库复习题集答案解析

一、选择题: 1、DB,DBMS和DBS三者的关系是(B) A、DB包括DBMS和DBS B、DBS包括DB和DBMS C、DBMS包括DBS和DB D、DBS与DB、DBMS无关 2、假定学生关系式S(S#,SNAME,SEX,AGE),课程关系式C(C#,CNAME,TEACHER),学生选课关系是SC(S#,C#,GRAND)。要查找选修“COMPUTER”课程的“女”学生,将涉及到关系(D) A、S B、SC,C C、S,SC D、S,C,SC 3、将E-R图转换为关系模式时,如果两实体间的联系是m:n,下列说确的是(C) A、将m方主键(主码)和联系的属性纳入n方的属性中 B、将m方属性和n方属性中均增加一个表示级别的属性 C、增加一个关系表示联系,其中纳入m方和n方的主键(主码) D、将n方主键(主码)和联系的属性纳入m方的属性中 4、由SELECT—FROM—WHERE—GROUP—ORDER组成的SQL语句,在被DBMS处理时,各字句的执行次序为(C) A、SELECT—FROM—WHERE—GROUP—ORDER B、FROM —SELECT—WHERE—GROUP—ORDER C、FROM —WHERE—GROUP—SELECT—ORDER D、SELECT—FROM—GROUP—WHERE—ORDER 5、以下不是数据库技术所具备的特点是(D) A、数据结构化 B、数据冗余小 C、有较高的数据独立性 D、数据联系弱 6、在信息模型的“学生”尸体中,对每个学生的具体情况的描述,称为(A) A、实体值 B、实体型 C、属性值 D、属性型 7、关系数据库三级模式中的(B),可用视图实现。 A、模式 B、外模式 C、存储模式 D、模式 8、可用于区别实体集中不同个体的属性或属性集合,称为该实体的(B) A、属性型 B、键 C、外部键 D、实体型 9、设有一个体育项目可以有多个运动员报名,一个运动员课参加多个项目,运动员与体育项目之间是(D) A、一对一的联系 B、一对多的联系 C、多对一的联系 D、多对多的联系 10、关系R与关系S只有1个公共属性,T1是R与S作等值连接的结果,T2是R与S作自然连接的结果,则(D) A、T1的属性个数等于T2的属性个数 B、T1的属性个数小于T2的属性个数 C、T1的属性个数大于或等于T2的属性个数 D、T1的属性个数大于T2的属性个数 11、数据库系统是由应用程序、DBMS、DB以及DBA组成。其中核心部分是(C) A、应用程序 B、DBA C、DBMS D、DB 12、下列集函数中不忽略空值(NULL)的是(A) A、COUNT(*) B、MAX(列名) C、SUM(列名) D、A VG(列名) 13、一个关系中的候选关键字(B) A、至少一个 B、可多个 C、必须多个 D、至少3个 14、在数据库设计中,具有最小性、唯一性和非空性的是(B) A、索引 B、关系模型主关键字(主码) C、外关键字(外码) D、约束 15、常用的关系运算时关系代数和(C) A、集合代数 B、逻辑演算 C、关系演算 D、集合演算 16、在基本层次联系中,记录型之间的联系是(B) A、一对一联系 B、一对多联系 C、多对多联系 D、多对一联系 17、关于冗余数据的叙述中,不正确的是(C) A、冗余的存在容易破坏数据库的完整性 B、冗余的存在给数据库的维护增加困难 C、不应该在数据库中存储任何冗余数据 D、冗余数据是指可由基本数据导出的数据 18、五种基本关系代数运算分别(D)

数据库常用函数汇总统计

实验二(续):利用SQL语句查询 三、常用库函数及统计汇总查询 1、求学号为 S1学生的总分和平均分; select sum(score) as TotalScore,avg(score)as AveScore from sc where sno='S1' 2、求选修 C1号课程的最高分、最低分及之间相差的分数; select max(score)as MaxScore, min(score)as MinScore, max(score)- min(score)as diff from sc where cno='C1' 3、求选修 C1号课程的学生人数和最高分; select count(distinct sno),max(score) from sc where cno='C 1' 4、求计算机系学生的总数; select count(sno) from s where dept=' 计算机 ' 5、求学校中共有多少个系; select count(distinct dept) as DeptNum from s 6、统计有成绩同学的人数; select count(score) from sc 7、利用特殊函数 COUNT(*)求计算机系学生的总数; select count(*) from s where dept=' 计算机 '

8、利用特殊函数 COUNT(*)求女学生总数和平均年龄;select count(*),avg(age) from s where sex=' 女 ' 9、利用特殊函数 COUNT(*)求计算机系女教师的总数。select count(*) from t where dept=' 计算机 'and sex=' 女 ' 四、分组查询及排序 1、查询各个教师的教师号及其任课门数; select tno,count(*)as c_num from tc group by tno 2、按系统计女教师的人数; select dept,count(tno) from t where sex=' 女 ' group by dept 3、查询选修两门以上课程的学生的学号和选课门数;select sno,count(*)as sc_num from sc group by sno having count(*)>2 4、查询平均成绩大于 70分的课程号和平均成绩; select cno,avg(score) from sc group by cno having avg(score)>70 5、查询选修 C1的学生学号和成绩,并按成绩降序排列;select sno,score

数据库索引的作用及实例(精)

1. 1.索引作用 2. 在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术, 能够大大提高查询效率。特别是当数据量非常大, 查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍。 3. 4. 例如,有 3个未索引的表 t1、 t2、 t3,分别只包含列 c1、 c2、 c3,每个表分别含有 1000行数据组成,指为 1~1000的数值,查找对应值相等行的查询如下所示。 5. 6. SELECT c1,c2,c3 FROM t1,t2,t3 WHERE c1=c2 AND c1=c3 7. 8. 此查询结果应该为 1000行, 每行包含 3个相等的值。在无索引的情况下处理此查询, 必须寻找 3个表所有的组合, 以便得出与 WHERE 子句相配的那些行。而可能的组合数目为 1000×1000×1000(十亿,显然查询将会非常慢。 9. 10. 如果对每个表进行索引,就能极大地加速查询进程。利用索引的查询处理如下。 11. 12. (1从表 t1中选择第一行,查看此行所包含的数据。 13. 14. (2使用表 t2上的索引,直接定位 t2中与 t1的值匹配的行。类似,利用表 t3上的索引,直接定位 t3中与来自 t1的值匹配的行。

15. 16. (3 扫描表 t1的下一行并重复前面的过程, 直到遍历 t1中所有的行。 17. 18. 在此情形下,仍然对表 t1执行了一个完全扫描,但能够在表 t2和 t3上进行索引查找直接取出这些表中的行, 比未用索引时要快一百万倍。 19. 20. 利用索引, MySQL 加速了 WHERE 子句满足条件行的搜索,而在多表连接查询时,在执行连接时加快了与其他表中的行匹配的速度。 21. 22.2. 创建索引 23. 在执行 CREATE TABLE语句时可以创建索引, 也可以单独用 CREATE INDEX或 ALTER TABLE来为表增加索引。 24. 25.1. ALTER TABLE 26.ALTER TABLE用来创建普通索引、 UNIQUE 索引或 PRIMARY KEY索引。 27. 28. 29. 30.ALTER TABLE table_name ADD INDEX index_name (column_list 31. 32.ALTER TABLE table_name ADD UNIQUE (column_list 34.ALTER TABLE table_name ADD PRIMARY KEY (column_list 35.

数据库建立索引的原则

数据库建立索引的原则 使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构,例如employee 表的姓(lname)列。如果要按姓查找特定职员,与必须搜索表中的所有行相比,索引会帮助您更快地获得该信息。 索引是一个单独的、物理的数据库结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。 索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引的方式与您使用书籍中的索引的方式很相似:它搜索索引以找到特定值,然后顺指针找到包含该值的行。 在数据库关系图中,您可以在选定表的“索引/键”属性页中创建、编辑或删除每个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。 建立索引的优点 1.大大加快数据的检索速度; 2.创建唯一性索引,保证数据库表中每一行数据的唯一性; 3.加速表和表之间的连接; 4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。 索引的缺点 1.索引需要占物理空间。 2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。 根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索引、主键索引和聚集索引。有关数据库所支持的索引功能的详细信息,请参见数据库文档。 提示尽管唯一索引有助于定位信息,但为获得最佳性能结果,建议改用主键或唯一约束。有关这些约束的更多信息,请参见主键约束和唯一约束。 唯一索引

唯一索引是不允许其中任何两行具有相同索引值的索引。 当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。例如,如果在employee 表中职员的姓(lname) 上创建了唯一索引,则任何两个员工都不能同姓。 有关唯一索引的更多信息,请参见创建唯一索引。 主键索引 数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。 在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。有关主键的更多信息,请参见定义主键。 聚集索引 在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。 如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。 一、索引 1. 概念:索引是揭示文献内容出处,提供文献查考线索的工具书。 2. 类型:种类很多,从不同的角度可以划分出不同的类型。按文种分,可以分为中文索引的外文索引;按收录范围分,可以分为综合性索引和专题性索引;按收录文献的时间分,可以分为近期索引和回溯性索引;按索引款目的标目分,可以分为题名索引、著者索引、语词索引、主题索引、分类索引等。 3. 功能:揭示文献的内容和指引读者查找信息 4. 作用:索引揭示了一书、一刊的基本情况,如篇目、文句。可以深入、完整、详细、系统地为读者提所需文献的具体线索。 铁律一:天下没有免费的午餐,使用索引是需要付出代价的。 索引的优点有目共睹,但是,却很少有人关心过采用索引所需要付出的成本。若数据库管理员能够对索引所需要付出的代价有一个充分的认识,也就不会那么随意到处建立索引了。

用c++编写计算日期的函数

14.1 分解与抽象 人类解决复杂问题采用的主要策略是“分而治之”,也就是对问题进行分解,然后分别解决各个子问题。著名的计算机科学家Parnas认为,巧妙的分解系统可以有效地系统的状态空间,降低软件系统的复杂性所带来的影响。对于复杂的软件系统,可以逐个将它分解为越来越小的组成部分,直至不能分解为止。这样在小的分解层次上,人就很容易理解并实现了。当所有小的问题解决完毕,整个大的系统也就解决完毕了。 在分解过程中会分解出很多类似的小问题,他们的解决方式是一样的,因而可以把这些小问题,抽象出来,只需要给出一个实现即可,凡是需要用到该问题时直接使用即可。 案例日期运算 给定日期由年、月、日(三个整数,年的取值在1970-2050之间)组成,完成以下功能: (1)判断给定日期的合法性; (2)计算两个日期相差的天数; (3)计算一个日期加上一个整数后对应的日期; (4)计算一个日期减去一个整数后对应的日期; (5)计算一个日期是星期几。 针对这个问题,很自然想到本例分解为5个模块,如图14.1所示。 图14.1日期计算功能分解图 仔细分析每一个模块的功能的具体流程: 1. 判断给定日期的合法性: 首先判断给定年份是否位于1970到2050之间。然后判断给定月份是否在1到12之间。最后判定日的合法性。判定日的合法性与月份有关,还涉及到闰年问题。当月份为1、3、5、7、8、10、12时,日的有效范围为1到31;当月份为4、6、9、11时,日的有效范围为1到30;当月份为2时,若年为闰年,日的有效范围为1到29;当月份为2时,若年不为闰年,日的有效范围为1到28。

图14.2日期合法性判定盒图 判断日期合法性要要用到判断年份是否为闰年,在图14.2中并未给出实现方法,在图14.3中给出。 图14.3闰年判定盒图 2. 计算两个日期相差的天数 计算日期A (yearA 、monthA 、dayA )和日期B (yearB 、monthB 、dayB )相差天数,假定A 小于B 并且A 和B 不在同一年份,很自然想到把天数分成3段: 2.1 A 日期到A 所在年份12月31日的天数; 2.2 A 之后到B 之前的整年的天数(A 、B 相邻年份这部分没有); 2.3 B 日期所在年份1月1日到B 日期的天数。 A 日期 A 日期12月31日 B 日期 B 日期1月1日 整年部分 整年部分 图14.4日期差分段计算图 若A 小于B 并且A 和B 在同一年份,直接在年内计算。 2.1和2.3都是计算年内的一段时间,并且涉及到闰年问题。2.2计算整年比较容易,但

数据库管理系统是位于用户与操作系统之间的一层数据管理软件

数据库管理系统是位于用户与操作系统之间的一层数据管理软件,用于科学地组织和存储数据、高效地获取和维护数据。DBMS 的主要功能包括数据定义功能、数据操纵功能、数据库的运行管理功能、数据库的建立和维护功能。 数据模型通常由数据结构、数据操作和完整性约束三部分组成。 实体型:具有相同属性的实体具有相同的特征和性质,用实体名及其属性名集合来抽象和刻画同类实体,称为实体型 数据库系统的三级模式结构由外模式、模式和内模式组成。数据库系统在这三级模式之间提供了两层映像:外模式/模式映像和模式/内模式映像。正是这两层映像保证了数据库系统中的数据能够具有较高的逻辑独立性和物理独立性。数据库系统一般由数据库、数据库管理系统(及其开发工具)、应用系统、数据库管理员和用户构成 sQL 语言的特点:(l)综合统一。(2)高度非过程化。(3)面向集合的操作方式。(4)以同一种语法结构提供两种使用方式。(5)语言简捷,易学易用。 基本表,视图。两者的区别和联系:基本表是本身独立存在的表,在sQL 中一个关系就对应一个表。视图是从一个或几个基本表导出的表。视图本身不独立存储在数据库中,是一个虚表。即数据库中只存放视图的定义而不存放视图对应的数据,这些数据仍存放在导出视图的基本表中。视图在概念上与基本表等同,用户可以如同基本表那样使用视图,可以在视图上再定义视图。 数据库的安全性是指保护数据库以防止不合法的使用所造成的数据泄露、更改或破坏。实现数据库安全性控制的常用方法和技术有:用户标识和鉴别,存取控制,视图机制,审计,数据加密。 数据库的完整性是指数据的正确性和相容性。完整性约束条件是指数据库中的数据应该满足的语义约束条件。参照完整性体现在外码,用户定义的完整性数据类型定义范围。DBMS 的完整性控制机制应具有三个方面的功能:( l )提供定义完整性约束条件的机制;( 2 )提供完整性检查的方法;( 3 )违约处理:如果发现用户的操作请求使数据违背了完整性约束条件,则采取一定的动作来保证数据的完整性。 数据库设计是指对于一个给定的应用环境,构造优化的数据库逻辑模式和物理结构,并建立数据库及其应用系统,使之能够有效地存储和管理数据,满足各种用户的应用需求,包括信息管理要求和数据操作要求。设计步骤:概念结构的设计方法有多种,其中最经常采用的策略是自底向上方法,该方法的设计步骤通常分为两步:第 1 步是抽象数据并设计局部视图,第 2 步是集成局部视图,得到全局的概念结构。 规范化理论为数据库设计人员判断关系模式的优劣提供了理论标准,可用以指导关系数据模型的优化,用来预测模式可能出现的问题,为设计人员提供了自动产生各种模式的算法工具,使数据库设计工作有了严格的理论基础。 求供应工程J1零件的供应商号码SNO:πSno(σSno=‘J1’(SPJ))

数据库中一些常用的名词解释

◆DB:数据库(Database),DB是统一管理的相关数据的集合。DB能为各种用户共享,具有最小冗余度,数据间联系密切,而又有较高的数据独立性。 ◆DBMS:数据库管理系统(Database Management System),DBMS是位于用户与操作系统之间的一层数据管理软件,为用户或应用程序提供访问DB的方法,包括DB 的建立、查询、更新及各种数据控制。DBMS总是基于某种数据模型,可以分为层次型、网状型、关系型、面向对象型DBMS。 ◆DBS:数据库系统(Database System),DBS是实现有组织地、动态地存储大量关联数据,方便多用户访问的计算机软件、硬件和数据资源组成的系统,即采用了数据库技术的计算机系统。 ◆1:1联系:如果实体集E1中的每个实体最多只能和实体集E2中的一个实体有联系,反之亦然,好么实体集E1对E2的联系称为“一对一联系”,记为“1:1”。 ◆1:N联系:如果实体集E1中每个实体与实体集E2中任意个(零个或多个)实体有联系,而E2中每个实体至多和E1中的一个实体有联系,那么E1对E2的联系是“一对多联系”,记为“1:N”。 ◆M:N联系:如果实体集E1中每个实体与实体集E2中任意个(零个或多个)实体有联系,反之亦然,那么E1对E2的联系是“多对多联系”,记为“M:N”。 ◆数据模型:表示实体类型及实体类型间联系的模型称为“数据模型”。它可分为两种类型:概念数据模型和结构数据模型。 ◆概念数据模型:它是独门于计算机系统的模型,完全不涉及信息在系统中的表示,只是用来描述某个特定组织所关心的信息结构。 ◆结构数据模型:它是直接面向数据库的逻辑结构,是现实世界的第二层抽象。这类模型涉及到计算机系统和数据库管理系统,所以称为“结构数据模型”。结构数据模型应包含:数据结构、数据操作、数据完整性约束三部分。它主要有:层次、网状、关系三种模型。

浅谈B-树以及数据库聚集索引、非聚集索引

浅谈B-树以及数据库聚集索引、非聚集索引 对数据库索引的关注从未淡出我的们的讨论,那么数据库索引是什么样的?聚集索引与非聚集索引有什么不同?这段时间,了解数据库索引的一些相关知识,又查阅了一些相关资料,希望本文对大家有一定的帮助,在此借此机会与 大家分享一些数据库聚集索引、数据库非聚集索引概念、实现过程、以及优缺点。有不少存疑的地方,诚心希望各位不吝赐教指正,共同进步。 我们常见的数据库系统,其索引使用的数据结构多是B-Tree或者B+Tree。例如,MySql使用的是B+Tree,Oracle及Sysbase使用的是B-Tree。所以在 最开始,简单地介绍一下B-Tree。 1.B-Tree 在严蔚敏编著的《数据结构》种对B-Tree有如下定义: B-Tree是一种平衡的多路查找树,它在文件系统中很有用,一棵m阶的 B-树,或为空树,或为满足下列特性的m叉树: 1)树中每个结点至多有m个孩子; 2)若根节点不是叶子节点,则至少有两棵子树; 3)除根结点和叶结点外,其它每个结点至少有m/2(上取整)个孩子; 4)所有叶结点在同一层,叶结点不包含任何关键字信息; 5)有K个关键字的非叶结点恰好包含K+1个孩子 另外,对于一个结点,其内部的关键字是从小到大排序的。以下是B-Tree (m=4)的样例:

对于每个结点,主要包含一个关键字数组key[],一个指针数组(指向儿子)son[]。在B-Tree内,查找的流程是:使用顺序查找(数组长度较短时)或折 半查找方法查找key[]数组,若找到关键字k,则返回该结点的地址及K在key[]中的位置;否则,可确定k在某个key[i]和key[i+1]之间,则从son[i]所指的子结点继续查找,直到在某结点中查找成功;或直至找到叶结点且叶结点中的查 找仍不成功时,查找过程失败。 接着,我们使用以下图片演示如何生成B-Tree(m=4,依次插入1~6):从图可见,当我们插入关键字4时,由于原结点已经满了,故进行分裂,基本 按一半的原则进行分裂,然后取出中间的关键字2,升级(这里是成为根结点)。其它的依类推,就是这样一个大概的过程。

日期函数使用大全

//今天 DateTime.Now.Date.ToString(); //昨天 DateTime.Now.AddDays(-1).ToString(); //明天 DateTime.Now.AddDays(1).ToString(); //本周第1天及最后1天 (要注意的是这里的每一周是从周日始至周六止) DateTime.Today.AddDays (-(int)DateTime.Today.DayOfWeek).ToString(); DateTime.Today.AddDays (6 -(int)DateTime.Today.DayOfWeek).ToString(); //上周, 一周是7天,上周就是本周再减去7天 DateTime.Today.AddDays (-(int)DateTime.Today.DayOfWeek - 7).ToString(); DateTime.Today.AddDays (6 -(int)DateTime.Today.DayOfWeek - 7).ToString(); //下周 DateTime.Today.AddDays (-(int)DateTime.Today.DayOfWeek + 7).ToString(); DateTime.Today.AddDays (6 -(int)DateTime.Today.DayOfWeek + 7).ToString(); 巧用C#裡ToString的字符格式化 //本月第1天及最后1天 DateTime.Now.ToString("yyyy-MM-01"); DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddMonths(1).AddDays(-1).To String(); //上个月第1天及最后1天 DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddMonths(-1).ToString(); DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddDays(-1).ToString(); //下个月第1天及最后1天 DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddMonths(1).ToString(); DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddMonths(2).AddDays(-1).To String(); //今年第1天 DateTime.Parse(DateTime.Now.ToString("yyyy-01-01")).ToString(); //今年最后1天 DateTime.Parse(DateTime.Now.ToString("yyyy-01-01")).AddYears(1).AddDays(-1).ToS tring(); //去年第1天及最后1天 DateTime.Parse(DateTime.Now.ToString("yyyy-01-01")).AddYears(-1).ToString(); DateTime.Parse(DateTime.Now.ToString("yyyy-01-01")).AddDays(-1).ToString();

相关文档
相关文档 最新文档