qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

简单十步让你全面理解SQL

 很多程序员认为SQL是一头难以驯服的野兽。它是为数不多的声明性语言之一,也因为这样,其展示了完全不同于其他的表现形式、命令式语言、 面向对象语言甚至函数式编程语言(虽然有些人觉得SQL 还是有些类似功能)。
  我每天都写SQL,我的开源软件JOOQ中也包含SQL。因此我觉得有必要为还在为此苦苦挣扎的你呈现SQL的优美!下面的教程面向于:
  已经使用过但没有完全理解SQL的读者
  已经差不多了解SQL但从未真正考虑过它的语法的读者
  想要指导他人学习SQL的读者
  本教程将重点介绍SELECT 语句。其他 DML 语句将在另一个教程中在做介绍。接下来就是…
  1、SQL是声明性语言
  首先你需要思考的是,声明性。你唯一需要做的只是声明你想获得结果的性质,而不需要考虑你的计算机怎么算出这些结果的。
  SELECT first_name, last_name FROM employees WHERE salary > 100000
  这很容易理解,你无须关心员工的身份记录从哪来,你只需要知道谁有着不错的薪水。
  从中我们学到了什么呢?
  那么如果它是这样的简单,会出现什么问题吗?问题就是我们大多数人会直观地认为这是命令式编程。如:“机器,做这,再做那,但在这之前,如果这和那都发生错误,那么会运行一个检测”。这包括在变量中存储临时的编写循环、迭代、调用函数,等等结果。
  把那些都忘了吧,想想怎么去声明,而不是怎么告诉机器去计算。
  2、SQL语法不是“有序的”
  常见的混乱源于一个简单的事实,SQL语法元素并不会按照它们的执行方式排序。语法顺序如下:
SELECT [DISTINCT]
FROM
WHERE
GROUP BY
HAVING
UNION
ORDER BY
  为简单起见,并没有列出所有SQL语句。这个语法顺序与逻辑顺序基本上不同,执行顺序如下:
FROM
WHERE
GROUP BY
HAVING
SELECT
DISTINCT
UNION
ORDER BY
  这有三点需要注意:
  1、第一句是FROM,而不是SELECT。首先是将数据从磁盘加载到内存中,以便对这些数据进行操作。
  2、SELECT是在其他大多数语句后执行,最重要的是,在FROM和GROUP BY之后。重要的是要理解当你觉得你可以从WHERE语句中引用你定义在SELECT语句当中的时候,。以下是不可行的:
  SELECT A.x + A.y AS z
  FROM A
  WHERE z = 10 -- z is not available here!
如果你想重用z,您有两种选择。要么重复表达式:
  SELECT A.x + A.y AS z
  FROM A
  WHERE (A.x + A.y) = 10
  或者你使用派生表、公用表表达式或视图来避免代码重复。请参阅示例进一步的分析:
  3、在语法和逻辑顺序里,UNION都是放在ORDER BY之前,很多人认为每个UNION子查询都可以进行排序,但根据SQL标准和大多数的SQL方言,并不是真的可行。虽然一些方言允许子查询或派生表排序,但却不能保证这种顺序能在UNION操作后保留。
  需要注意的是,并不是所有的数据库都以相同的形式实现,例如规则2并不完全适用于MySQL,PostgreSQL,和SQLite上
  从中我们学到了什么呢?
  要时刻记住SQL语句的语法顺序和逻辑顺序来避免常见的错误。如果你能明白这其中的区别,就能明确知道为什么有些可以执行有些则不能。
  如果能设计一种在语法顺序上实际又体现了逻辑顺序的语言就更好了,因为它是在微软的LINQ上实现的。
  3、SQL是关于数据表引用的
  因为语法顺序和逻辑顺序的差异,大多数初学者可能会误认为SQL中列的值是第一重要的。其实并非如此,最重要的是数据表引用。
  该SQL标准定义了FROM语句,如下:
  <from clause> ::= FROM &lt;table reference&gt; [ { &lt;comma&gt; &lt;table reference&gt; }... ]
  ROM语句的"output"是所有表引用的结合程度组合表引用。让我们慢慢地消化这些。
  FROM a, b
  上述产生一个a+b度的组合表引用,如果a有3列和b有5列,那么"输出表"将有8(3+5)列。
  包含在这个组合表引用的记录是交叉乘积/笛卡儿积的axb。换句话说,每一条a记录都会与每一条b记录相对应。如果a有3个记录和b有5条记录,然后上面的组合表引用将产生15条记录(3×5)。
  在WHERE语句筛选后,GROUP BY语句中"output"是"fed"/"piped",它已转成新的"output",我们会稍后再去处理。
  如果我们从关系代数/集合论的角度来看待这些东西,一个SQL表是一个关系或一组元素组合。每个SQL语句将改变一个或几个关系,来产生新的关系。
  从中我们学到了什么呢?
  一直从数据表引用角度去思考,有助于理解数据怎样通过你的sql语句流水作业的
  4、SQL数据表引用可以相当强大
  表引用是相当强大的东西。举个简单的例子,JOIN关键字其实不是SELECT语句的一部分,但却是"special"表引用的一部分。连接表,在SQL标准中有定义(简化的):
  <table reference> ::=
  <table name>
  | <derived table>
  | <joined table>
  如果我们又拿之前的例子来分析:
  FROM a, b
  a可以作为一个连接表,如:
  a1 JOIN a2 ON a1.id = a2.id
  这扩展到前一个表达式,我们会得到:
  FROM a1 JOIN a2 ON a1.id = a2.id, b
  虽然结合了数据表引用语法与连接表语法的逗号分隔表让人很无语,但你肯定还会这样做的。结果,结合数据表引用将有a1+a2+b度。
  派生表甚至比连接表更强大,我们接下来将会说到。
  从中我们学到了什么呢?
  要时时刻刻考虑表引用,重要的是这不仅让你理解数据怎样通过你的sql语句流水作业的,它还将帮助你了解复杂表引用是如何构造的。
  而且,重要的是,了解JOIN是构造连接表的关键字。不是的SELECT语句的一部分。某些数据库允许JOIN在插入、更新、删除中使用。
  5、应使用SQL JOIN的表,而不是以逗号分隔表
  前面,我们已经看到这语句:
  FROM a, b
  高级SQL开发人员可能会告诉你,最好不要使用逗号分隔的列表,并且一直完整的表达你的JOINs。这将有助于改进你的SQL语句的可读性从而防止错误出现。
  一个非常常见的错误是忘记某处连接谓词。思考以下内容:
  FROM a, b, c, d, e, f, g, h
  WHERE a.a1 = b.bx
  AND a.a2 = c.c1
  AND d.d1 = b.bc
  -- etc...
  使用join来查询表的语法
  更安全,你可以把连接谓词与连接表放一起,从而防止错误。
  更富于表现力,你可以区分外部连接,内部连接,等等。??
  从中我们学到了什么呢?
  使用JOIN,并且永远不在FROM语句中使用逗号分隔表引用。
  6、SQL的不同类型的连接操作
  连接操作基本上有五种
  EQUI JOIN
  SEMI JOIN
  ANTI JOIN
  CROSS JOIN
  DIVISION
  这些术语通常用于关系代数。对上述概念,如果他们存在,SQL会使用不同的术语。让我们仔细看看:
  EQUI JOIN(同等连接)
  这是最常见的JOIN操作。它有两个子操作:
  INNER JOIN(或者只是JOIN)
  OUTER JOIN(可以再次拆分为LEFT, RIGHT,FULL OUTER JOIN)
  例子是其中的区别最好的解释:
-- This table reference contains authors and their books.
-- There is one record for each book and its author.
-- authors without books are NOT included
author JOIN book ON author.id = book.author_id
-- This table reference contains authors and their books
-- There is one record for each book and its author.
-- ... OR there is an "empty" record for authors without books
-- ("empty" meaning that all book columns are NULL)
author LEFT OUTER JOIN book ON author.id = book.author_id
  SEMI JOIN(半连接)
  这种关系的概念在SQL中用两种方式表达:使用IN谓词或使用EXISTS谓语。"Semi"是指在拉丁语中的"half"。这种类型的连接用于连接只有"half"的表引用。再次考虑上述加入的作者和书。让我们想象,我们想要作者/书的组合,但只是那些作者实际上也有书。然后我们可以这样写:
  -- Using IN
  FROM author
  WHERE author.id IN (SELECT book.author_id FROM book)
  -- Using EXISTS
  FROM author
  WHERE EXISTS (SELECT 1 FROM book WHERE book.author_id = author.id)
  虽然不能肯定你到底是更加喜欢IN还是EXISTS,而且也没有规则说明,但可以这样说:
  IN往往比EXISTS更具可读性
  EXISTS往往比IN更富表现力(如它更容易表达复杂的半连接)
  一般情况下性能上没有太大的差异,但,在某些数据库可能会有巨大的性能差异。
  因为INNER JOIN有可能只产生有书的作者,因为很多初学者可能认为他们可以使用DISTINCT删除重复项。他们认为他们可以表达一个像这样的半联接:
  -- Find only those authors who also have books
  SELECT DISTINCT first_name, last_name
  FROM author
  这是非常不好的做法,原因有二:
  它非常慢,因为该数据库有很多数据加载到内存中,只是要再删除重复项。
  它不完全正确,即使在这个简单的示例中它产生了正确的结果。但是,一旦你JOIN更多的表引用,,你将很难从你的结果中正确删除重复项。
  更多的关于DISTINCT滥用的问题,可以访问这里的博客。
  ANTI JOIN(反连接)
  这个关系的概念跟半连接刚好相反。您可以简单地通过将 NOT 关键字添加到IN 或 EXISTS中生成它。在下例中,我们选择那些没有任何书籍的作者:
  -- Using IN
  FROM author
  WHERE author.id NOT IN (SELECT book.author_id FROM book)
  -- Using EXISTS
  FROM author
  WHERE NOT EXISTS (SELECT 1 FROM book WHERE book.author_id = author.id)
  同样的规则对性能、可读性、表现力都适用。然而,当使用NOT IN时对NULLs会有一个小警告,这个问题有点超出本教程范围。
  CROSS JOIN(交叉连接)
  结合第一个表中的内容和第二个表中的内容,引用两个join表交叉生成一个新的东西。我们之前已经看到,这可以在FROM语句中通过逗号分隔表引用来实现。在你确实需要的情况下,可以在SQL语句中明确地写一个CROSS JOIN。
  -- Combine every author with every book
  author CROSS JOIN book
  DIVISION(除法)
  关系分割就是一只真正的由自己亲自喂养的野兽。简而言之,如果JOIN是乘法,那么除法就是JOIN的反义词。在SQL中,除法关系难以表达清楚。由于这是一个初学者的教程,解释这个问题超出了我们的教程范围。当然如果你求知欲爆棚,那么就看这里,这里还有这里。
  从中我们学到了什么呢?
  让我们把前面讲到的内容再次牢记于心。SQL是表引用。连接表是相当复杂的表引用。但关系表述和SQL表述还是有点区别的,并不是所有的关系连接操作都是正规的SQL连接操作。对关系理论有一点实践与认识,你就可以选择JOIN正确的关系类型并能将其转化为正确的SQL。

 7、SQL的派生表就像表的变量
  前文,我们已经了解到SQL是一种声明性语言,因此不会有变量。(虽然在一些SQL语句中可能会存在)但你可以写这样的变量。那些野兽一般的表被称为派生表。
  派生表只不过是包含在括号里的子查询。
  -- A derived table
  FROM (SELECT * FROM author)
  需要注意的是,一些SQL方言要求派生表有一个关联的名字(也被称为别名)。
  -- A derived table with an alias
  FROM (SELECT * FROM author) a
  当你想规避由SQL子句逻辑排序造成的问题时,你会发现派生表可以用帅呆了来形容。例如,如果你想在SELECT和WHERE子句中重用一个列表达式,只写(Oracle方言):
  -- Get authors' first and last names, and their age in days
  SELECT first_name, last_name, age
  FROM (
  SELECT first_name, last_name, current_date - date_of_birth age
  FROM author
  )
  -- If the age is greater than 10000 days
  WHERE age > 10000
  注意,一些数据库和SQL:1999标准里已将派生表带到下一级别,,引入公共表表达式。这将允许你在单一的SQL SELECT中重复使用相同的派生表。上面的查询将转化为类似这样的:
  WITH a AS (
  SELECT first_name, last_name, current_date - date_of_birth age
  FROM author
  )
  SELECT *
  FROM a
  WHERE age > 10000
  很明显,对广泛重用的常见SQL子查询,你也可以灌输具体"a"到一个独立视图中。想要了解更多就看这里。
  从中我们学到了什么呢?
  再温习一遍,SQL主要是关于表引用,而不是列。好好利用这些表引用。不要害怕写派生表或其他复杂的表引用。
  8、SQL GROUP BY转换之前的表引用
  让我们重新考虑我们之前的FROM语句:
  FROM a, b
  现在,让我们来应用一个GROUP BY语句到上述组合表引用
  GROUP BY A.x, A.y, B.z
  这会产生一个只有其余三个列(!)的新的表引用。让我们再消化一遍。如果你用了GROUP BY,那么你在所有后续逻辑条款-包括选择中减少可用列的数量。这就是为什么你只可以从SELECT语句中的GROUP BY语句引用列语法的原因。
  请注意,其他列仍然可能可作为聚合函数的参数:
  SELECT A.x, A.y, SUM(A.z)
  FROM A
  GROUP BY A.x, A.y
  值得注意并很不幸的是,MySQL不遵守这一标准,只会造成混乱。不要陷入MySQL的把戏。GROUP BY转换表引用,因此你可以只引用列也引用GROUPBY语句。
  从中我们学到了什么呢?
  GROUP BY,在表引用上操作,将它们转换成一个新表。
  9、SQL SELECT在关系代数中被称为投影
  当它在关系代数中使用时,我个人比较喜欢用"投影"一词中。一旦你生成你的表引用,过滤,转换它,你可以一步将它投影到另一个表中。SELECT语句就像一个投影机。表函数利用行值表达式将之前构造的表引用的每个记录转化为最终结果。
  在SELECT语句中,你终于可以在列上操作,创建复杂的列表达式作为记录/行的一部分。
  有很多关于可用的表达式,函数性质等的特殊规则。最重要的是,你应该记住这些:
  1、你只能使用从“output”表引用产生的列引用
  2、如果你有GROUP BY语句,你只可能从该语句或聚合函数引用列
  3、当你没有GROUP BY语句时,你可以用窗口函数替代聚合函数
  4、如果你没有GROUP BY语句,你就不能将聚合函数与非聚合函数结合起来
  5、这有一些关于在聚合函数包装常规函数的规则,反之亦然
  6、还有…
  嗯,这有很多复杂的规则。他们可以填补另一个教程。例如,之所以你不能将聚合函数与非聚合函数结合起来投影到没有GROUP BY的SELECT语句中是因为:
  1、凭直觉,没有任何意义。
  2、对一个SQL初学者来说,直觉还是毫无帮助的,语法规则则可以。SQL:1999年引入了分组集,SQL:2003引入了空分组集GROUP BY()。每当存在没有显式GROUP BY语句的聚合函数时就会应用隐式的空分组集(规则2)。因此,最初关于逻辑顺序的那个规则就不完全正确了,SELECT的投影也会影响前面的逻辑结果,但语法语句GROUP BY却不受影响。
  是不是有点迷糊?其实我也是,让我们看一些简单的吧。
  从中我们学到了什么呢?
  在SQL语句中,SELECT语句可能是最复杂的语句之一,即使它看起来是那么的简单。所有其他语句只不过是从这个到另一个表引用的的输送管道。通过将它们完全转化,后期地对它们应用一些规则,SELECT语句完完全全地搅乱了这些表引用的美。
  为了了解SQL,重要的是要理解其他的一切,都要尝试在SELECT之前解决。即便SELECT在语法顺序中排第一的语句,也应该将它放到最后。
  10.相对简单一点的SQL DISTINCT,UNION,ORDER BY,和OFFSET
  看完复杂的SELECT之后,我们看回一些简单的东西。
  集合运算(DISTINCT和UNION)
  排序操作(ORDER BY,OFFSET..FETCH)
  集合运算
  集合运算在除了表其实没有其他东西的“集”上操作。嗯,差不多是这样,从概念上讲,它们还是很容易理解的
  DISTINCT投影后删除重复项。
  UNION求并集,删除重复项。
  UNION ALL求并集,保留重复项。
  EXCEPT求差集(在第一个子查询结果中删除第二个子查询中也含有的记录删除),删除重复项。
  INTERSECT求交集(保留所有子查询都含有的记录),删除重复项。
  所有这些删除重复项通常是没有意义的,很多时候,当你想要连接子查询时,你应该使用UNION ALL。
  排序操作
  排序不是一个关系特征,它是SQL仅有的特征。在你的SQL语句中,它被应用在语法排序和逻辑排序之后。保证可以通过索引访问记录的唯一可靠方法是使用ORDER BY a和OFFSET..FETCH。所有其他的排序总是任意的或随机的,即使它看起来像是可再现的。
  OFFSET..FETCH是唯一的语法变体。其他变体包括MySQL'和PostgreSQL的LIMIT..OFFSET,或者SQL Server和Sybase的TOP..START AT(这里)。
  让我们开始应用吧
  跟其他每个语言一样,要掌握SQL语言需要大量的实践。上述10个简单的步骤将让你每天编写SQL时更有意义。另一方面,你也可以从常见的错误中学习到更多。下面的两篇文章列出许多Java(和其他)开发者写SQL时常见的错误:
  · 10 Common Mistakes Java Developers Make when Writing SQL
  · 10 More Common Mistakes Java Developers Make when Writing SQL

posted @ 2014-07-16 09:57 顺其自然EVO 阅读(196) | 评论 (0)编辑 收藏

不要急于切换到Java 8的6个原因

 Java 8是极好的。不过我们在尝完鲜了之后,也开始对它持怀疑的态度。所有好的东西都是有代价的,在这篇文章中,我会分享Java 8的主要的几个难点。在你放弃Java 7升级到8之前,你最好确保自己知道这些。
  并行流会影响性能
  Java 8的所承诺的并行处理是最受期待的新特性之一。集合以及流上的.parallelStream()方法就是实现这点的。它将问题分解成子问题,然后分别运行在不同的线程上,它们可能会被分配到不同的CPU核上,当完成之后再组合起来。这些全都是在底层通过fork/join框架来实现的。好的,听起来很酷吧,在多核环境下的大数据集上,这么做肯定能提升操作速度的,对吧?
  不,如果你用的不对的话,这么做可能会让你的代码变得更慢。在我们运行的基准测试上大概是慢了15%左右,而且还有可能会更糟。假设我们已经是运行在多核环境中了,我们又使用了.parallelStream(),将更多的线程加入了线程池中。这很可能会超出我们的核数的处理能力,并且由于上下文切换,会导致性能出现下降。
  下面是我们的一个性能变差的基准测试,它是要将一个集合分到不同的组里(素数或者非素数):
  Map<Boolean, List<Integer>> groupByPrimary = numbers
  .parallelStream().collect(Collectors.groupingBy(s -> Utility.isPrime(s)));
  还有别的原因可能会让它变得更慢。考虑下这种情况,假设我们有多个任务要完成,其中一个可能花费的时间比其它的更长。将它用.parallelStream() 进行分解可能会导致更快的那些任务完成的时间往后推迟。看下Lukas Krecan的这篇文章,里面有更多的一些例子以及代码。
  诊断:并行处理带来好处的同时也带来了许多额外的问题。当你已经是处于一个多核环境中了,你要时刻牢记这点,并要弄清楚事情表面下所隐藏的本质。
  Lambda表达式的负作用
  Lambda。喔,Lambda。尽管没有你,我们也什么都可以做,但是你让我们变得更优雅,减少了许多样板代码,因此大家都很容易会喜欢上你。假设一下早上我起床了想要遍历世界杯的一组球队,然后计算出它们的长度:
  List lengths = new ArrayList();
  for (String countries : Arrays.asList(args)) {
  lengths.add(check(country));
  }
  如果有了Lambda我们就可以使用函数式了:
  Stream lengths = countries.stream().map(countries -> check(country));
  这太牛了。尽管很多时候它都是件好事,不过把Lambda这样的新元素增加到Java中使得它有点偏离了最初的设计规范。字节码是完全面向对象的,但同时这个游戏里又带上了lambda,实际的代码和运行时之间的差别变得越来越大了。可以读下Tal Weiss的这篇文章,了解更多关于lambda表达式的一些阴暗面。
  最后,这意味着你所写的和你所调试的完全是两个不同的东西。栈信息会变得越来越大,这使得你调试代码变得更加费劲了。
  将空串增加到列表里,原先只是这么简单的一个栈信息:
  at LmbdaMain.check(LmbdaMain.java:19)
  at LmbdaMain.main(LmbdaMain.java:34)
  现在变成了:
at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39)
  lambda表达式引起的另一个问题就是重载:由于lambda的参数必须得强制转化成某个类型才能进行方法调用,而它们可以转化成好几个类型,这可能会导致调用发生歧义。Lukas Eder通过代码示例说明了这点。
  诊断:记住这点,栈跟踪信息可能会成为一种痛苦,不过这并不会阻挡我们使用lambda的脚步。

  默认方法使人困惑
  默认方法使得接口方法的默认实现成为了可能。这的确是Java 8带来的一个非常酷的新特性,但是它多少影响了我们之前所习惯的做事的方式。那为什么还要引入它呢?什么时候不应该使用它?
  默认方法背后最大的动机应该就是如果我们需要给现有的一个接口增加方法的话,我们可以不用重写接口的实现。这使得它可以兼容老的版本。比如说,下面是从Oracle官方的一个Java教程中拿过来的一段代码,它是要给一个指定的时区添加某个功能:
public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default public ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
  问题解决了。是吗?但默认方法将接口及实现弄得有点混淆。类型结构自己是不会纠缠到一起的,所以现在我们得好好驯服下这个新生物了。可以读下RebelLabs的Oleg Shelajev的这篇文章。
  诊断:当你抡起锤子的时候看谁都像颗钉子,记住了,要坚持原始的用例,将一个现有的接口重构成一个新的抽象类是不会有什么用处的。
  下面讲的这些,要么是漏掉的,要么是该删除却仍在的,或者是还没有完全实现的:
  为什么是Jigsaw
  Jigsaw项目的目标是使得Java可以模块化,并将JRE分解成能互相协作的不同组件。项目的初衷是希望Java可以更好,更快,更强地进行嵌入。我已经尽量避免提起“物联网”了,但刚才实际已经说到了。减少JAR包的大小,提升性能,提高安全性,这些也是这个项目的一些愿景。
  那么它怎么样了?Jigsaw目前已经通过了探索性的阶段,进入了第二阶段了,目前将致力于设计及实现能达到上线质量的产品,Oracle的首席架构师Mark Reinhold如是说。这个项目原本是计划随着Java8发布的,后来被推迟到了Java 9,这也是9中倍受期待的新特性之一。
  遗留的问题
  受检查异常
  大家都不喜欢模板代码,这也是为什么lambda会如此流行的原因之一。想像一下异常的样板代码吧,不管你是不是需要捕获或者处理这些受检查异常,你都得去捕获它。尽管是根本不可能发生 的事情,就像下面这个一样,它压根儿就不会发生 :
  try {
  httpConn.setRequestMethod("GET");
  }?catch (ProtocolException pe) { /* Why don’t you call me anymore? */ }
  基础类型
  它们还在这里,要想正确地使用它们简直是种痛苦。正是它使得Java无法成为一门纯粹的面向对象的编程语言,并且其实上移除它们对性能也没有太大的影响。新的JVM语言里也都没有基础类型。
  操作符重载
  Java之父James Gosling曾在一次采访中说道:“我没有采用操作符重载这完全是我个人的喜好,因为我看过太多人在C++中滥用它了”。这也有一定的道理,不过也有不少反对的声音。别的JVM语言也提供了这一特性,但另一方面,它可能会导致下面这样的代码:
  javascriptEntryPoints <<= (sourceDirectory in Compile)(base =>
  ((base / "assets" ** "*.js") --- (base / "assets" ** "_*")).get
  )
  这是Scala的Play框架中的一行真实的代码,我已经有点崩溃了。
  诊断:这些真的算是问题吗?我们都有自己的怪癖,这些也算是Java的吧。未来的版本可能会有惊喜,这些也可能会变,但是向后兼容的问题也在那里等着我们了。
  函数式编程
  Java之前也可以进行函数式编程,虽然说有点勉强。Java 8通过lambda以及别的一些东西改进了这一状态。这确实是最受欢迎的特性,不过并没有之前传说中的那么大的变化。它是比Java 7要优雅多了,不过要想成为真正的函数式语言还有很长的路要走。
  关于这个问题最激烈的评论应该是来自Pierre-yves Saumont 的这一系列的文章了,他详细比较了函数式编程的范式和Java的实现方式之间的区别。
  那么该用Scala还是Java?Java采用了更现代的函数式范式也算是对Scala的一种认可,后者提供lambda也有一段时间了。lambda确实是独领风骚,但是还有许多别的特性比如说trait,惰性求值,不可变性等,它们也是Scala不同于Java之处。
  诊断: 不要被lambda分心了,Java 8算不算函数式编程仍在争论当中。

posted @ 2014-07-16 09:55 顺其自然EVO 阅读(396) | 评论 (0)编辑 收藏

Python fabric远程自动部署简介

 Fabric是一个Python(2.5-2.7)库,用于简化使用SSH的应用程序部署或系统管理任务。
  它提供的操作包括:执行本地或远程shell命令,上传/下载文件,以及其他辅助功能,如提示用户输入、中止执行等。
  本文主要介绍CentOS 6.3上使用fabric进行自动部署的基本方法。
  1.    环境部署
  本节主要介绍python版本升级,pip及fabric部署方法。
  1.1.    Python版本升级
  CentOS 6.3自带的Python版本为2.6,首先需要升级到2.7版本。由于旧版本的Python已被深度依赖,所以不能卸载原有的Python,只能全新安装。
  1. 下载Pyhon,选择下载Gzipped source tar ball (2.7.6) (sig),网址:https://www.python.org/download/releases/2.7.6
  2. 解压安装,命令如下
  tar -xvf Python-2.7.6.tgz
  cd Python-2.7.6
  ./configure --prefix=/usr/local/python2.7
  make
  make install
  3. 创建链接来使系统默认python变为python2.7
  ln -fs /usr/local/python2.7/bin/python2.7 /usr/bin/python
  4. 查看Python版本
  python –V
  5. 修改yum配置(否则yum无法正常运行)
  vi /usr/bin/yum
  将第一行的#!/usr/bin/python修改为系统原有的python版本地址#!/usr/bin/python2.6
  至此CentOS6.3系统Python已成功升级至2.7.6版本。
  1.2.    安装pip
  Pip是一个安装和管理python包的工具。
  安装方法如下:
  1. 下载pip,地址https://raw.github.com/pypa/pip/master/contrib/get-pip.py
  2. 执行安装命令
  python get-pip.py
  3. 创建连接(否则会报错提示“命令不存在”)
  ln -s /usr/local/python2.7/bin/pip /usr/bin/pip
  1.3.    安装fabric
  1. 执行安装命令
  pip install fabric
  2. 创建连接(否则会报错提示“命令不存在”)
  ln -s /usr/local/python2.7/bin/fab /usr/bin/fab
  2.    脚本编写
  本节对fabric用法进行简单介绍,并提供实例以供参考。
  2.1.    Hello,fab
  1. 在当前目录下新建文件fabfile.py,输入内容如下
  def hello():
  print("Hello fab!")
  2. 执行命令fab hello,结果如下
  # fab hello
  Hello fab!
 3. 文件名不为fabfile.py时需进行指定
  # mv fabfile.py test.py
  # fab hello
  Fatal error: Couldn't find any fabfiles!
  Remember that -f can be used to specify fabfile path, and use -h for help.
  # fab -f test.py hello
  Hello fab!
  4. 参数传递
  #vi fabfile.py
  def hello(name):
  print 'Hello %s!'%name
  # fab hello:name=fab
  Hello fab!
  # fab hello:fab
  Hello fab!
  2.2.    本地操作
  执行本地操作命令使用local
  1. fabfile.py脚本内容如下
  from fabric.api import local
  def test():
  local('cd /home/')
  local('ls -l|wc -l')
  2. 执行命令fab test,结果如下
  # fab test
  [localhost] local: cd /home/
  [localhost] local: ls -l|wc -l
  2.3.    远程操作
  执行远程操作命令使用run
  1. fabfile.py脚本内容如下
  from fabric.api import cd,run,env,hosts
  env.hosts=['192.168.85.99:22','192.168.85.101:22']
  env.password='test'
  def test():
  with cd('/home'):
  run("du -sh")
  2. 执行命令fab test,结果如下
  # fab test
  [192.168.85.99:22] Executing task 'test'
  [192.168.85.99:22] run: du -sh
  [192.168.85.99:22] out: 392G      .
  [192.168.85.99:22] out:
  [192.168.85.101:22] Executing task 'test'
  [192.168.85.101:22] run: du -sh
  [192.168.85.101:22] out: 5.6G     .
  [192.168.85.101:22] out:
  Disconnecting from 192.168.85.99... done.
  Disconnecting from 192.168.85.101... done.
  3. 多服务器混合,需要在不同服务器进行不同操作时,可参考如下脚本
  from fabric.api import env,roles,run,execute
  env.roledefs = {
  'server1': ['root@192.168.85.99:22',],
  'server2': ['root@192.168.85.100:22', ]
  }
  env.password = 'test'
  @roles('server1')
  def task1():
  run('ls /home/ -l | wc -l')
  @roles('server2')
  def task2():
  run('du -sh /home')
  def test():
  execute(task1)
  execute(task2)
  结果如下
  # fab test
  [root@192.168.85.99:22] Executing task 'task1'
  [root@192.168.85.99:22] run: ls /home/ -l | wc -l
  [root@192.168.85.99:22] out: 27
  [root@192.168.85.99:22] out:
  [root@192.168.85.100:22] Executing task 'task2'
  [root@192.168.85.100:22] run: du -sh /home
  [root@192.168.85.100:22] out: 1.4G   /home
  [root@192.168.85.100:22] out:
  Disconnecting from 192.168.85.99... done.
  Disconnecting from 192.168.85.100... done.

posted @ 2014-07-16 09:48 顺其自然EVO 阅读(406) | 评论 (0)编辑 收藏

使用Gradle运行集成测试

 如果Gradle构建的项目是一个web项目的话,里面可能包含一些集成测试和功能性测试。这些测试和单元测试不同之处是在运行之前要先在本地将web服务启动起来,并且跑完测试后能够自动的关闭web服务。
  在本地启动web服务我们可以使用Gradle内置的jetty插件。jetty是一个轻量级的web容器,其执行速度快,配置简单,远程调试方便。启用jetty只需在build.gradle中加入对这个插件的引用。
  apply plugin: 'jetty'
  之后可以配置war包的名称,启动的端口等属性。
  apply plugin: 'jetty'
  httpPort = 9876
  [jettyRun, jettyRunWar, jettyStop]*.stopPort = 9966
  [jettyRun, jettyRunWar, jettyStop]*.stopKey = 'stopKey'
  我们需要将集成测试与一般的单元测试分开。因为单元测试并不需要事先启动web服务,保证其执行速度快,能够得到更快的反馈。一般做法是单元测试后缀名为Test.java,集成测试后缀名为IntegrationTest.java。
  配置单元测试执行的测试集合。
  test {
  include '**/*Test.class'
  exclude '**/*IntegrationTest.class'
  }
  然后新建一个Task,用于运行集成测试。
build.gradle
task intTest(type: Test, dependsOn: test) {
include '**/*IntegrationTest.class'
doFirst {
jettyRun.daemon = true
jettyRun.execute()
}
doLast {
jettyStop.execute()
}
}
  上述代码首先是创建一个名为intTest的task,其类型为Test,依赖于test task。该集成测试只会运行后缀名为IntegrationTest的测试类。在运行测试之前,首先采用后台模式启动jetty服务器,运行完测试后再调用jettyStop task停止jetty服务。
  为了使我们在运行gradle build时也会运行intTest task,可以添加对intTest的依赖。
  build.dependsOn intTest
  这样在运行gradle build时也会运行集成测试。并且在集成测试前后web服务会自动的启动和关闭。

posted @ 2014-07-16 09:41 顺其自然EVO 阅读(455) | 评论 (0)编辑 收藏

Asp.net与Flex交互测试记录

  一、利用asp.net为Flex提供数据服务,flex为前端表现。
  二、flex通过三种方式四种代码实现来取数据。分别为
  HttpService、
  WebService、
  RemoteObject、
  RemoteObjectAMF0。
  三、Project文件夹中的
  Vs2010Service提供HttpService、Webservice服务;
  Vs2008RemoteObject提供RemoteObject服务。
  四、FluorineFx.NET+RemoteObjectAMF0文件为附加文件。
  五、<sessionState cookieless="AutoDetect"></sessionState>
  设置可兼容客户端禁用Cookie(要在Flex项目中传递回sessionid)
  六、asmx中使用session需要[WebMethod(EnableSession = true)] 如此设置。
  七、安装FluorineFx.NET+RemoteObjectAMF0文件下的setup(FluorineFx.NET).exe
  可以将项目模板集成到2005和2008中,不支持vs2010。
  八、使用七中的模板建立普通网站(FluorineFx ASP.NET Web Site)项目。
  将Console.aspx设为启动页,可进行类中的方法测试
  九、使用RemoteObjectAMF0时将renaun_com_RemoteObjectAMF0.zip解压把src下的
  com文件夹整体复制到flex项目中;
  注意路径与mxml同路径;
  RemoteObjectAMF0.as中的override public function setCredentials( username:String, password:String):void)修改成override public function setCredentials( username:String, password:String,charset:String=null ):void)
  添加xmlns:renaun="com.renaun.rpc.*"到<mx:Application ……中。
  十、使用普通RemoteObject在mxml的同目录下添加services_config.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="remoting-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="fluorine">
<channels>
<channel ref="my-amf"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost:12432/ReomteFlex/Gateway.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>
  注:http://localhost:12432/ReomteFlex/Gateway.aspx随项目启动的端口变化。
  之后,Flex项目右键-属性-Flex compiler-compiler参数(-locale en_US -services "services_config.xml")

posted @ 2014-07-16 09:40 顺其自然EVO 阅读(193) | 评论 (0)编辑 收藏

最糟糕的编程面试题

 多年前,我写了一篇关于我所鄙视的某些类型的面试题。今天我想讨论一个更具体的问题,而不仅是类型。我从来没有问过自己这个问题,但我已经看有人在实际面试中提这个问题,我正式提名它为最糟糕的编程面试题。
  在之前的公司里,有个同事经常问这个问题,那次是我第一次在面试时听到它。这家公司结对面试,两个工程师,一个候选人。有一天,我和他作为一对,去面试一些杯具的应聘者。我觉得应聘者其实表现不错,然后我的同事抛出了这个问题。应聘者结结巴巴地回答,很明显他囧了。在面试后的聚会,所有面试他的工程师都向他竖起了大拇指,只有我搭档反对雇用他,只因“任何称职的工程师都应该能够回答它”。他确凿地说不能跟那个人共存。值得一提的是,这个故事有个大团圆结局,我们不顾我搭档的抗议,还是招了那个人,并在几个月内炒了我搭档,而那个应聘者仍在那家公司,干得好好的。
  无论如何,我认为有这个问题的面试都是“有问题”的,所以我想在这里说明为什么它几乎是恐怖的一个面试题:
  写一个能检测链表是否存在环路的函数。
  看来像是的基本算法问题,对吗?站起来,在白板上写函数。很正常,对吗?不是,这是不对的,别这么干。
  1.这完全是不恰当的。
  这是一个工作面试。你有一个实时的环境,跟你说话的人在面试着。紧张是很正常的。而那种带有“灵关一闪”的智力题是最坏的问题。你的大脑将致力于思考“该死,我正搞砸这个面试”而不是关注手边的问题。
  人们喜欢“看到应聘者如何思考”,但智力题无助于此。正因为它是智力题。你只能希望智力题给你“恍然大悟”。有时我听到人们想知道应聘者如何处理压力,但你应该知道,面试中本来就是有压力的。
  问人难题完全是浪费时间,这样做只是考察到应聘者以前有没看过这题。或者说考察了他们的演技(当他们听说过这问题并知道答案却假装没听说,然后装模作样逐步推导以得出答案)。
  这条问题是最浪费时间的。你还问为什么?好,想象一下如果一个人真的是第一次听到这个问题,然后你期望他推出答案。
  对于这条题,一般来说的“正确”的答案是龟兔算法,在链表头放两个指针,一个是一步走两个节点,另一个则一步一点;如果走着走着指针指向同一个节点,那就代表有环路了。
  当然,还有更简单的答案,例如,将所有走过的节点标记为“走过”,或者从每个点出发,看能不能回答该点,又或者在遍历过程中做哈希,看有没有重复。但当你抛出这些答案时,面试官又会加条件,要求使用更少的内存或时间或不能改原本的数据结构。而最佳的答案就是龟兔。
  是否一开始就该考虑到这么多?无论如何,看来你很觉得你能考虑周到。链表这种数据结构是1955年由Allen Newell,Cliff Shaw 和 Herbert A. Simon 发现的。而“正确”的链表环路检测算法,则叫做“Floyd 环查找算法”,以纪念发现者Robert W. Floyd,那是1967年的事。
  1955至1967之间,这个问题是开放的,就是说,无数考数学或计算机博士的人都可能将它写进论文里。即使那么多人在钻研着,这个问题还是12年无解。
  你真的觉得你行吗,用20分钟,仅凭超越所有学者的压力,搞定这个曾经12年无解的难题?看来是不行的,你觉得行,只不过是因为你看过答案,然后在面试中,你”似曾相识“、”灵关一闪“。
  2.这完全是不切实际的
  如果上面给的理由还不能让你耻笑那个烂问题,那你再想想,这个问题是否真的有用于日常工作。
  我的意思是:你怎么会在实际中碰到有环的链表?
  我并不是说叫你故意搞出一个有指回自身的链表,而是说,无端端会有这样的东西?
  链表这种数据结构不是抽象的东西,栈或队列才是,你或者会在一些抽象的数据结构中用到链表这种实在的东西。例如栈,你就用来压入,弹出,查看,是吧?那怎么会造成环呢?没有人想把它搞成一团的吧?
  即使你自己写个链表,你也不会想做出这种样子。看下java的LinkedList类,你是无法从外部去操控它的next和prev的,你只能取得第一个或最后一个,添加节点到某一位置,按位置或值删除节点。
 看下java源码就知道,真的没有:
private static class Entry <E> {
E element;
java.util.LinkedList.Entry<E> next;
java.util.LinkedList.Entry<E> previous;
Entry(E e, java.util.LinkedList.Entry<E> entry, java.util.LinkedList.Entry<E> entry1) {
/* compiled code */
}
}
  它是一个私有静态类,你无法从外部实例化它。你不能直接操控next和prev,因为它们俩代表了链表的状态,它们就该这样被封装起来。
  如果你真的把链表搞成有环了,那说明你写错。写错的话,你最好重新写对它,而不是写个“检测环”的方法。
  “检测环”的方法就该这样写:
public class LinkedList {
public boolean containsCycle() {
return false;
}
}
  如果你的链表写对的话,“龟兔算法”返回的结果也跟这个方法一样。
  现实中你是很少有机会亲手写个链表的,即使有,你也别写个能造成环的方法。造成环的方法,只能是“留后门”,“元编程”,“反射”。既然是这样故意的话,那么绕过你的“检测环”也是轻而易举的。
  结论
  很多面试题,都中了以上其中一点,太过困难或者与工作无关。
  而这个问题,两点都中了。
  如果有人给到你满意的答案,就说明那个人死记硬背,无他。因回答不了而被你否决的人,说不定还比你更适合这份实务。
  链表环路检测:别问了。
  更新:有位评论者说如果问题问的是有向图,且每个节点的出度最多只有,即是问“检测这个有向图有没有环”。搞图的话,你确实可能会向API用户提供修改每点的指向的方法,这看上去符合实际。但是,还是那句话,你只是在考察应聘者把CS课程记住了多少,或者你只想随便问问,又或者你想听听他说除了龟兔算法以外的低效算法。

posted @ 2014-07-16 09:29 顺其自然EVO 阅读(178) | 评论 (0)编辑 收藏

Linux上搭建nginx,及简单配置

 在上家公司都是运维安装nginx,到新公司后代码开发完成部署测试服务器要求自己装nginx,研究了好久安装好之后,到正式上线还要自己安装,索性把安装步骤自己记载下来(好大一部分都是在网站找的)。
  一,安装
  1.选定源码目录
  可以是任何目录,本文选定的是/usr/local/src
  cd/usr/local/src
  2.安装PCRE库
  ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/下载最新的PCRE源码包,使用下面命令下载编译和安装PCRE包:
cd/usr/local/src
wgetftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.33.tar.gz
tar-zxvfpcre-8.21.tar.gz
cdpcre-8.21
./configure
make
makeinstall
  3.安装zlib库
  http://zlib.net/zlib-1.2.8.tar.gz下载最新的zlib源码包,使用下面命令下载编译和安装zlib包:
cd/usr/local/src
wgethttp://zlib.net/zlib-1.2.8.tar.gz
tar-zxvfzlib-1.2.8.tar.gz
cdzlib-1.2.8
./configure
make
makeinstall
  4.安装ssl(某些vps默认没装ssl)
  cd/usr/local/src
  wgethttp://www.openssl.org/source/openssl-1.0.1c.tar.gz
  tar-zxvfopenssl-1.0.1c.tar.gz
  5.安装nginx
  Nginx一般有两个版本,分别是稳定版和开发版,您可以根据您的目的来选择这两个版本的其中一个,下面是把Nginx安装到/usr/local/nginx目录下的详细步骤:
cd/usr/local/src
wgethttp://nginx.org/download/nginx-1.4.2.tar.gz
tar-zxvfnginx-1.4.2.tar.gz
cdnginx-1.4.2
./configure--sbin-path=/usr/local/nginx/nginx\
--conf-path=/usr/local/nginx/nginx.conf\
--pid-path=/usr/local/nginx/nginx.pid\
--with-http_ssl_module\
--with-pcre=/usr/local/src/pcre-8.21\
--with-zlib=/usr/local/src/zlib-1.2.8\
--with-openssl=/usr/local/src/openssl-1.0.1c
make
makeinstall
--with-pcre=/usr/src/pcre-8.21指的是pcre-8.21的源码路径。
--with-zlib=/usr/src/zlib-1.2.7指的是zlib-1.2.7的源码路径。
  安装成功后/usr/local/nginx目录下如下
fastcgi.confkoi-winnginx.conf.default
fastcgi.conf.defaultlogsscgi_params
fastcgi_paramsmime.typesscgi_params.default
fastcgi_params.defaultmime.types.defaultuwsgi_params
htmlnginxuwsgi_params.default
koi-utfnginx.confwin-utf
  6.启动
  确保系统的80端口没被其他程序占用,运行/usr/local/nginx/nginx命令来启动Nginx,
  netstat-ano|grep80
  如果查不到结果后执行,有结果则忽略此步骤(ubuntu下必须用sudo启动,不然只能在前台运行)
  sudo/usr/local/nginx/nginx
  打开浏览器访问此机器的IP,如果浏览器出现Welcometonginx!则表示Nginx已经安装并运行成功。
  二,简单配置(nginx后挂resin或者tomcat)
  (标红部分为自己新加的配置)
  注:只是简单的配置
http{
upstreammgame-crm{
server10.10.0.103:9080;
server10.10.0.104:9080;
}
includemime.types;
default_typeapplication/octet-stream;
#log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
#'$status$body_bytes_sent"$http_referer"'
#'"$http_user_agent""$http_x_forwarded_for"';
#access_loglogs/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout65;
#gzipon;
server{
listen80;
server_namelocalhost;
#charsetkoi8-r;
#access_loglogs/host.access.logmain;
location/{
roothtml;
indexindex.htmlindex.htm;
proxy_passhttp://mgame-crm;
proxy_redirectoff;
proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;
proxy_set_headerX-Real-IP$remote_addr;
proxy_set_headerHost$http_host;
access_logoff;
}
#error_page404/404.html;
#redirectservererrorpagestothestaticpage/50x.html
#
error_page500502503504/50x.html;
location=/50x.html{
roothtml;
}
#proxythePHPscriptstoApachelisteningon127.0.0.1:80
#
#location~\.php${
#proxy_passhttp://127.0.0.1;
#}
#passthePHPscriptstoFastCGIserverlisteningon127.0.0.1:9000
#
#location~\.php${
#roothtml;
#fastcgi_pass127.0.0.1:9000;

posted @ 2014-07-15 10:33 顺其自然EVO 阅读(229) | 评论 (0)编辑 收藏

我的经常使用linux小命令

  这里并非系统具体介绍每个Linux命令,不过记录本人在平时工作中经经常使用到的一些比較基础的命令及相关的參数,同一时候用了一些简单的样例来说明这些命令的用途,以及怎样用多种命令来实现同一种功能。
  1、ls命令
  要显示当前文件夹下的文件列表时,我常常就使用ls命令的下面三个參数。
  -l 使用长列表格式
  -h 文件大小以人可读的方式打印
  -t 以最后改动时间排序,最新的放在最前
  使用样例,怎样显示当前文件夹下的文件夹?
  方法1: ls -l | grep '^d'
  方法2: ls -l | awk '/^d/'
  2、grep命令
  当须要找出匹配指定模式的文件时,常常会用到grep命令的下面几个參数。
  -w 全然匹配整个单词,而不是字符串的一部分,若要匹配abc,则不会匹配abcd
  -i 忽略大写和小写
  -n 显示指定模式在匹配文件里的行号
  -v 反向匹配,即匹配不符合的行
  -f 从文件里获取待匹配的模式
  使用样例:
  grep ^$ file 匹配文件file中的全部空行
  grep -v '^$' file 匹配文件file中的全部非空行
  grep '[^^$]' file 匹配文件file中的全部非空行
  grep -vf t2 t1 获取t1中跟t2数据不匹配的数据,即获取在t1,但不在t2的数据
  正則表達式[^$]表示匹配空行,前面加上^即表示不匹配空行
  3、排序去重命令 sort vs uniq
  sort 命令用来对数据进行排序。
  -t 用于指定域的分隔符
  -n 以数字方式进行比較,而不是字符比較
  -r 反向排序
  -u 去除反复的行
  -k 以指定的域開始进行排序,域从1開始
  +表示以第几域排序,-表示不以第几域排序,如 sort +0 -2 +3则表示以第0域開始排序,略过第2域,然后再使用第3域排序
  uniq 用于删除文本文件里的连续反复的行,一般使用sort之后再调用uniq。
  -d 仅显示反复一次的行
  -c 打印每一反复行出现的次数
  sort -u 和uniq的差别在于,uniq仅去除连续反复的行,而sort -u会去除整个文本中全部反复的行,比如,
  #源文件file1中的内容
  hello world
  Good morning
  Good morning
  hello world
  #使用sort -u < file1的结果例如以下:
  hello world
  Good morning
  使用uniq < file1的结果例如以下:
  hello world
  Good morning
  hello world
  4、tr命令
  当须要进行一些简单的字符串替换和删除时,tr命令就会显得很实用。
  比如,以下的tr操作都是没有直接改动源文件,改动则须要文件重定向
  tr 'A-Z' 'a-z' < file 将字符串的大写字母都转为小写字母:
  tr -s ' ' ' ' < file 把多个连续空格替换成单个空格,-s选项可将多个连续字符替换为一个指定字符
  tr -s '\n' < file 删除空行
  tr -d '[0-9]' < file 删除全部数字,-d选项能够删除指定的字符
 5、tail命令
  tail命令用于显示文件末尾指定区域的内容,如tail -10 file, 表示显示文件file最后10行内容,tail命令有非常多參数,当中有一个常常使用并且非常好用的參数 "-f":
  tail -f 动态显示文件新增的内容
  该參数作用是不返回文件结束符,并动态显示文件新增的内容,须要结束时,须要用户手动进行中断。这个命令在显示日志的时候作用就很明显,对于一个执行中的server,可能会不断将server当前执行状态和错误状态写入日志文件,使用tail -f,将会以一定的时间实时追踪该文件的全部更新。
  6、find命令
  在查找文件文件的时候,首先会想到是使用find命令。
  -name 依照文件名称查找文件
  比如,查找当前文件夹下名字以cpp结尾的文件
  find . -name "*cpp" | xargs grep "abc"
  有些系统对參数的长度有限制,xargs命令的作用是将參数列表转换成小块分段传递给其它命令,以避免參数列表过长的问题。上面的find中,当前文件夹下可能包括非常多名字以cpp结尾的文件,因此为了避免“參数列太长”或“參数列溢出“的错误,使用xargs来接收find命令的输出。
  7、alias命令
  alias命令用于设置命令的别名。利用alias能够把非常长的命令变成随意简短的命令。
  若要加入自己的alias,格式例如以下
  alias ll="ls -lht"
  上述加入alias的操作,在用户退出后就变为无效了,若要永久生效,能够改动配置文件~/.bashrc文件(仅改动用户自己),在文件最后面加上你自定义的alias,如:
  alias la="ls -al --color=auto"
  本次改动,要在下次登录的时候才干生效。若要立即生效,能够输入:
  source ~/.bashrc
  8、进程相关命令
  ps命令用于查看当前执行的进程。
  ps -ef 显示系统执行的进程
  top 用于查看当前进程执行情况,包含内存使用、cpu使用量、交换区使用量等。
  shift + m 能够按内存占用量来排序进程,查找进程内存使用量就最实用了
  q 退出
  9、经常使用快捷键
  Ctrl + z 将当前程序放到后台并挂起
  Ctrl + d 输入文件结束符,在命令行中直接输入Ctrl + D,即退出shell
  Ctrl + a 跳到命令行的头部位置
  Ctrl + e 跳到命令行的尾部位置
  Ctrl + w 删除一个单字(word)
  Ctrl + u 删除光标到行首的字符
  Ctrl + k 删除光标到行末的字符
  Ctrl + c 中断当前程序
  10、任务命令
  命令运行顺序
  && 第一个命令成功才运行第二个命令
  || 第一个命令失败才运行第二个命令
  jobs 查看挂起的当前的任务列表
  fg + N  将第N个任务放到前端,唤醒Ctrl + z挂起的任务
  11、vim命令
  !%xxd 以十六进制的方式查看文件内容
  ! 后面加上shell命令就而已运行对应的shell命令
  * 将光标移至下一个匹配的单词
  # 将光标移至上一个匹配的单词
  [[ 光标跳到函数的开头
  ]] 光标调到函数的结尾说
  [{ 光标跳到上一个作用域的开头
  ]} 光标跳到下一个作用域的开头
  ggvG或者ggVG 全选并高亮
  gg=G 对整个文件内容进行格式化缩进
  u 撤销上一个动作
  Ctrl + r 取消上一个撤销动作
  set scb 同步两个屏幕的文件,当中一个文件移动,则令一个文件跟着移动,对照两个文件时经常常使用到,解开分屏set scb!
  两个非常好用的插件ctag和Tlist:
  ctags
  功能是扫描指定的源文件,找出当中所包括的语法元素,并将找到的相关内容记录下来。记录的内容包括:宏定义、枚举型、变量的值、函数的定义、原型和声明、namespace、typedefs变量 、类、结构、枚举类型和联合 类、结构和联合中成员变量或函数等。通过ctags我们能够非常easy的找到某个变量或者函数等在哪个文件定义。
  Tlist
  在当前vim中,新开一个区域,显示了当前文件的函数、类、全局变量等定义,输入回车就可以跳转到对应内容的起始位置。平时在查找某个函数时,就能够用到了。
  12、shell中凝视一块代码
  行凝视
  shell中默认提供的凝视方法是#,作用是凝视一行,比如
  #这里是一行凝视
  if [ "1" = "2" ]; then
  DoSomething
  fi
  块凝视
  shell中并没有提供块凝视的方法,可是我们能够使用还有一种方法来实现块凝视,这样的叫做here document。here document表示包括的这段内容是一个总体数据。以下代码中,两个document之间的内容是一个总体数据。
  <<document
  ....
  document
  here document,<<符号表示这是一个文本内容,由其后的字符串包括的范围就是文本的正文,:符号表示空命令,什么都不干,以下的意思就是将一个文本传给空命令,即对文本不做不论什么处理,间接就等于凝视了
  :<<block
  code
  block
  避免反引號不能被凝视的做法是加上单引號
  :<<block'
  code
  'block
  一些小问题
  问题1: 怎样删除文件里的全部空行?
  1、sed '/^$/d' file 对匹配的空行运行删除操作
  2、awk '/./ {print $0;}' file 匹配除空白换行字符外的全部字符,即匹配非空行
  3、awk '/[^^$]/{ print $0; } file
  问题2: 怎样删除文件名称带空格的文件,如"a b.html"s?
  1、对空格进行转义,即空格前加上反斜杠,能够用反斜杠\来屏蔽特殊字符
  2、使用双引號括住文件名称
  3、使用单引號括住文件名称
  问题3:
  一个傻x错误,在运行一段代码后,然后推断$?是否为0,以决定前面的功能代码是否运行成功,可是发现什么情况下都是返回成功,查了好久,原来是在推断$?之前,使用了echo输出 $?,所以永远都是为真了

posted @ 2014-07-15 10:32 顺其自然EVO 阅读(283) | 评论 (0)编辑 收藏

Oracle中SQL语句解析的步骤

 我们都知道在Oracle中每条SQL语句在执行之前都需要经过解析,这里面又分为软解析和硬解析。那么这两种解析有何不同之处呢?它们又分别是如何进行解析呢?Oracle内部解析的步骤又是如何进行的呢?下面我们就这些话题进行共同探讨。
  在Oracle中存在两种类型的SQL语句,一类为DDL语句,他们是从来不会共享使用的,也就是每次执行都需要进行硬解析。还有一类就是DML语句,他们会根据情况选择要么进行硬解析,要么进行软解析。在Oracle 8i OCP教材的023中1-12有说明SQL语句的解析步骤,当一条SQL语句从客户端进程传递到服务器端进程后,需要执行如下步骤:
   在共享池中搜索 SQL 语句的现有副本
   验证 SQL 语句的语法是否准确
   执行数据字典查找来验证表和列的定义
   获取对象的分析锁以便在语句的分析过程中对象的定义不会改变
   检查用户访问引用方案对象的权限
   确定语句的最佳执行计划
   将语句和执行计划载入共享的 SQL 区
  这个先入为主的概念一直占据着我的脑海,我认为硬解析就是上面几个步骤。相对于硬解析,软解析的步骤就是上面第一步找到现有SQL语句的副本后,只需要验证用户是否有权限执行就是了,这样省略上面好几个步骤,相对硬解析来说性能开销就非常小了。即使是在论坛上和大家讨论时,我也一直坚持这个看法。直到前一天看了Tom的《Effective Oracle By Design》中关于语句处理的章节后,我才知道这个自己一直坚持的观点事实上是错误的。
  事实上,在Oracle中SQL语句的解析步骤如下:
  1、语法检测。判断一条SQL语句的语法是否符合SQL的规范,比如执行:SQL> selet  *  from emp;我们就可以看出由于Select关键字少了一个“c”,这条语句就无法通过语法检验的步骤了。
  2、语义检查。语法正确的SQL语句在解析的第二个步骤就是判断该SQL语句所访问的表及列是否准确?用户是否有权限访问或更改相应的表或列?比如如下语句:
  SQL> select * from emp;
  select * from emp
  *
  ERROR at line 1:
  ORA-00942: table or view does not exist
  由于查询用户没有可供访问的emp对象,因此该SQL语句无法通过语义检查。
  3、检查共享池中是否有相同的语句存在。假如执行的SQL语句已经在共享池中存在同样的副本,那么该SQL语句将会被软解析,也就是可以重用已解析过的语句的执行计划和优化方案,可以忽略语句解析过程中最耗费资源的步骤,这也是我们为什么一直强调避免硬解析的原因。这个步骤又可以分为两个步骤:
  (1、)验证SQL语句是否完全一致。在这个步骤中,Oracle将会对传递进来的SQL语句使用HASH函数运算得出HASH值,再与共享池中现有语句的HASH值进行比较看是否一一对应。现有数据库中SQL语句的HASH值我们可以通过访问v$sql、v$sqlarea、v$sqltext等数据字典中的HASH_VALUE列查询得出。如果SQL语句的HASH值一致,那么ORACLE事实上还需要对SQL语句的语义进行再次检测,以决定是否一致。那么为什么Oracle需要再次对语句文本进行检测呢?不是SQL语句的HASH值已经对应上了?事实上就算是SQL语句的HASH值已经对应上了,并不能说明这两条SQL语句就已经可以共享了。我们首先参考如下一个例子:
  假如用户A有自己的一张表EMP,他要执行查询语句:select * from emp;用户B也有一张EMP表,同样要查询select * from emp;这样他们两条语句在文本上是一模一样的,他们的HASH值也会一样,但是由于涉及到查询的相关表不一样,他们事实上是无法共享的。假如这时候用户C又要查询同样一条语句,他查询的表为scott下的公有同义词,还有就是SCOTT也查询同样一张自己的表emp,情况会是如何呢?
SQL> connect a/a
Connected.
SQL> create table emp ( x int );
Table created.
SQL> select * from emp;
no rows selected
SQL> connect b/b
Connected.
SQL> create table emp ( x int );
Table created.
SQL> select * from emp;
no rows selected
SQL> conn scott/tiger
Connected.
SQL> select * from emp;
SQL> conn c/c
Connected.
SQL> select * from emp;
SQL> conn/as sysdba
Connected.
SQL> select address,hash_value, executions, sql_text
from v$sql
where upper(sql_text) like 'SELECT * FROM EMP%'
/
ADDRESS  HASH_VALUE EXECUTIONS SQL_TEXT
-------- ---------- ---------- ------------------------
78B89E9C 3011704998          1 select * from emp
78B89E9C 3011704998          1 select * from emp
78B89E9C 3011704998          2 select * from emp
...
  我们可以看到这四个查询的语句文本和HASH值都是一样的,但是由于查询的对象不同,只有后面两个语句是可以共享的,不同情况的语句还是需要硬解析的。因此在检查共享池共同SQL语句的时候,是需要根据具体情况而定的。
  我们可以进一步查询v$sql_shared_cursor以得知SQL为何不能共享的原因:
SQL> select kglhdpar, address,
auth_check_mismatch, translation_mismatch
from v$sql_shared_cursor
where kglhdpar in
( select address
from v$sql
where upper(sql_text) like 'SELECT * FROM EMP%' )
/
KGLHDPAR ADDRESS  A T
-------- -------- - -
78B89E9C 786C9D78 N N
78B89E9C 786AC810 Y Y
78B89E9C 786A11A4 Y Y
...
  TRANSLATION_MISMATCH表示SQL游标涉及到的数据对象是不同的;AUTH_CHECK_MISMATCH表示对同样一条SQL语句转换是不匹配的。
  (2、)验证SQL语句执行环境是否相同。比如同样一条SQL语句,一个查询会话加了/*+ first_rows */的HINT,另外一个用户加/*+ all_rows */的HINT,他们就会产生不同的执行计划,尽管他们是查询同样的数据。我们下面就一个实例来说明SQL执行环境对解析的影响,我们通过将会话的workarea_size_policy变更来查看对同样一条SQL语句执行的影响:
SQL> alter system flush shared_pool;
System altered.
SQL> show parameter workarea_size_policy
NAME                                 TYPE        VALUE
------------------------------------ ----------- --------------
workarea_size_policy                 string      AUTO
SQL> select count(*) from t;
COUNT(*)
----------
5736
SQL> alter session set workarea_size_policy=manual;
Session altered.
SQL> select count(*) from t;
COUNT(*)
----------
5736
SQL> select sql_text, child_number, hash_value, address
from v$sql
where upper(sql_text) = 'SELECT COUNT(*) FROM T'
/
SQL_TEXT                       CHILD_NUMBER HASH_VALUE ADDRESS
------------------------------ ------------ ---------- --------
select count(*) from t                    0 2199322426 78717328
select count(*) from t                    1 2199322426 78717328
...
  可以看到由于不同会话workarea_size_policy设置得不同,即使是同样一条SQL语句还是无法共享的。通过进一步查询v$sql_shared_cursor我们可以发现两个会话的优化器环境是不同的:
SQL>selectoptimizer_mismatch
fromv$sql_shared_cursor
wherekglhdparin
(selectaddress
fromv$sql
whereupper(sql_text)='SELECTCOUNT(*)FROMT');
O
-
N
Y
...
  通过如上三个步骤检查以后,如果SQL语句是一致的,那么就会重用原有SQL语句的执行计划和优化方案,也就是我们通常所说的软解析。如果SQL语句没有找到同样的副本,那么就需要进行硬解析了。
  4、Oracle根据提交的SQL语句再查询相应的数据对象是否有统计信息。如果有统计信息的话,那么CBO将会使用这些统计信息产生所有可能的执行计划(可能多达成千上万个)和相应的Cost,最终选择Cost最低的那个执行计划。如果查询的数据对象无统计信息,则按RBO的默认规则选择相应的执行计划。这个步骤也是解析中最耗费资源的,因此我们应该极力避免硬解析的产生。至此,解析的步骤已经全部完成,Oracle将会根据解析产生的执行计划执行SQL语句和提取相应的数据。

posted @ 2014-07-15 10:31 顺其自然EVO 阅读(219) | 评论 (0)编辑 收藏

与加密后的Access数据库建立连接

  Default.aspx
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>与加密后的Access数据库建立连接</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<div>
<table style="width: 587px; height: 338px">
<tr>
<td colspan="6">
<asp:Image ID="Image1" runat="server" ImageUrl="~/image/head.gif" /></td>
</tr>
<tr>
<td style="width: 100px; height: 13px">
<asp:Label ID="Label1" runat="server" Font-Size="Smaller" Text="输入连接Access数据库的密码:"
Width="199px"></asp:Label></td>
<td style="width: 77px; height: 13px">
<asp:TextBox ID="TxtMm" runat="server" TextMode="Password"></asp:TextBox></td>
<td style="width: 77px; height: 13px">
<asp:Button ID="BtnOK" runat="server" OnClick="BtnOK_Click" Text="显示数据" Width="58px" /></td>
<td style="width: 100px; height: 13px">
<asp:Label ID="lblMessage" runat="server" Font-Bold="True" Font-Size="Smaller" ForeColor="Red"
Width="296px"></asp:Label></td>
<td style="width: 100px; height: 13px">
</td>
<td style="width: 95px; height: 13px">
</td>
</tr>
<tr>
<td colspan="6" style="height: 142px">
<asp:GridView ID="GridbookSell" runat="server" AutoGenerateColumns="False" CellPadding="4"
Font-Size="Smaller" ForeColor="#333333" GridLines="None" Width="788px">
<FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<Columns>
<asp:BoundField DataField="图书编号" HeaderText="图书编号" />
<asp:BoundField DataField="图书名称" HeaderText="图书名称" />
<asp:BoundField DataField="价格" HeaderText="价格" />
<asp:BoundField DataField="出版时间" HeaderText="出版时间" />
<asp:BoundField DataField="类别" HeaderText="类别" />
<asp:BoundField DataField="备注" HeaderText="备注" />
</Columns>
<RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
<EditRowStyle BackColor="#999999" />
<SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
<PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
<HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
</td>
</tr>
<tr>
<td colspan="6" style="height: 60px">
<asp:Image ID="Image2" runat="server" ImageUrl="~/image/foot.gif" /></td>
</tr>
</table>
</div>
</div>
</form>
</body>
</html>
  Default.aspx.cs
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Data.OleDb;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void BtnOK_Click(object sender, EventArgs e)
{
string ConStr = "Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:DataBase Password=" + this.TxtMm.Text + ";User Id=admin;Data source=" + Server.MapPath("db_ADO.mdb");
OleDbConnection Con = new OleDbConnection(ConStr);
if (this.TxtMm.Text == "")
{
Response.Write("<script language=javascript>alert('对不起!文本框不能为空!');location='javascript:history.go(-1)'</script>");
}
else
{
try
{
//打开数据库连接
Con.Open();
OleDbDataAdapter Dap = new OleDbDataAdapter("select * from tb_booksell", Con);
DataSet ds = new DataSet();
Dap.Fill(ds, "tb_booksell");
GridbookSell.DataSource = ds;
GridbookSell.DataBind();
this.lblMessage.Text = " 恭喜你,与加密后的Access数据库连接成功!";
}
catch (Exception error)
{
this.lblMessage.Text = " 很遗憾,密码错误,请重新输入密码!";
return;
}
finally
{
//关闭数据库连接
Con.Close();
}
}
}
}

posted @ 2014-07-15 10:30 顺其自然EVO 阅读(179) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 83 84 85 86 87 88 89 90 91 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜