??xml version="1.0" encoding="utf-8" standalone="yes"?>
写于Q?002/08 最后更斎ͼ
11/29/2006 17:23:30
Feed Back >> (Read this before you ask question)<q告>
版权声明Q可以Q意{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及本声?br />http://www.chedong.com/tech/lucene.html
关键词:Lucene java full-text search engine Chinese word segment
内容摘要Q?/p>
Lucene是一个基于Java的全文烦引工具包?/p>
Lucene不是一个完整的全文索引应用Q而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各U应用中实现针对应用的全文烦?索功能?/p>
Lucene的作者:Lucene的A献?a >Doug Cutting是一位资深全文烦?索专Ӟ曄是V-Twin搜烦引擎(Apple的Copland操作pȝ的成׃一)的主要开发者,后在Excite担Q高pȝ架构设计师,目前从事于一些INTERNET底层架构的研I。他贡献出的Lucene的目标是为各U中型应用E序加入全文索功能?/p>
Lucene的发展历E:早先发布在作者自qwww.lucene.comQ后来发布在SourceForgeQ?001q年底成为APACHE基金会jakarta的一个子目Q?a >http://jakarta.apache.org/lucene/
已经有很多Java目都用了Lucene作ؓ其后台的全文索引引擎Q比较著名的有:
Eclipse:ZJava的开攑ּ发^収ͼ帮助部分的全文烦引用了Lucene
对于中文用户来说Q最兛_的问题是其是否支持中文的全文索。但通过后面对于Lucene的结构的介绍Q你会了解到׃Lucene良好架构设计Q对中文的支持只需对其语言词法分析接口q行扩展p实现对中文检索的支持?/p>
Lucene的API接口设计的比较通用Q输入输出结构都很像数据库的?=>记录==>字段Q所以很多传l的应用的文件、数据库{都可以比较方便的映到Lucene的存储结?接口中。M上看Q可以先?b>Lucene当成一个支持全文烦引的数据库系l?/b>?/p>
比较一下Lucene和数据库Q?/p>
Lucene | 数据?/td> |
索引数据源:doc(field1,field2...) doc(field1,field2...) |
索引数据源:record(field1,field2...) record(field1..) |
DocumentQ一个需要进行烦引的“单元?br />一个Document由多个字D늻?/td> | RecordQ记录,包含多个字段 |
FieldQ字D?/td> | FieldQ字D?/td> |
HitsQ查询结果集Q由匚w的Documentl成 | RecordSetQ查询结果集Q由多个Recordl成 |
全文??like "%keyword%"
通常比较厚的书籍后面常常附关键词索引表(比如Q北京:12, 34,上vQ?,77……)Q它能够帮助读者比较快地找到相兛_容的늠。而数据库索引能够大大提高查询的速度原理也是一P惛_一下通过书后面的索引查找的速度要比一一地d定w多少倍……而烦引之所以效率高Q另外一个原因是它是排好序的?b>对于索系l来说核心是一个排序问?/b>?/p>
׃数据库烦引不是ؓ全文索引设计的,因此Q?b>使用like "%keyword%"Ӟ数据库烦引是不v作用?/b>Q在使用like查询Ӟ搜烦q程又变成类g一页M的遍历过E了Q所以对于含有模p查询的数据库服务来_LIKEҎ能的危x极大的。如果是需要对多个关键词进行模p匹配:like"%keyword1%" and like "%keyword2%" ...其效率也可惌知了?/p>
所以徏立一个高效检索系l的关键是徏立一个类gU技索引一L反向索引机制Q将数据源(比如多篇文章Q排序顺序存储的同时Q有另外一个排好序的关键词列表Q用于存储关键词==>文章映射关系Q利用这L映射关系索引Q[关键?=>出现关键词的文章~号Q出现次敎ͼ甚至包括位置Qv始偏U量Q结束偏U量Q,出现频率]Q检索过E就是把模糊查询变成多个可以利用索引的精查询的逻辑l合的过E?/b>。从而大大提高了多关键词查询的效率,所以,全文索问题归l到最后是一个排序问题?/p>
由此可以看出模糊查询相对数据库的_查询是一个非怸定的问题,q也是大部分数据库对全文索支持有限的原因。Lucene最核心的特征是通过Ҏ的烦引结构实C传统数据库不擅长的全文烦引机Ӟq提供了扩展接口Q以方便针对不同应用的定制?/p>
可以通过一下表格对比一下数据库的模p查询:
Lucene全文索引引擎 | 数据?/td> | |
索引 | 数据源中的数据都通过全文索引一一建立反向索引 | 对于LIKE查询来说Q数据传l的索引是根本用不上的。数据需要逐个便利记录q行GREP式的模糊匚wQ比有烦引的搜烦速度要有多个数量U的下降?/td> |
匚w效果 | 通过词元(term)q行匚wQ通过语言分析接口的实玎ͼ可以实现对中文等非英语的支持?/td> | 使用Qlike "%net%" 会把netherlands也匹配出来, 多个关键词的模糊匚wQ用like "%com%net%"Q就不能匚w词序颠倒的xxx.net..xxx.com |
匚w?/td> | 有匹配度法Q将匚wE度Q相似度Q比较高的结果排在前面?/td> | 没有匚wE度的控Ӟ比如有记录中net出现5词和出现1ơ的Q结果是一L?/td> |
l果输出 | 通过特别的算法,最匚w度最高的?00条结果输出,l果集是~冲式的批量读取的?/td> | q回所有的l果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放q些临时l果集?/td> |
可定制?/td> | 通过不同的语a分析接口实现Q可以方便的定制出符合应用需要的索引规则Q包括对中文的支持) | 没有接口或接口复杂,无法定制 |
l论 | 高负载的模糊查询应用Q需要负责的模糊查询的规则,索引的资料量比较?/td> | 使用率低Q模p匹配规则简单或者需要模p查询的资料量少 |
全文索和数据库应用最大的不同在于Q让
最相关?/span>
?00条结果满?8%以上用户的需?br />
Lucene的创C处:
大部分的搜烦Q数据库Q引擎都是用B树结构来l护索引Q烦引的更新会导致大量的IO操作QLucene在实CQ对此稍微有所改进Q不是维护一个烦引文Ӟ而是在扩展烦引的时候不断创建新的烦引文Ӟ然后定期的把q些新的烦引文件合q到原先的大索引中(针对不同的更新策略,Ҏ的大可以调_Q这样在不媄响检索的效率的前提下Q提高了索引的效率?/p>
Lucene和其他一些全文检索系l?应用的比较:
Lucene | 其他开源全文检索系l?/td> | |
增量索引和批量烦?/td> | 可以q行增量的烦?Append)Q可以对于大量数据进行批量烦引,q且接口设计用于优化扚w索引和小扚w的增量烦引?/td> | 很多pȝ只支持批量的索引Q有时数据源有一点增加也需要重建烦引?/td> |
数据?/td> | Lucene没有定义具体的数据源Q而是一个文档的l构Q因此可以非常灵zȝ适应各种应用Q只要前端有合适的转换器把数据源{换成相应l构Q, | 很多pȝ只针对网,~Z其他格式文档的灵zL?/td> |
索引内容抓取 | Lucene的文档是由多个字D늻成的Q甚臛_以控刉些字D需要进行烦引,那些字段不需要烦引,q一步烦引的字段也分为需要分词和不需要分词的cdQ?br /> 需要进行分词的索引Q比如:标题Q文章内容字D?br /> 不需要进行分词的索引Q比如:作?日期字段 | ~Z通用性,往往文档整个烦引了 |
语言分析 | 通过语言分析器的不同扩展实现Q?br />可以qo掉不需要的词:an the of {, 西文语法分析Q将jumps jumped jumper都归l成jumpq行索引/?br />非英文支持:对亚z语aQ阿拉伯语言的烦引支?/td> | ~Z通用接口实现 |
查询分析 | 通过查询分析接口的实玎ͼ可以定制自己的查询语法规则: 比如Q?多个关键词之间的 + - and or关系{?/td> | |
q发讉K | 能够支持多用L使用 |
对于中文来说Q全文烦引首先还要解决一个语a分析的问题,对于英文来说Q语句中单词之间是天焉过I格分开的,但亚z语a的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行烦引的话,q个词如何切分出来就是一个很大的问题?/p>
首先Q肯定不能用单个字符?si-gram)为烦引单元,否则查“上”时Q不能让含有“v上”也匚w?/p>
但一句话Q“北京天安门”,计算机如何按照中文的语言习惯q行切分呢?
“北?天安门?q是“北 ?天安门”?让计机能够按照语言习惯q行切分Q往往需要机器有一个比较丰富的词库才能够比较准的识别句中的单词?/p>
另外一个解决的办法是采用自动切分算法:单词按?元语?bigram)方式切分出来Q比如:
"北京天安? ==> "北京 京天 天安 安门"?/p>
q样Q在查询的时候,无论是查?北京" q是查询"天安?Q将查询词组按同L规则q行切分Q?北京"Q?天安安门"Q多个关键词之间按与"and"的关pȝ合,同样能够正确地映到相应的烦引中。这U方式对于其他亚z语aQ韩文,日文都是通用的?/p>
Z自动切分的最大优Ҏ没有词表l护成本Q实现简单,~点是烦引效率低Q但对于中小型应用来_Z2元语法的切分q是够用的。基?元切分后的烦引一般大和源文件差不多Q而对于英文,索引文g一般只有原文g?0%-40%不同Q?/p>
|
自动切分 | 词表切分 |
实现 | 实现非常?/td> | 实现复杂 |
查询 | 增加了查询分析的复杂E度Q?/td> | 适于实现比较复杂的查询语法规?/td> |
存储效率 | 索引冗余大,索引几乎和原文一样大 | 索引效率高,为原文大的30Q左?/td> |
l护成本 | 无词表维护成?/td> | 词表l护成本非常高:中日韩等语言需要分别维护?br />q需要包括词频统计等内容 |
适用领域 | 嵌入式系l:q行环境资源有限 分布式系l:无词表同步问?br />多语a环境Q无词表l护成本 |
Ҏ询和存储效率要求高的专业搜烦引擎 |
目前比较大的搜烦引擎的语a分析法一般是Z以上2个机制的l合。关于中文的语言分析法Q大家可以在Google查关键词"wordsegment search"能找到更多相关的资料?/p>
下蝲Q?a >http://jakarta.apache.org/lucene/
注意QLucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCCQJavaCompilerCompilerQ纯Java的词法分析生成器Q,所以如果从源代码编译或需要修改其中的QueryParser、定制自q词法分析器,q需要从https://javacc.dev.java.net/下蝲javacc?/p>
lucene的组成结构:对于外部应用来说索引模块(index)和检索模?search)是主要的外部应用入口
org.apache.Lucene.search/ | 搜烦入口 |
org.apache.Lucene.index/ | 索引入口 |
org.apache.Lucene.analysis/ | 语言分析?/td> |
org.apache.Lucene.queryParser/ | 查询分析?/td> |
org.apache.Lucene.document/ | 存储l构 |
org.apache.Lucene.store/ | 底层IO/存储l构 |
org.apache.Lucene.util/ | 一些公用的数据l构 |
单的例子演示一下Lucene的用方法:
索引q程Q从命o行读取文件名Q多个)Q将文g分\?path字段)和内?body字段)2个字D进行存储,q对内容q行全文索引Q烦引的单位是Document对象Q每个Document对象包含多个字段Field对象Q针对不同的字段属性和数据输出的需求,对字D还可以选择不同的烦?存储字段规则Q列表如下:Ҏ | 切词 | 索引 | 存储 | 用?/th> |
---|---|---|---|---|
Field.Text(String name, String value) | Yes | Yes | Yes | 切分词烦引ƈ存储Q比如:标题Q内容字D?/td> |
Field.Text(String name, Reader value) | Yes | Yes | No | 切分词烦引不存储Q比如:META信息Q?br />不用于返回显C,但需要进行检索内?/td> |
Field.Keyword(String name, String value) | No | Yes | Yes | 不切分烦引ƈ存储Q比如:日期字段 |
Field.UnIndexed(String name, String value) | No | No | Yes | 不烦引,只存储,比如Q文件\?/td> |
Field.UnStored(String name, String value) | Yes | Yes | No | 只全文烦引,不存?/td> |
public class IndexFiles {
//使用ҎQ? IndexFiles [索引输出目录] [索引的文件列表] ...
public static void main(String[] args) throws Exception {
String indexPath = args[0];
IndexWriter writer;
//用指定的语言分析器构造一个新的写索引器(W?个参数表C是否ؓq加索引Q?br /> writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);
for (int i=1; i<args.length; i++) {
System.out.println("Indexing file " + args[i]);
InputStream is = new FileInputStream(args[i]);
//构造包?个字DField的Document对象
//一个是路径path字段Q不索引Q只存储
//一个是内容body字段Q进行全文烦引,q存?br /> Document doc = new Document();
doc.add(Field.UnIndexed("path", args[i]));
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));
//文档写入烦?br /> writer.addDocument(doc);
is.close();
};
//关闭写烦引器
writer.close();
}
}
索引q程中可以看刎ͼ
索过E和l果昄Q?/p>
搜烦l果q回的是Hits对象Q可以通过它再讉KDocument==>Field中的内容?/p>
假设Ҏbody字段q行全文索,可以查询结果的path字段和相应查询的匚w?score)打印出来Q?/p>
public class Search {在整个检索过E中Q语a分析器,查询分析器,甚至搜烦器(SearcherQ都是提供了抽象的接口,可以Ҏ需要进行定制?
public static void main(String[] args) throws Exception {
String indexPath = args[0], queryString = args[1];
//指向索引目录的搜索器
Searcher searcher = new IndexSearcher(indexPath);
//查询解析器:使用和烦引同L语言分析?br /> Query query = QueryParser.parse(queryString, "body",
new SimpleAnalyzer());
//搜烦l果使用Hits存储
Hits hits = searcher.search(query);
//通过hits可以讉K到相应字D늚数据和查询的匚w?br /> for (int i=0; i<hits.length(); i++) {
System.out.println(hits.doc(i).get("path") + "; Score: " +
hits.score(i));
};
}
}
化的查询分析?/b>
个h感觉lucene成ؓJAKARTA目后,d了太多的旉用于调试日趋复杂QueryParserQ而其中大部分是大多数用户q不很熟悉的Q目前LUCENE支持的语法:
Query ::= ( Clause )*
Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")
中间的逻辑包括Qand or + - &&||{符P而且q有"短语查询"和针对西文的前缀/模糊查询{,个h感觉对于一般应用来_q些功能有一些华而不实,其实能够实现目前cM于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择?/p>
d修改删除指定记录QDocumentQ?/b>
Lucene提供了烦引的扩展机制Q因此烦引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另徏索引Q然后利用IndexReader.delete(Termterm)Ҏ通过q个记录ID删除相应的Document?/p>
Ҏ某个字段值的排序功能
lucene~省是按照自q相关度算法(scoreQ进行结果排序的Q但能够Ҏ其他字段q行l果排序是一个在LUCENE的开发邮件列表中l常提到的问题,很多原先Z数据库应用都需要除了基于匹配度QscoreQ以外的排序功能。而从全文索的原理我们可以了解刎ͼM不基于烦引的搜烦q程效率都会D效率非常的低Q如果基于其他字D늚排序需要在搜烦q程中访问存储字D,速度回大大降低,因此非常是不可取的?/p>
但这里也有一个折中的解决ҎQ在搜烦q程中能够媄响排序结果的只有索引中已l存储的docID和scoreq?个参敎ͼ所以,Zscore以外的排序,其实可以通过数据源预先排好序,然后ҎdocIDq行排序来实现。这样就避免了在LUCENE搜烦l果外对l果再次q行排序和在搜烦q程中访问不在烦引中的某个字D倹{?/p>
q里需要修改的是IndexSearcher中的HitCollectorq程Q?/p>
...
scorer.score(new HitCollector() {
private float minScore = 0.0f;
public final void collect(int doc, float score) {
if (score > 0.0f && // ignore zeroed buckets
(bits==null || bits.get(doc))) { // skip docs not in bits
totalHits[0]++;
if (score >= minScore) {
/* 原先QLucenedocID和相应的匚w度score例入l果命中列表中:
* hq.put(new ScoreDoc(doc, score)); // update hit queue
* 如果用doc ?1/doc 代替 scoreQ就实现了根据docID排或逆排
* 假设数据源烦引时已经按照某个字段排好了序Q而结果根据docID排序也就实现?br /> * 针对某个字段的排序,甚至可以实现更复杂的score和docID的拟合?br /> */
hq.put(new ScoreDoc(doc, (float) 1/doc ));
if (hq.size() > nDocs) { // if hit queue overfull
hq.pop(); // remove lowest in hit queue
minScore = ((ScoreDoc)hq.top()).score; // reset minScore
}
}
}
}
}, reader.maxDoc());
更通用的输入输出接?/b>
虽然lucene没有定义一个确定的输入文档格式Q但来多的h惛_使用一个标准的中间格式作ؓLucene的数据导入接口,然后其他数据Q比如PDF只需要通过解析器{换成标准的中间格式就可以q行数据索引了。这个中间格式主要以XMLZQ类似实现已l不?Q?个:
数据? WORD PDF HTML DB other
\ | | | /
XML中间格式
|
Lucene INDEX
目前q没有针对MSWord文档的解析器Q因为Word文档和基于ASCII的RTF文档不同Q需要用COM对象机制解析。这个是我在Google上查的相兌料:http://www.intrinsyc.com/products/enterprise_applications.asp
另外一个办法就是把Word文档转换成textQ?a >http://www.winfield.demon.nl/index.html
索引q程优化
索引一般分2U情况,一U是批量的索引扩展Q一U是大批量的索引重徏。在索引q程中,q不是每ơ新的DOC加入q去索引都重新进行一ơ烦引文件的写入操作Q文件I/O是一仉常消耗资源的事情Q?/p>
Lucene先在内存中进行烦引操作,q根据一定的扚wq行文g的写入。这个批ơ的间隔大Q文件的写入ơ数少Q但占用内存会很多。反之占用内存少Q但文gIO操作频繁Q烦引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造烦引器后根据应用环境的情况充分利用内存减少文g的操作。根据我的用经验:~省Indexer是每20条记录烦引后写入一ơ,每将MERGE_FACTOR增加50倍,索引速度可以提高1倍左叟?br />
搜烦q程优化
lucene支持内存索引Q这L搜烦比基于文件的I/O有数量的速度提升?br />http://www.onjava.com/lpt/a/3273
而尽可能减少IndexSearcher的创建和Ҏ索结果的前台的缓存也是必要的?br />
Lucene面向全文索的优化在于首次索引索后Qƈ不把所有的记录QDocumentQ具体内容读取出来,而v只将所有结果中匚w度最高的?00条结果(TopDocsQ的ID攑ֈl果集缓存中q返回,q里可以比较一下数据库索:如果是一?0,000条的数据库检索结果集Q数据库是一定要把所有记录内定w取得以后再开始返回给应用l果集的。所以即使检索匹配L很多QLucene的结果集占用的内存空间也不会很多。对于一般的模糊索应用是用不到这么多的结果的Q头100条已l可以满?0%以上的检索需求?br />
如果首批~存l果数用完后q要d更后面的l果时Searcher会再ơ检索ƈ生成一个上ơ的搜烦~存数大1倍的~存Qƈ再重新向后抓取。所以如果构造一个SearcherL1Q?20条结果,Searcher其实是进行了2ơ搜索过E:?00条取完后Q缓存结果用完,Searcher重新索再构造一?00条的l果~存Q依此类推,400条缓存,800条缓存。由于每ơSearcher对象消失后,q些~存也访问那不到了,你有可能惛_l果记录~存下来Q缓存数量保证?00以下以充分利用首ơ的l果~存Q不让Lucene费多次索,而且可以分q行l果~存?br />
Lucene的另外一个特Ҏ在收集结果的q程中将匚w度低的结果自动过滤掉了。这也是和数据库应用需要将搜烦的结果全部返回不同之处?/p>
我的一些尝?/a>Q?/p> Luene的确是一个面对对象设计的典范 q些优点都是非常值得在以后的开发中学习借鉴的。作Z个通用工具包,Lunece的确l予了需要将全文索功能嵌入到应用中的开发者很多的便利?/p> 此外Q通过对Lucene的学习和使用Q我也更深刻地理解了Z么很多数据库优化设计中要求,比如Q?/p> 参考资料: Apache: Lucene Project The Lucene search engine: Powerful, flexible, and free Lucene Tutorial Notes on distributed searching with Lucene 中文语言的切分词 搜烦引擎工具介绍 Lucene作者Cutting的几论文和专利 Lucene?NET实现QdotLucene Lucene作者Cutting的另外一个项目:ZJava的搜索引擎Nutch 关于Z词表和N-Gram的切分词比较 特别感谢Q?br />前网易CTO许良?Jack Xu)l我的指|是您我带入了搜索引擎这个行业?/p>原文出处Q?lt;a>http://www.chedong.com/tech/lucene.html</a>
http://jakarta.apache.org/lucene/
Lucene开?用户邮g列表归档
Lucene-dev@jakarta.apache.org
Lucene-user@jakarta.apache.org
http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html
http://www.darksleep.com/puff/lucene/lucene.html
http://home.clara.net/markharwood/lucene/
http://www.google.com/search?sourceid=navclient&hl=zh-CN&q=chinese+word+segment
http://searchtools.com/
http://lucene.sourceforge.net/publications.html
http://sourceforge.net/projects/dotlucene/
http://www.nutch.org/ http://sourceforge.net/projects/nutch/
http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html
2005-01-08 Cutting在Pisa大学做的关于Lucene的讲座:非常详细的Lucene架构解说
from:
关于索引Q推荐{载的q篇文章
http://blog.csdn.net/dutguoyi/archive/2006/01/10/575617.aspx
改善SQL语句的效?br />http://community.csdn.net/Expert/topic/5087/5087396.xml?temp=.345669
数据量很大怎样加快索检速度
http://community.csdn.net/Expert/topic/5058/5058320.xml?temp=.1229517
索引建立Ҏ的区?br />http://community.csdn.net/Expert/topic/5068/5068154.xml?temp=.3010218
频繁插入删除数据需要更新烦?br />http://community.csdn.net/Expert/topic/4937/4937910.xml?temp=.8428614
试了一下sql server 2005 全文?br />http://community.csdn.net/Expert/topic/4878/4878430.xml?temp=.6049311
其他关于效率的高频问?/p>
判断一个表的数据不在另一个表中最优秀ҎQ?br />http://community.csdn.net/Expert/topic/5038/5038742.xml?temp=.4704553
删除千万U表中重复记录的办法
http://community.csdn.net/Expert/topic/5089/5089261.xml?temp=.7907068
数据库数据查询变得不正常cd问题
大数据量Q稳定运行一D|候以后无法得到查询结果?br />http://community.csdn.net/Expert/topic/4810/4810464.xml?temp=9.014529E-02
from: http://www.aygfsteel.com/zqli/archive/2006/12/08/86391.html
Type: copyright for distribution terms
h for help with SQL commands
? for help with psql commands
g or terminate with semicolon to execute query
q to quit
house=#
输入encoding GBK
house=#set encoding GBK
然后可以插入汉字了?br />
from: http://publish.it168.com/2006/0219/20060219173801.shtml?positioncode=1547
现在几乎所有的应用pȝ都无法避免用数据库pȝ。在JAVA世界里访问数据库是一仉常轻杄事情QJDBC为JAVA应用E序讉K数据库提供了一个统一的接口,通过使用JDBC接口开发者无需兛_pȝ最l采用哪U数据库Q因为JDBC仅仅是定义了讉K几个JAVA的接口类Q具体的实现是由数据库厂商提供的Q这U做法其实与其他数据库连接方式例如ODBC是类似的。但是在实际的应用过E中Q开发者发现离JDBC设计的初衯是有一定距,比如说在存储字W串时的~码问题Q我惛_多开发者都会遇见这个问题,倒不是因解决它有什么技术方面的隑ֺQ而是它的的确非常繁琐。我们必d每次写入或者读出字W串的时候进行编码和反编码处理;或者说我们可以写一个方法可以进行编码处理的Q但又必d每次数据库操作的时候调用,虽然调用很简单,可是我非得这样吗Q要是忘了编码那又要DEBUG了。当然你可能觉得qƈ没有什么,或者你可能很勤快,喜欢写大量重复的代码Q可是你N没有觉得q种J琐的工作正在浪费你q于宝贵的青春吗Q停止你的键盘输入,让我们来解决q个问题吧!
解决思\
在传l的应用E序中数据库操作部分我们可以惌成两层,如图所C:一个是数据库的"q接?Q另外一个业务数据操作层。在q里数据库的q接池是q义的,你可以把JDBC中的DriverManager也当成是q接池,具体的意思就是我们可以通过q层来获取到指定数据库的q接而不d心它是怎么获取的。如果这个时候数据库pȝQ有如InformixQSQL ServerQ要求对字符串进行{码才能存储(例如最常见的GBK->ISO8859_1转码Q,那我们就必须在业务数据操作层来进行,q样有多业务数据操作我们就要做多少~码转码的工作,太麻烦了Q代码中充斥中大量重复的内容。本文提出的解决Ҏ是利用对获取到的数据库q接实例q行二次装Q也是在数据库q接池与业务数据操作层之间加入了q接装层,当然了,我们也完全可以直接将q接装集成到数据库q接池内部。关于连接池的实现请参照我的另外一文章《用JAVA动态代理实现数据库q接池?/p>
图一
我们知道q行~码和{码工作都是集中在JDBC的两个接口PreparedStatement和ResultSet上进行的Q主要涉及PreparedStatement的setStringҎ以及ResultSet的getStringҎ。前面我们讲q需要加入一个连接封装层来对数据库连接实例进行二ơ封装,但是怎么通过q个装来改变PreparedStatement和ResultSetq两个接口的行ؓ呢?q个问题其实也很单,因ؓPreparedStatement接口必须通过Connection接口来获取实例,而ResultSet接口又必MStatement或者PreparedStatement接口来获取实例,有了q样的联关p,问题也就q刃而解了。还是利用我在文章《用JAVA动态代理实现数据库q接池》中使用的动态接口代理技术。首先我们设计Connection接口的代理类_ConnectionQ这个代理类接管了Connection接口中所有可能获取到Statement或者PreparedStatement接口实例的方法,例如QprepareStatement和createStatement。改变这两个Ҏ使之q回的是l过接管后的Statement或者PreparedStatement实例。通过对于Statement接口也有相应的代理类_StatementQ这个代理类接管用于获取ResultSet接口实例的所有方法,包括对setStringҎ的接以军_是否对字W串q行~码处理。对于接口ResultSet的接类_ResultSetq应的比较单,它只需要处理getStringҎ卛_?/p>
关键代码
前面我们大概介绍了这个解x案的思\Q下面我们给出关键的实现代码包括Connection的代理类QStatement的代理类QResultSet的代理类。这些代码是在原来关于数据库q接池实现的基础上进行扩充之增加对自动~码处理的功能。有需要源码打包的可以通过电子邮g跟我联系?/p>
_Connection.java
/*
* Created on 2003-10-23 by Liudong
*/
package lius.pool;
import java.sql.*;
import java.lang.reflect.*;
/*
*
* 数据库连接的代理c?
* @author Liudong
*/
class _Connection implements InvocationHandler{
private Connection conn = null;
private boolean coding = false;
//指定是否q行字符串{码操?br /> _Connection(Connection conn, boolean coding){
this.conn = conn;
this.coding = coding;
initConnectionParam(this.conn);
}
/**
* Returns the conn.
* @return Connection
*/
public Connection getConnection() {
Class[] interfaces = conn.getClass().getInterfaces();
if(interfaces==null||interfaces.length==0){
interfaces = new Class[1];
interfaces[0] = Connection.class;
}
Connection conn2 = (Connection)Proxy.newProxyInstance( conn.getClass().getClassLoader(), interfaces,this);
return conn2;
}
/**
* @see java.lang.reflect.InvocationHandler#invoke
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String method = m.getName();
//调用相应的操?br /> Object obj = null;
try{
obj = m.invoke(conn, args);
//接管用于获取语句句柄实例的方?br /> if((CS.equals(method)||PS.equals(method))&&coding)
return new _Statement((Statement)obj,true).getStatement();
} catch(InvocationTargetException e) {
throw e.getTargetException();
}
return obj;
}
private final static String PS = "prepareStatement";
private final static String CS = "createStatement";
}
_Statement.java
/*
* Created on 2003-10-23 by Liudong
*/
package lius.pool;
import java.sql.*;
import java.lang.reflect.*;
/**
* 数据库语句对象实例的代理c?
* @author Liudong
*/
class _Statement implements InvocationHandler{
private Statement statement ; //保存所接管对象的实例?br /> private boolean decode = false; //指定是否q行字符串{码?br />
public _Statement(Statement stmt,boolean decode) {
this.statement = stmt;
this.decode = decode;
}
/**
* 获取一个接后的对象实例?
* @return
*/
public Statement getStatement() {
Class[] interfaces = statement.getClass().getInterfaces();
if(interfaces==null||interfaces.length==0){
interfaces = new Class[1];
interfaces[0] = Statement.class;
}
Statement stmt = (Statement)Proxy.newProxyInstance(
statement.getClass().getClassLoader(),
interfaces,this);
return stmt;
}
/**
* Ҏ接管
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String method = m.getName(); //接管setStringҎ
if(decode && SETSTRING.equals(method)) {
try{
String param = (String)args[1];
if(param!=null)
param = new String(param.getBytes(),"8859_1");
return m.invoke(statement,new Object[]{args[0],param});
} catch(InvocationTargetException e){
throw e.getTargetException();
}
}
//接管executeQueryҎ
if(decode && EXECUTEQUERY.equals(method)){
try{
ResultSet rs = (ResultSet)m.invoke(statement,args);
return new _ResultSet(rs,decode).getResultSet();
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
try{
return m.invoke(statement, args);
} catch(InvocationTargetException e) {
throw e.getTargetException();
}
}
//两个要接的Ҏ?br />
private final static String SETSTRING = "setString";
private final static String EXECUTEQUERY = "executeQuery";
}
_ResultSet.java
/*
* Created on 2003-10-23 by Liudong
*/
package lius.pool;
import java.sql.ResultSet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 数据库结果集的代理类
* @author Liudong
*/
class _ResultSet implements InvocationHandler{
private ResultSet rs = null;
private boolean decode = false;
public _ResultSet(ResultSet rs,boolean decode) {
this.rs = rs;
this.decode = decode;
}
public ResultSet getResultSet(){
Class[] interfaces = rs.getClass().getInterfaces();
if(interfaces==null||interfaces.length==0){
interfaces = new Class[1];
interfaces[0] = ResultSet.class;
}
ResultSet rs2 = (ResultSet)Proxy.newProxyInstance(rs.getClass().getClassLoader(),interfaces,this);
return rs2;
}
/**
* l果getStringҎ
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String method = m.getName();
if(decode && GETSTRING.equals(method)){
try{
String result = (String)m.invoke(rs,args);
if(result!=null)
return new String(result.getBytes("8859_1"));
return null;
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
try{
return m.invoke(rs, args);
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
private final static String GETSTRING = "getString";
}
现在我们已经把三个接口的代理cd好了Q下一步就是怎么来用这三个cR其实对于用者来讲ƈ不需要关心三个类Q只需要了解_Connection可以了Q因为另外两个是_Connection直接调用的。ؓ了用_Connection我们必须传入两个参数Q第一个是数据库实际的数据库连接实例,另外一个是布尔g表是否进行{码处理。我们必d通过实际的情况获取到数据库连接后再传入_Connection的构造函C为参敎ͼ下面例子告诉你如何来使用_Connectionq个c:
Connection conn = getConnection(); //获取数据库连?br /> boolean coding = false; //从配|或者其他地方读取是否进行{码的配置
//接管数据库连接实例?br /> _Connection _conn = new _Connection(conn,coding);
//获得接管后的数据库连接实例,以后直接使用conn2而不是conn
Connection conn2 = _conn.getConnection();
因ؓ对一个应用系l来Ԍ数据库连接的获取必然有统一的方法,在这个方法中加入对连接的接管可以一x逸的解决数据库的~码问题?/p>
性能比较
功能没有问题了,开发者接下来׃兛_性能的问题,因ؓ在进行一些对响应速度要求很高或者大数据量的处理情况下性能成Z个非常突出的问题。由于JAVA中的动态接口代理采用的是反(ReflectionQ机Ӟ同时又加入我们自q一些代码例如方法名判断Q字W串转码{操作因此在性能上肯定比不上直接使用没有l过接管的数据库q接。但是这Ҏ能上的差别是不是我们可以忍受的呢,为此我做了一个试验对二者进行了比较Q?/p>
试环境单描qͼ
使用ACCESS数据库,Z张结构一L表,计算从获取连接后到插入数据完毕后的时间差Q两个程序(直连数据库和使用q接接管Q都q行的字W串的{码操作?/p>
试l果Q?/p>
插入记录?/td> | 直连数据库程序耗时 单位Qms | 使用q接接管E序耗时 | 性能比较 |
1000 | 2063 | 2250 | 9.0% |
5000 | 8594 | 8359 | -2.7% |
10000 | 16750 | 17219 | 2.8% |
15000 | 22187 | 23000 | 3.6% |
20000 | 27031 | 27813 | 2.9% |
从上面这张测试结果表中来看,二者的性能的差别非常小Q尽在两万条数据的扚w插入的时候时间差别也不会多于一U钟Q这Ll果应该说还是o人满意的Q毕竟ؓ了程序良好的l构有时候牺牲一点点性能q是值得的?/p>
本文是我之前文章《用JAVA动态代理实现数据库q接池》中提出的数据库q接池实现的q一步完善,同样使用动态接口代理的技术来解决数据库编码的问题。JAVA的这个高U技术可以用来解册多实际中非常手的问题,像本文提到的编码问题的处理以及数据库连接池的实玎ͼ同时在WEB开发框架的实现上也有非常大的作为。欢q对q方面感兴趣的朋友来信共同来研究?br />
from: http://www.javafan.net/article/20041212111952983.html
l 为应用选择最好最快的 JDBC 驱动 ,参?a >本站文章 ?JDBC3.0提供了新的特性来提高性能Q诸如连接池Q?statemente池的改进
l Ҏ据库使用q接池ƈ且重用连接,而不要重复打开和关闭连接。最佳的q接池大是当连接池大到_使服务请求不{待
l 量使用支持 JDBC3.0 的驱动,因ؓ JDBC3.0 支持包括 DataSource 对象Q连接池Q分布式事务支持Q?RowSets ?prepared statement 池等性能增强Ҏ?/span>
l Prepared statement 池(自从 JDBC3.0 开始有Q高速缓存已l预先优化ƈq行了的 SQL 查询Q这P他们被再ơ请求的时候,不必l历再次的优化预处理Q避免最优化步骤Q诸如检查语法,验证地址Q优化访问\径和执行计划Q?Statement 池是一个很好的Q重要的性能优化Ҏ
l JDBC3.0 中的 Statement 池和q接池能合作׃n statement 池,q样Q能使用一个已高速缓存的 statement Q该 statement 来自另外一个连接)的连接,在由Mq接执行?一些SQL 首次被执行时Q生的 statement 准备开销仅一?/span>
l RowSet对象?ResultSet 对象怼Q但是能提供当断开q接的时候对数据库数据的讉K。这允许数据以它最单的形式被高效的高速缓?/span>
l 用同一个连接执行多?statements
l 关闭 autocommit Q但不要让事务打开太久
l 避免事务分布开Q事务跨多个连接)
l 最化数据库的行和列数据获取。?setMaxRows, setMaxFieldSize,?SetFetchSize
l 使用最高效的数据类型:字符串比整数型快Q整数型比Q点类型和旉戳类型都要高效(是否不太理解^&^Q这是针对DB2数据库处理来说的Q处理charactercd最快,而处理integercd通常需要一些{换或者字节排序)
l 使用 updateXXX()Ҏ更新Q?updateXXX() 在可更新的结果集上调用。结果集已经定位C一?, 因此当用一?UPDATE statement Ӟ可以消除通常的查找要更新的数据行的开销
l CacheMh的元数据Q?metadata Qƈ可能少的用元数据 ҎQ其慢的E度一用便?/span>
l 避免在元数据 查询中?null 参数
l 使用虚拟查询获得一行的元数据,不要使用getcolumns()Q假如应用允许用户用列数据Q应用是使用getColumns来返回列的信息给用户q是准备一个虚拟查询而后调用getMetadata呢?
l 使用存储q程Q避免多余的|络传输
l 在存储过E中使用参量Q不要将数据挨个地放在statement中,最化解析开销。此条针对DB2来说Q其它数据库未必适用。SQLL以字W串形式发送给DB2数据库,例如Q?/span> l 寚w要重复执行的statement使用预处理statementQPreparedStatementQ?/span> l 选择使用最x标:对连l读取用游标;对双向滚动用游标。对仅返回一行的查询避免使用游标?/span> l 在JVM中Cache频繁h的数据,避免不必要的数据库请?/span> l 采用预读取机Ӟ 扚w取行Q而不要一ơ一?。调整批大小和预取行的数量。避免用预?BLOB 数据?/span> l 除非l对需要,否则避免Ud数据 l 在数据穿q网l之前要使流化数据( Streamline data Q?/span> l 避免每次处理一行,可能一起处理多行?/span> l 在表中统计个敎ͼ例如Q?select count(*) from myTable,yourTable where ?/span>Q属于资源密集型的。试试首先选入临时表,仅返回该计数QcountQ,然后发送精的二次查询获得临时表中的行的子集?/span> l ?/span>当的使用 SQL 能减资源请求。用返回所需数据的最值的查询Q避?select * 查询。一个返回小的数据子集的复杂查询Q比一个简单的Q返回超q所需的大量数据的单查询更高效?/span> l 使你的查询尽可能_yQ例如:可能精地最化要传输的数据Q其是所需的子?/span> l 努力扚w更新Q将 statement 攉CP然后在一个事务里面一h行。如果可能,使用有条件的逻辑和时变量来辑ֈ statement 批处?/span> l 永远不要?DBMS 事务跨越用户输入 l 考虑使用乐观锁。乐观锁使用旉戳验证数据是否还没有被其他用h变,否则事务p| l 使用 恰当的更斎ͼ例如Q更新行/表中已经存在的数据,而不要添加或者删除行/表。在适当的位|更新数据要比移动数据快得多Q如果更新需要的I间比表设计能提供的更多Q这可能是需要的。如果你设计的行需要空间初始化Q更新将会更快。交易是你的表可能需要更多的盘I间Q但可能速度更快。由于磁盘空间是便宜的,使用一点点能提高性能Q这应该说是非常有h值的投资 l 分开存储正在操作的数据和历史数据Q更一般的情况是将频繁使用的数据和不常使用的数据分开存储Q?/span> l 可能小的保留你的操作数据集Q避免必读那些不相关的数据 l DBMS可以很好的ƈ行运转,量应用设计成当和 DBMS交互时应用能做其他事情?/span> l 使用水U操作和q行操作?应用设计成支持大量q行q程Q?使应用运行更快。如果要处理多步Q努力设计好应用Q以使后来的步骤能够在Q何优先的q程已经完成的数据部分上开始工作,而不是必ȝC先进E完?/span> l 事物的保护别越高,性能损失p大。事物别按增长的顺序ؓQ?TRANSACTION_NONE, TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE。用Connection.setTransactionIsolation() 讄你想要的事物U别 l 默认的自动提交模式由于每一个数据库命o都成Z个单独的事务Q这会严重媄响性能Q关闭自动提交(Connection.setAutoCommit(false) Q,明确声明事务 l 通过整合多个事务Z个的扚w操作Qƈ在一个statement中用Statement.addBatch() 和Statement.executeBatch() l Savepoints (from JDBC3.0)需要昂늚资源。一旦不再需要,qM用Connection.releaseSavepoint()释放掉Savepoints l ConnectionPoolDataSource (from JDBC3.0)和PooledConnection接口接池提供了built-in支持 l 使用setLogWriter() (from Driver, DataSource, or ConnectionPooledDataSource; from JDBC3.0) 帮助跟踪JDBC?/span> l 使用Connection.setReadOnly(true)优化只读数据库(操作Q交?/span> l 使用Connection.nativeSQL()察看SQL查询如何在数据库U执行,帮助保SQL已被优化 l 切记Q一旦可能,立刻关闭Statement和ResultSet l 使用DatabaseMetaData获得数据库功能性信?/span> l 一直捕捉和处理数据库警告和异常 l 使用最恰当的数据类型明数据的cdQ例如:以datecd存储日期Q儿不要用varchar l 使用可滚动ResultSet (JDBC 2.0)
CallableStatement cstmt = conn.prepareCall ("call getCustName (12345)");
ResultSet rs = cstmt.executeQuery ();
DB2服务器必解析该SQLQ验证参量类型,q将参量转化为正的数据cd?/p>
from: http://www.ijsp.net/2/2003-9/20/0000431.shtml
CREATE TABLE person ( id SERIAL, name TEXT );
会自动{换ؓ以下SQL语句Q?
CREATE SEQUENCE person_id_seq; CREATE TABLE person ( id INT4 NOT NULL DEFAULT nextval('person_id_seq'), name TEXT );
from: http://www.pgsqldb.org/twiki/bin/view/PgSQL/PostgreFAQ#4.11.1
2. 对一些稍微复杂些的语? 例如在对金额{敏感数据操作时, 一个常用的操作序列?
a. 先取出当前金?br />b. q算后得到更新的金额
c. 执行 Update 语句: Update < tableName > set amount= < New amount > where amount= < Old amount >
q种操作?ORM 不能支持? SqlMap 能很好的支持?/p>
3. SqlMap ?Domain 对象可以直接攑֜业务? 一?ORM 的对数据讉K的基c要攑֜数据讉K?因ؓ带有Ҏ据访问的接口, 攑֜业务层不合?, 增加了代码的冗余度?/p>
4. ?ORM 的目的是什? 最主要的目的是减少重复的底层编E工作量, SqlMap 完全可以做到?/p>
再说?SqlMap 的不?
1. 因ؓ不象 ORM 那样生成E_可靠的对数据讉K的基c? 所以要?Map 操作做好充的单元测? 增加了测试的工作量?/p>
2. 每次改动数据? Map ?Domain 文g往往要手工修? 因ؓ SqlMap 的灵zL? 往往我们会手工调?Map 而不会直接?Generator 生成的代码?br />
from: http://matrix.foresee.cn/blogs/simon/archives/001638.html
[Gid] [int] IDENTITY (1, 1) NOT NULL ,
--本表的idP也是主键
[title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,
--U头文g的标?/p>
[fariqi] [datetime] NULL ,
--发布日期
[neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL ,
--发布用户
[reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL ,
--需要浏览的用户。每个用户中间用分隔W?”分开
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
下面Q我们来往数据库中d1000万条数据Q?/p>
declare @i int
set @i=1
while @i<=250000
begin
insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通信U?,'通信U?办公?王局?刘局?张局?admin,刑侦支队,特勤支队,交E警支?l侦支队,hU?d支队,外事U?,'q是最先的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','办公?,'办公?通信U?王局?刘局?张局?admin,刑侦支队,特勤支队,交E警支?l侦支队,hU?外事U?,'q是中间?5万条记录')
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)),'通信U?,'办公?通信U?王局?刘局?张局?admin,刑侦支队,特勤支队,交E警支?l侦支队,hU?外事U?,'q是最后的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','通信U?,'通信U?办公?王局?刘局?张局?admin,刑侦支队,特勤支队,交E警支?l侦支队,hU?d支队,外事U?,'q是最后添加的900万条记录')
set @i=@i+1000000
end
GO
通过以上语句Q我们创Z25万条由通信U于2004q??日发布的记录Q?5万条由办公室?004q??日发布的记录Q?002q和2003q各100?500条相同日期、不同分U的由通信U发布的记录Q共50万条Q,q有由通信U于2004q??日发布的900万条记录Q合?000万条?br />
from: http://www.pconline.com.cn/pcedu/empolder/db/sql/0501/538958.html
按上面的Ҏ不能得出所查询l果得总记录数,q里我们可以用select count(*) 来获取记录L,速度还q得?不过感觉要慢一?特别是记录集很多得情?不过q样占用得内存很得.
以上是我最q搞SQL查询得心?
from: http://www.fixdown.com/article/article/2121.htm
①ؓ什么要使用存储q程?
因ؓ它比SQL语句执行?
②存储过E是什?
把一堆SQL语句|在一?q可以根据条件执行不同SQL语句.(AX写作本文时观?
③来一个最单的存储q程
CREATE PROCEDURE dbo.testProcedure_AX
AS
select userID from USERS order by userid desc
?dbo.testProcedure_AX是你创徏的存储过E名,可以改ؓ:AXzhz{?别跟关键字冲H就行了.AS下面是一条SQL语句,不会写SQL语句的请回避.
④我怎么在ASP.NET中调用这个存储过E?
下面黄底的这两行够使了.
public static string GetCustomerCName(ref ArrayList arrayCName,ref ArrayList arrayID)
{
SqlConnection con=ADConnection.createConnection();
SqlCommand cmd=new SqlCommand("testProcedure_AX",con);
cmd.CommandType=CommandType.StoredProcedure;
con.Open();
try
{
SqlDataReader dr=cmd.ExecuteReader();
while(dr.Read())
{
if(dr[0].ToString()=="")
{
arrayCName.Add(dr[1].ToString());
}
}
con.Close();
return "OK!";
}
catch(Exception ex)
{
con.Close();
return ex.ToString();
}
}
?其实是把以?br />SqlCommand cmd=new SqlCommand("select userID from USERS order by userid desc",con);
中的SQL语句替换为存储过E名,再把cmd的类型标注ؓCommandType.StoredProcedure(存储q程)
⑤写个带参数的存储过E吧,上面q个单得有点惨不忍睹,不过q是蛮实用的.
参数带就带两,一个的没面?太小家子气了.
CREATE PROCEDURE dbo.AXzhz
/*
q里写注?br />*/
@startDate varchar(16),
@endDate varchar(16)
AS
select id from table_AX where commentDateTime>@startDate and commentDateTime<@endDate order by contentownerid DESC
?@startDate varchar(16)是声明@startDate q个变量,多个变量名间用?】隔开.后面的SQL可以用这个变量了.
⑥我怎么在ASP.NET中调用这个带参数的存储过E?
public static string GetCustomerCNameCount(string startDate,string endDate,ref DataSet ds)
{
SqlConnection con=ADConnection.createConnection();
//-----------------------注意q一D?-------------------------------------------------------------------------------------------------------
SqlDataAdapter da=new SqlDataAdapter("AXzhz",con);
para0=new SqlParameter("@startDate",startDate);
para1=new SqlParameter("@endDate",endDate);
da.SelectCommand.Parameters.Add(para0);
da.SelectCommand.Parameters.Add(para1);
da.SelectCommand.CommandType=CommandType.StoredProcedure;
//-------------------------------------------------------------------------------------------------------------------------------
try
{
con.Open();
da.Fill(ds);
con.Close();
return "OK";
}
catch(Exception ex)
{
return ex.ToString();
}
}
?把命令的参数dq去,OK?br />鸟的,改字体颜色的东西太垃圾了,改不?大家凑活着?
⑦我q想看看SQL命o执行成功了没?
注意看下面三行红色的语句
CREATE PROCEDURE dbo.AXzhz
/*
@parameter1 用户?br /> @parameter2 新密?br />*/
@password nvarchar(20),
@userName nvarchar(20)
AS
declare @err0 int
update WL_user set password=@password where UserName=@userName
set @err0=@@error
select @err0 as err0
?先声明一个整型变量@err0,再给其赋gؓ@@error(q个是系l自动给出的语句是否执行成功,0为成?其它为失?,最后通过select把它选择出来,某位高h说可以通过Returnq回,出本h的认知范?俺暂时不?以后再补充吧
⑧那怎么从后台获得这个执行成功与否的值呢?
下面q段代码可以告诉你答?
public static string GetCustomerCName()
{
SqlConnection con=ADConnection.createConnection();
SqlCommand cmd=new SqlCommand("AXzhz",con);
cmd.CommandType=CommandType.StoredProcedure;
para0=new SqlParameter("@startDate","2006-9-10");
para1=new SqlParameter("@endDate","2006-9-20");
da.SelectCommand.Parameters.Add(para0);
da.SelectCommand.Parameters.Add(para1);
con.Open();
try
{
Int32 re=(int32)cmd.ExecuteScalar();
con.Close();
if (re==0)
return "OK!";
else
return "false";
}
catch(Exception ex)
{
con.Close();
return ex.ToString();
}
}
?是通过SqlCommand的ExecuteScalar()Ҏ取回q个?q句话是从MSDN上找?为改?
int re=(int)cmd.ExecuteScalar(); 99%正确,现在没时间验?期待您的试!!!
⑨我要根据传入的参数判断执行哪条SQL语句!!~
下面q个存储q程可以满我们的要?竟然是Pascal/VB的写?Begin----End ,不是{},,,对用C#的我来说,q个语法有点恶心.........
ALTER PROCEDURE dbo.selectCustomerCNameCount
@customerID int
AS
if @customerID=-1
begin
select contentownerid ,userCName,count(*) as countAll from view_usercomment group by contentownerid,userCName order by contentownerid DESC
end
else
begin
select contentownerid ,userCName,count(*) as countAll from view_usercomment where contentownerid=@customerID group by contentownerid,userCName order by contentownerid DESC
end
好了,俺的水^只止于此,也够菜鸟们喝一壶的?q有更多东西{着我们d?无尽的征?!!!!!!!!!!
from: http://www.knowsky.com/340678.html
一、烦引的概念
索引是加快索表中数据的Ҏ?a >数据?/a>的烦引类g书籍的烦引。在书籍中,索引允许用户不必阅完整个书pq速地扑ֈ所需要的信息。在数据库中Q烦引也允许数据库程序迅速地扑ֈ表中的数据,而不必扫描整个数据库?/p>
二、烦引的特点
1.索引可以加快数据库的索速度
2.索引降低了数据库插入、修攏V删除等l护d的速度
3.索引创徏在表上,不能创徏在视图上
4.索引既可以直接创建,也可以间接创?
5.可以在优化隐藏中Q用烦?
6.使用查询处理器执行SQL语句Q在一个表上,一ơ只能用一个烦?
7.其他
三、烦引的优点
1.创徏唯一性烦引,保证数据库表中每一行数据的唯一?br /> 2.大大加快数据的检索速度Q这也是创徏索引的最主要的原?br /> 3.加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义?br /> 4.在用分l和排序子句q行数据索时Q同样可以显著减查询中分组和排序的旉?br /> 5.通过使用索引Q可以在查询的过E中使用优化隐藏器,提高pȝ的性能?/p>
四、烦引的~点
1.创徏索引和维护烦引要耗费旉Q这U时间随着数据量的增加而增?br /> 2.索引需要占物理I间Q除了数据表占数据空间之外,每一个烦引还要占一定的物理I间Q如果要建立聚簇索引Q那么需要的I间׃更大
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的l护Q降低了数据的维护速度
五、烦引分c?br /> 1.直接创徏索引和间接创建烦?br /> 直接创徏索引Q?CREATE INDEX mycolumn_index ON mytable (myclumn)
间接创徏索引Q定义主键约束或者唯一性键U束Q可以间接创建烦?br /> 2.普通烦引和唯一性烦?br /> 普通烦引:CREATE INDEX mycolumn_index ON mytable (myclumn)
唯一性烦引:保证在烦引列中的全部数据是唯一的,对聚烦引和非聚烦引都可以使用
CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)
3.单个索引和复合烦?br /> 单个索引Q即非复合烦?br /> 复合索引Q又叫组合烦引,在烦引徏立语句中同时包含多个字段名,最?6个字D?br /> CREATE INDEX name_index ON username(firstname,lastname)
4.聚簇索引和非聚簇索引(聚集索引Q群集烦?
聚簇索引Q物理烦引,与基表的物理序相同Q数据值的序L按照序排列
CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH
ALLOW_DUP_ROW(允许有重复记录的聚簇索引)
非聚烦引:CREATE UNCLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)
六、烦引的使用
1.当字D|据更新频率较低,查询使用频率较高q且存在大量重复值是使用聚簇索引
2.l常同时存取多列Q且每列都含有重复值可考虑建立l合索引
3.复合索引的前导列一定好控制好,否则无法起到索引的效果。如果查询时前导列不在查询条件中则该复合索引不会被用。前导列一定是使用最频繁的列
4.多表操作在被实际执行前,查询优化器会Ҏq接条gQ列出几l可能的q接Ҏq从中找出系l开销最的最x案。连接条件要充䆾考虑带有索引的表、行数多的表Q内外表的选择可由公式Q外层表中的匚w行数*内层表中每一ơ查扄ơ数定Q乘U最ؓ最x?br /> 5.where子句中对列的M操作l果都是在sqlq行旉列计算得到的,因此它不得不q行表搜索,而没有用该列上面的索引Q如果这些结果在查询~译时就能得刎ͼ那么可以被sql优化器优化,使用索引Q避免表搜烦(例:select * from record where substring(card_no,1,4)=?378?
&& select * from record where card_no like ?378%?M对列的操作都导致表扫描Q它包括数据库函数、计表辑ּ{等Q查询时要尽可能操作移至等号右?br /> 6.where条g中的’in’在逻辑上相当于’or’,所以语法分析器会将in ('0','1')转化为column='0' or column='1'来执行。我们期望它会根据每个or子句分别查找Q再结果相加,q样可以利用column上的索引Q但实际上它却采用了"or{略"Q即先取出满x个or子句的行Q存入时数据库的工作表中,再徏立唯一索引以去掉重复行Q最后从q个临时表中计算l果。因此,实际q程没有利用column上烦引,q且完成旉q要受tempdb数据库性能的媄响。in、or子句怼使用工作表,使烦引失效;如果不生大量重复|可以考虑把子句拆开Q拆开的子句中应该包含索引
7.要善于用存储过E,它sql变得更加灉|和高?br />
from: http://www.knowsky.com/339315.html
数据库的最初雏形据说源自美国一个奶牛场的记账薄(U质的,由此可见Q数据库q不一定是存储在电脑里的数据^_^)Q里面记录的是该奶牛场的收支账目Q程序员在将其整理、录入到电脑中时从中受到启发。当按照规定好的数据l构所采集到的数据量大C定程度后Q出于程序执行效率的考虑Q程序员其中的索、更新维护等功能分离出来Q做成单独调用的模块Q这个模块后来就慢慢发展、演变成现在我们所接触到的数据库管理系l?DBMS)——程序开发中的一个重要分支?/p>
下面q入正题Q首先按我个人所接触q的E序l数据库设计人员的功底分一下类Q?br /> Q、没有系l学习过数据l构的程序员。这cȝ序员的作品往往只是他们的即兴玩P他们往往习惯只设计有限的几个表,实现某类功能的数据全部塞在一个表中,各表之间几乎毫无兌。网上不的免费理软g都是q样的东西,当程序功能有限,数据量不多的时候,其程序运行v来没有什么问题,但是如果用其理比较重要的数据,风险性非常大?br /> Q、系l学习过数据l构Q但是还没有开发过对程序效率要求比较高的管理Y件的E序员。这cMh多半刚从学校毕业不久Q他们在设计数据库表l构Ӟ严格按照教科书上的规定,LE-R囑֒3NF(别灰心,所有的数据库设计高手都是从q一步开始的)。他们的作品Q对于一般的access型轻量的管理YӞ已经够用。但是一旦该pȝ需要添加新功能Q原有的数据库表差不多得q行大换血?br /> Q、第二类E序员,在经历过数次E序效率的提升,以及功能升的折腑Q终于升U成为数据库设计的老鸟Q第一cȝ序员g的高人。这cȝ序员可以胜Q二十个表以上的中型商业数据管理系l的开发工作。他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率,而且其设计的数据库可拓展性较好,当用户需要添加新功能Ӟ原有数据库表只需做少量修改即可?br /> Q、在l历q上十个cM数据库管理Y件的重复设计后,W三cȝ序员中坚持下来没有{行,而是希望从中扑և“偷懒”窍门的有心Z慢慢觉悟Q从而完成量变到质变的{换。他们所设计的数据库表结构有一定的q见Q能够预到未来功能升所需要的数据Q从而预先留下伏W。这cȝ序员目前大多晋成数据挖掘方面的高软g开发h员?br /> Q、第三类E序员或W四cȝ序员Q在对现有的各家数据库管理系l的原理和开发都有一定的ȝ后,要么在其基础上进行二ơ开发,要么自行开发一套有自主版权的通用数据库管理系l?/p>
我个人正处于W三cȝ末期Q所以下面所列出的一些设计技巧只适合W二cd部分W三cL据库设计人员。同Ӟ׃我很碰到有兴趣在这斚w深钻下去的同行,所以文中难免出现错误和遗漏Q在此先行声明,Ƣ迎大家指正Q不要藏U哦8)
一、树型关pȝ数据?br /> 不少E序员在q行数据库设计的时候都遇到q树型关pȝ数据Q例如常见的cd表,即一个大c,下面有若q个子类Q某些子cd有子c这L情况。当cd不确定,用户希望可以在Q意类别下d新的子类Q或者删除某个类别和其下的所有子c,而且预计以后其数量会逐步增长Q此时我们就会考虑用一个数据表来保存这些数据。按照教U书上的教导Q第二类E序员大概会设计出类DL数据表结构:
cd表_1(Type_table_1)
名称 cd U束条g 说明
type_id int 无重复 cd标识Q主?br />type_name char(50) 不允ؓI ?cd名称Q不允许重复
type_father int 不允ؓI ?该类别的父类别标识,如果是顶节点的话讑֮为某个唯一?/p>
q样的设计短精悍,完全满3NFQ而且可以满用户的所有要求。是不是q样p呢?{案是NOQWhyQ?/p>
我们来估计一下用户希望如何罗列出q个表的数据的。对用户而言Q他当然期望按他所讑֮的层ơ关pMơ罗列出所有的cdQ例如这P
ȝ?br /> cd1
cd1.1
cd1.1.1
cd1.2
cd2
cd2.1
cd3
cd3.1
cd3.2
…?/p>
看看Z实现q样的列表显C?树的先序遍历)Q要对上面的表进行多次索?注意Q尽类?.1.1可能是在cd3.2之后d的记录,{案仍然是Nơ。这L效率对于量的数据没什么媄响,但是日后cd扩充到数十条甚至上百条记录后Q单单列一ơ类型就要检索数十次该表Q整个程序的q行效率׃敢恭l了。或许第二类E序员会_那我再徏一个时数l或临时表,专门保存cd表的先序遍历l果Q这样只在第一ơ运行时索数十次Q再ơ罗列所有的cd关系时就直接读那个时数l或临时表就行了。其实,用不着再去分配一块新的内存来保存q些数据Q只要对数据表进行一定的扩充Q再Ҏ加类型的数量q行一下约束就行了Q要完成上面的列表只需一ơ检索就行了。下面是扩充后的数据表结构:
cd表_2(Type_table_2)
名称 cd U束条g 说明
type_id int 无重复 cd标识Q主?br />type_name char(50) 不允ؓI ?cd名称Q不允许重复
type_father int 不允ؓI ?该类别的父类别标识,如果是顶节点的话讑֮为某个唯一?br />type_layer char(6) 限定3?初始gؓ000000 cd的先序遍历,主要为减检索数据库的次?/p>
按照q样的表l构Q我们来看看上面例子记录在表中的数据是怎样的:
type_id type_name type_father type_layer
1 ȝ别 ?0 000000
2 cd1 1 010000
3 cd1.1 2 010100
4 cd1.2 2 010200
5 cd2 1 020000
6 cd2.1 5 020100
7 cd3 1 030000
8 cd3.1 7 030100
9 cd3.2 7 030200
10 cd1.1.1 3 010101
…?/p>
现在按type_layer的大来索一下:SELECT * FROM Type_table_2 ORDER BY type_layer
列出记录集如下:
type_id type_name type_father type_layer
1 ȝ别 ?0 000000
2 cd1 1 010000
3 cd1.1 2 010100
10 cd1.1.1 3 010101
4 cd1.2 2 010200
5 cd2 1 020000
6 cd2.1 5 020100
7 cd3 1 030000
8 cd3.1 7 030100
9 cd3.2 7 030200
…?/p>
现在列出的记录顺序正好是先序遍历的结果。在控制昄cd的层ơ时Q只要对type_layer字段中的数D行判断,?位一l,如大?则向右移2个空根{当Ӟ我这个例子中讑֮的限制条件是最?层,每层最多可?9个子cdQ只要按用户的需求情况修改一下type_layer的长度和位数Q即可更攚w制层数和子类别数。其实,上面的设计不单单只在cd表中用到Q网上某些可按树型列表显C的论坛E序大多采用cM的设计?/p>
或许有h认ؓQType_table_2中的type_father字段是冗余数据,可以除去。如果这P在插入、删除某个类别的时候,得对type_layer 的内容进行比较繁琐的判定Q所以我q没有消去type_father字段Q这也正W合数据库设计中适当保留冗余数据的来降低E序复杂度的原则Q后面我会D一个故意增加数据冗余的案例?/p>
二、商品信息表的设?br /> 假设你是一家百货公司电脑部的开发h员,某天老板要求你ؓ公司开发一套网上电子商务^収ͼ该百货公司有数千U商品出售,不过目前仅打先在网上销售数十种方便q输的商品,当然Q以后可能会陆箋在该电子商务q_上增加新的商品出售。现在开始进行该q_数据库的商品信息表的设计。每U出售的商品都会有相同的属性,如商品编P商品名称Q商品所属类别,相关信息Q供货厂商,内含件数Q库存,q货P销售hQ优惠h。你很快p计出4个表Q商品类型表(Wares_type)Q供货厂商表(Wares_provider)Q商品信息表(Wares_info)Q?/p>
商品cd?Wares_type)
名称 cd U束条g 说明
type_id int 无重复 cd标识Q主?br />type_name char(50) 不允ؓI ?cd名称Q不允许重复
type_father int 不允ؓI ?该类别的父类别标识,如果是顶节点的话讑֮为某个唯一?br />type_layer char(6) 限定3?初始gؓ000000 cd的先序遍历,主要为减检索数据库的次?/p>
供货厂商?Wares_provider)
名称 cd U束条g 说明
provider_id int 无重复 供货商标识,主键
provider_name char(100) 不允ؓI ?供货商名U?/p>
商品信息?Wares_info)
名称 cd U束条g 说明
wares_id int 无重复 商品标识Q主?br />wares_name char(100) 不允ؓI ?商品名称
wares_type int 不允ؓI 商品cd标识Q和Wares_type.type_id兌
wares_info char(200) 允许为空 相关信息
provider int 不允ؓI ?供货厂商标识Q和Wares_provider.provider_id兌
setnum int 初始gؓ1 内含件数Q默认ؓ1
stock int 初始gؓ0 库存Q默认ؓ0
buy_price money 不允ؓI ?q货?br />sell_price money 不允ؓI ?销售h
discount money 不允ؓI ?优惠?/p>
你拿着q?个表l老板查,老板希望能够再添加一个商品图片的字段Q不q只有一部分商品有图片。OKQ你在商品信息表(Wares_info)中增加了一个haspic的BOOL型字D,然后再徏了一个新表——商品图片表(Wares_pic)Q?/p>
商品囄?Wares_pic)
名称 cd U束条g 说明
pic_id int 无重复 商品囄标识Q主?br />wares_id int 不允ؓI ?所属商品标识,和Wares_info.wares_id兌
pic_address char(200) 不允ؓI 囄存放路径
E序开发完成后Q完全满板目前的要求,于是正式启用。一D|间后Q老板打算在这套^C推出新的商品销售,其中Q某cd品全部都需d“长度”的属性。第一轮折腾来了……当Ӟ你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslength的BOOL型字D,又徏了一个新表——商品长度表(Wares_length)Q?/p>
商品长度?Wares_length)
名称 cd U束条g 说明
length_id int 无重复 商品囄标识Q主?br />wares_id int 不允ؓI ?所属商品标识,和Wares_info.wares_id兌
length char(20) 不允ؓI 商品长度说明
刚刚改完没多久,老板又打上一Ҏ的商品,q次某类商品全部需要添加“宽度”的属性。你咬了咬牙Q又照方抓药Q添加了商品宽度?Wares_width)。又q了一D|_老板C的商品中有一些需要添加“高度”的属性,你是不是开始觉得你所设计的数据库按照q种方式增长下去Q很快就能变成一个迷宫呢Q那么,有没有什么办法遏制这U不可预见性,但却cM重复的数据库膨胀呢?我在阅读《敏捯Y件开发:原则、模式与实践》中发现作者Dq类似的例子Q?.3 “Copy”程序。其中,我非常赞同敏捯Y件开发这个观点:在最初几乎不q行预先设计Q但是一旦需求发生变化,此时作ؓ一名追求卓的E序员,应该从头审查整个架构设计Q在此次修改中设计出能够满日后cM修改的系l架构。下面是我在需要添加“长度”的属性时所提供的修Ҏ案:
L商品信息?Wares_info)中的haspic字段Q添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能?/p>
商品额外属性表(Wares_ex_property)
名称 cd U束条g 说明
ex_pid int 无重复 商品额外属性标识,主键
p_name char(20) 不允ؓI ?额外属性名U?/p>
商品额外信息?Wares_ex_info)
名称 cd U束条g 说明
ex_iid int 无重复 商品额外信息标识Q主?br />wares_id int 不允ؓI ?所属商品标识,和Wares_info.wares_id兌
property_id int 不允ؓI 商品额外属性标识,和Wares_ex_property.ex_pid兌
property_value char(200) 不允ؓI ?商品额外属性?/p>
在商品额外属性表(Wares_ex_property)中添?条记录:
ex_pid p_name
1 商品囄
2 商品长度
再在整个电子商务q_的后台管理功能中q加一商品额外属性管理的功能Q以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表(Wares_ex_property)中添加一条记录即可。不要害怕变化,被第一颗子弹击中ƈ不是坏事Q坏的是被相同轨道飞来的W二颗、第三颗子弹M。第一颗子Ҏ得越早,所受的伤越重,之后的抵抗力也越?) 下面看看如何自行设计一套比较灵zȝ多用L理模块,卌数据库管理Y件的pȝ理员可以自行添加新用户Q修改已有用L权限Q删除已有用戗首先,分析用户需求,列出该数据库理软g所有需要实现的功能Q然后,Ҏ一定的联系对这些功能进行分c,x某类用户需使用的功能归Zc;最后开始徏表: 用户l表(User_group) 用户?User_table) 采用q种用户l的架构设计Q当需要添加新用户Ӟ只需指定新用h属的用户l;当以后系l需要添加新功能或对旧有功能权限q行修改Ӟ只用操作功能表和用户l表的记录,原有用户的功能即可相应随之变化。当Ӟq种架构设计把数据库理软g的功能判定移C前台Q得前台开发相对复杂一些。但是,当用h较大(10Z?Q或日后软g升的概率较大时Q这个代h值得的?/p> 书籍?Book_table) 借阅用户?Renter_table) 借阅记录?Rent_log) Z实现按批查询借阅记录Q我们可以再Z个表来保存批量借阅的信息,例如Q?/p> 扚w借阅?Batch_rent) q样的设计好吗?我们来看看ؓ了列出某个用hơ借阅的所有书c,需要如何查询?首先索批量借阅?Batch_rent)Q把W合条g的的所有记录的rent_id字段的数据保存v来,再用q些数据作ؓ查询条g带入到借阅记录?Rent_log)中去查询。那么,有没有什么办法改q呢Q下面给ZU简z的扚w设计ҎQ不需d新表Q只需修改一下借阅记录?Rent_log)卛_。修改后的记录表(Rent_log)如下Q?/p> 借阅记录?Rent_log) 其中Q同一ơ借阅的batch_no和该批第一条入库的rent_id相同。D例:假设当前最大rent_id?4Q接着某用户一ơ借阅?本书Q则扚w插入?条借阅记录的batch_no都是65。之后另外一个用L了一套碟Q再插入出租记录的rent_id?8。采用这U设计,查询扚w借阅的信息时Q只需使用一条标准T_SQL的嵌套查询即可。当Ӟq种设计不符?NFQ但是和上面标准?NF设计比v来,哪一U更好呢Q答案就不用我说了吧?/p> 员工?Clerk_table) 每餐总表(Eatdata1) 餐计费l表(Eatdata2) 其中Q就计费细?Eatdata2)的记录就是把每餐总表(Eatdata1)的一条记录按餐员工qx拆开Q是个不折不扣的冗余表。当Ӟ也可以把每餐总表(Eatdata1)的部分字D合q到餐计费l表(Eatdata2)中,q样每餐总表(Eatdata1)成了冗余表Q不q这h设计出来的就计费细表重复数据更多,相比来说q是上面的方案好些。但是,是餐计费l表(Eatdata2)q个冗余表,在做每月每h费l计的时候,大大化了~程的复杂度Q只用类D么一条查询语句即可统计出每h每月的寄次数和费dQ?/p> SELECT clerk_name AS personname,COUNT(c_id) as eattimes,SUM(price) AS ptprice FROM Eatdata2 JOIN Clerk_tabsle ON (c_id=clerk_id) JOIN eatdata1 ON (totleid=tid) WHERE eat_date>=CONVERT(datetime,'"&the_date&"') AND eat_date<DATEADD(month,1,CONVERT(datetime,'"&the_date&"')) GROUP BY c_id 惌一下,如果不用q个冗余表,每次l计每h每月的餐Ҏd时会多麻烦,E序效率也够呛。那么,到底什么时候可以增加一定的冗余数据呢?我认为有2个原则: Q、用L整体需求。当用户更多的关注于Q对数据库的规范记录按一定的法q行处理后,再列出的数据。如果该法可以直接利用后台数据库系l的内嵌函数来完成,此时可以适当的增加冗余字D,甚至冗余表来保存q些l过法处理后的数据。要知道Q对于大扚w数据的查询,修改或删除,后台数据库系l的效率q远高于我们自己~写的代码?br /> Q、简化开发的复杂度。现代Y件开发,实现同样的功能,Ҏ有很多。尽不必要求程序员_N绝大部分的开发工具和q_Q但是还是需要了解哪U方法搭配哪U开发工LE序更简z,效率更高一些。冗余数据的本质是用空间换旉Q尤其是目前g的发展远q高于YӞ所以适当的冗余是可以接受的。不q我q是在最后再一下:不要q多的依赖^台和开发工LҎ来化开发,q个度要是没把握好的话,后期l护升会栽大跟头的?br />
三、多用户及其权限理的设?br /> 开?a >数据?/font>理cȝ软gQ不可能不考虑多用户和用户权限讄的问题。尽目前市面上的大、中型的后台数据库系lY仉提供了多用户Q以及细x个数据库内某张表的权限设|的功能Q我个hQ一套成熟的数据库管理YӞq是应该自行设计用户理q块功能Q原因有二:
1.那些大、中型后台数据库pȝ软g所提供的多用户及其权限讄都是针对数据库的共有属性,q不一定能完全满某些特例的需求;
2.不要q多的依赖后台数据库pȝ软g的某些特D功能,多种大、中型后台数据库pȝ软g之间q不完全兼容。否则一旦日后需要{换数据库q_或后台数据库pȝ软g版本升Q之前的架构设计很可能无法重用?/p>
功能?Function_table)
名称 cd U束条g 说明
f_id int 无重复 功能标识Q主?br />f_name char(20) 不允ؓI ?功能名称Q不允许重复
f_desc char(50) 允许为空 功能描述
名称 cd U束条g 说明
group_id int 无重复 ?用户l标识,主键
group_name char(20) 不允ؓI ?用户l名U?br />group_power char(100) 不允ؓI ?用户l权限表Q内容ؓ功能表f_id的集?/p>
名称 cd U束条g 说明
user_id int 无重复 ?用户标识Q主?br />user_name char(20) 无重复 ?用户?br />user_pwd char(20) 不允ؓI ?用户密码
user_type int 不允ؓI ?所属用L标识Q和User_group.group_id兌
四、简z的扚wm:n设计
到m:n的关p,一般都是徏?个表Qm一个,n一个,m:n一个。但是,m:n有时会遇到批量处理的情况Q例如到图书馆借书Q一般都是允许用户同时借阅n本书Q如果要求按Ҏ询借阅记录Q即列出某个用户某次借阅的所有书c,该如何设计呢Q让我们建好必须?个表先:
名称 cd U束条g 说明
book_id int 无重复 ?书籍标识Q主?br />book_no char(20) 无重复 ?书籍~号
book_name char(100) 不允ؓI ?书籍名称
…?/p>
名称 cd U束条g 说明
renter_id int 无重复 ?用户标识Q主?br />renter_name char(20) 不允ؓI ?用户姓名
…?/p>
名称 cd U束条g 说明
rent_id int 无重复 ?借阅记录标识Q主?br />r_id int 不允ؓI ?用户标识Q和Renter_table.renter_id兌
b_id int 不允ؓI ?书籍标识Q和Book_table.book_id兌
rent_date datetime 不允ؓI ?借阅旉
…?/p>
名称 cd U束条g 说明
batch_id int 无重复 ?扚w借阅标识Q主?br />batch_no int 不允ؓI ?扚w借阅~号Q同一批借阅的batch_no相同
rent_id int 不允ؓI ?借阅记录标识Q和Rent_log.rent_id兌
batch_date datetime 不允ؓI ?扚w借阅旉
名称 cd U束条g 说明
rent_id int 无重复 ?借阅记录标识Q主?br />r_id int 不允ؓI ?用户标识Q和Renter_table.renter_id兌
b_id int 不允ؓI ?书籍标识Q和Book_table.book_id兌
batch_no int 不允ؓI ?扚w借阅~号Q同一批借阅的batch_no相同
rent_date datetime 不允ؓI ?借阅旉
…?/p>
五、冗余数据的取舍
上篇的“树型关pȝ数据表”中保留了一个冗余字D,q里的例子更q一步——添加了一个冗余表。先看看例子Q我原先所在的公司Z解决员工的工作餐Q和附近的一家小馆联系Q每天吃饭记账,费用按h数^摊,月底由公司现金结,每个人每个月的工作餐费从工资中扣除。当Ӟ每天吃饭的h员和人数都不是固定的Q而且Q由于每工作餐的所点的菜色不同Q每的p也不相同。例如,星期一中餐5?0元,晚餐2?0Q星期二中餐6?6元,晚餐3?8元。ؓ了方便计每个h每个月的工作费Q我写了一个简陋的餐记̎理E序Q数据库里有3个表Q?/p>
名称 cd U束条g 说明
clerk_id int 无重复 ?员工标识Q主?br />clerk_name char(10) 不允ؓI ?员工姓名
名称 cd U束条g 说明
totle_id int 无重复 ?每餐总表标识Q主?br />persons char(100) 不允ؓI ?餐员工的员工标识集?br />eat_date datetime 不允ؓI ?餐日期
eat_type char(1) 不允ؓI ?餐cdQ用来区分中、晚?br />totle_price money 不允ؓI ?每餐总花?br />persons_num int 不允ؓI ?餐人数
名称 cd U束条g 说明
id int 无重复 ?餐计费l表标识Q主?br />t_id int 不允ؓI ?每餐总表标识Q和Eatdata1.totle_id兌
c_id int 不允ؓI ?员工标识标识Q和Clerk_table.clerk_id兌
price money 不允ؓI ?每h每餐p
from: http://www.knowsky.com/4937.html
mysql> ALTER TABLE Jokes ADD COLUMN -> AuthorName VARCHAR(100); |
mysql> ALTER TABLE Jokes ADD COLUMN -> AuthorEMail VARCHAR(100); |
mysql> SELECT DISTINCT AuthorName, AuthorEMail -> FROM Jokes; |
mysql> ALTER TABLE Jokes DROP COLUMN AuthorName; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> ALTER TABLE Jokes DROP COLUMN AuthorEMail; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 |
mysql> CREATE TABLE Authors ( -> ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -> Name VARCHAR(100), -> EMail VARCHAR(100) -> ); |
$jokelist = mysql_query( "SELECT JokeText, AuthorName, AuthorEMail ". "FROM Jokes"); while ($joke = mysql_fetch_array($jokelist)) { $joketext = $joke["JokeText"]; $name = $joke["AuthorName"]; $email = $joke["AuthorEMail"]; // Display the joke with author information echo( "<P>$joketext<BR>" . "(by QHREF='mailto:$email'Q?name)</P>" ); } |
// Get the list of jokes $jokelist = mysql_query( "SELECT JokeText, AID FROM Jokes"); while ($joke = mysql_fetch_array($jokelist)) { // Get the text and Author ID for the joke $joketext = $joke["JokeText"]; $aid = $joke["AID"]; // Get the author details for the joke $authordetails = mysql_query( "SELECT Name, Email FROM Authors WHERE ID=$aid"); $author = mysql_fetch_array($authordetails); $name = $author["Name"]; $email = $author["EMail"]; // Display the joke with author information echo( "<P>$joketext<BR>" . "(by QA HREF='mailto:$email'Q?name)</P>" ); } |
mysql> SELECT <columns> FROM <tables> -> WHERE <condition(s) for data to be related>; |
mysql> SELECT LEFT(JokeText,20), Name, Email -> FROM Jokes, Authors WHERE AID = ID; ERROR 1052: Column: 'ID' in where clause is ambiguous |
$jokelist = mysql_query( "SELECT JokeText, Name, EMail " . "FROM Jokes, Authors WHERE AID=Authors.ID"); while ($joke = mysql_fetch_array($jokelist)) { $joketext = $joke["JokeText"]; $name = $joke["Name"]; $email = $joke["EMail"]; // Display the joke with author information echo( "<P>$joketext<BR>" . "(by QA HREF='mailto:$email'Q?name)</P>" ); } |
mysql> SELECT JokeText FROM Jokes, Authors WHERE -> Name="Joan Smith" AND AID=Authors.ID; |
mysql> CREATE TABLE Categories ( -> ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -> Name VARCHAR(100), -> Description TEXT -> ); Query OK, 0 rows affected (0.00 sec) |
mysql> CREATE TABLE JokeLookup ( -> JID INT NOT NULL, -> CID INT NOT NULL, -> PRIMARY KEY(JID,CID) -> ); |
mysql> SELECT JokeText -> FROM Jokes, Categories, JokeLookup -> WHERE Name="Knock-Knock" AND -> CID=Categories.ID AND JID=Jokes.ID; |
mysql> SELECT Categories.Name -> FROM Jokes, Categories, JokeLookup -> WHERE JokeText LIKE "How many lawyers%" -> AND CID=Categories.ID AND JID=Jokes.ID; |
mysql> SELECT Authors.Name -> FROM Jokes, Authors, Categories, JokeLookup -> WHERE Categories.Name="Knock-Knock" -> AND CID=Categories.ID AND JID=Jokes.ID -> AND AID=Authors.ID; |
表productInfo有如下记?
productID productName parentID clickNum
1 男士衣服 1 90 --衣服cd中这条记录的点击率最?br />2 奛_衣服 1 80
3 男士裤子 2 70
4 奛_裤子 2 90 --裤子cd中这条记录点ȝ最?br />5 男士帽子 5 15
6 奛_帽子 5 30 --帽子cd中这条点ȝ最?br />7 男士鞋子 10 65 --鞋子cd中这条点ȝ最?br />8 奛_鞋子 10 52
9 奛_鞋子1 10 54
现在要求分别把衣?裤子,帽子,鞋子q些cd中点ȝ最高的一条记录找出来,然后再降序排?l果应如?
productID productName clickNum
1 男士衣服 90
4 奛_裤子 90
7 男士鞋子 65
6 奛_帽子 30
影响到数据库性能的最大因素就是烦引。由于该问题的复杂性,我只可能单的谈谈q个问题Q不q关于这斚w的问题,目前有好几本不错的书c可供你参阅。我在这里只讨论两种SQL Server索引Q即clustered索引和nonclustered索引。当考察建立什么类型的索引Ӟ你应当考虑数据cd和保存这些数据的column。同P你也必须考虑数据库可能用到的查询cd以及使用的最为频J的查询cd?/p>
如果column保存了高度相关的数据Qƈ且常常被序讉KӞ最好用clustered索引Q这是因为如果用clustered索引QSQL Server会在物理上按升序Q默认)或者降序重排数据列Q这样就可以q速的扑ֈ被查询的数据。同P在搜L制在一定范围内的情况下Q对q些column也最好用clustered索引。这是因为由于物理上重排数据Q每个表g只有一个clustered索引?/p>
与上面情늛反,如果columns包含的数据相x较差,你可以用nonculstered索引。你可以在一个表g使用高达249个nonclustered索引——尽我惌不出实际应用场合会用的上q么多烦引?/p>
当表g用主关键字(primary keysQ,默认情况下SQL Server会自动对包含该关键字的column(s)建立一个独有的cluster索引。很昄Q对q些column(s)建立独有索引意味着d键字的唯一性。当建立外关键字Qforeign keyQ关pLQ如果你打算频繁使用它,那么在外关键字cloumn上徏立nonclustered索引不失Z个好的方法。如果表格有clustered索引Q那么它用一个链表来l护数据之间的关系。相反,如果表格没有clustered索引QSQL Server在一个堆栈中保存数据c?/p>
当烦引徏立v来的时候,SQLServer徏立数据页QdatapageQ,数据|用以加速搜索的指针。当索引建立h的时候,其对应的填充因子也即被设|。设|填充因子的目的是ؓ了指C索引中数据页的百分比。随着旉的推U,数据库的更新会消耗掉已有的空闲空_q就会导致页被拆分。页拆分的后果是降低了烦引的性能Q因而用该索引的查询会D数据存储的支ȝ。当建立一个烦引时Q该索引的填充因子即被设|好了,因此填充因子不能动态维护?/p>
Z更新数据中的填充因子,我们可以停止旧有索引q建烦引,q新设|填充因子(注意Q这媄响到当前数据库的q行Q在重要场合误}慎用)?i>DBCC INDEXDEFRAG?i>DBCC DBREINDEX是清除clustered和nonculstered索引片的两个命令?i>INDEXDEFRAG是一U在U操作(也就是说Q它不会d其它表格动作Q如查询Q,?i>DBREINDEX则在物理上重建烦引。在l大多数情况下,重徏索引可以更好的消除碎片,但是q个优点是以d当前发生在该索引所在表g其它动作Zh取来得。当出现较大的碎片烦引时Q?i>INDEXDEFRAG会花上一D|较长的时_q是因ؓ该命令的q行是基于小的交互块Qtransactional blockQ?/p>
当你执行上述措施中的M一个,数据库引擎可以更有效的返回编入烦引的数据。关于填充因子(fillfactorQ话题已l超Z本文的范_不过我还是提醒你需要注意那些打用填充因子徏立烦引的表格?/p>
在执行查询时QSQL Server动态选择使用哪个索引。ؓ此,SQL ServerҎ每个索引上分布在该关键字上的l计量来军_使用哪个索引。值得注意的是Q经q日常的数据库活动(如插入、删除和更新表格Q,SQL Server用到的这些统计量可能已经“过期”了Q需要更新。你可以通过执行DBCC SHOWCONTIG来查看统计量的状态。当你认为统计量已经“过期”时Q你可以执行该表格的UPDATE STATISTICS命oQ这样SQL Server刷C关于该烦引的信息了?/p>
SQL Server提供了一U简化ƈ自动l护数据库的工具。这个称之ؓ数据库维护计划向|Database Maintenance Plan Wizard QDMPWQ的工具也包括了对烦引的优化。如果你q行q个向导Q你会看到关于数据库中关于烦引的l计量,q些l计量作为日志工作ƈ定时更新Q这样就减轻了手工重建烦引所带来的工作量。如果你不想自动定期h索引l计量,你还可以在DMPW中选择重新l织数据和数据页Q这停止旧有烦引ƈ按特定的填充因子重徏索引?br />
from: http://www.zdnet.com.cn/developer/database/story/0,3800066906,39109102,00.htm
解决ҎQ?/p>
2. 在属性对话框中选择安全性选项Q在w䆾验证处选择“SQL Server和Windows”,然后定?br />
from: http://www.lunji.com/faq/login_error.htm