一段有意思的JS For循环代码

var a =0;
for (var i =0,j =0; i <10,j<6;i++,j++) {
	a = i+j;
	console.log("i="+i);
	console.log("j="+j);
	console.log("a="+a);
}

朋友面试,遇到的面试题,电话跟我说完就回来写下来试了一下,结果与自己预想的一样。

没写之前,电话里说的思路是,因为 j 限制了 小于 6,那么应该只循环6次,实际上也确实是这样。

所以最终结果,a = 10。

但是想不明白为什么会有这样的面试题,或者说,什么样的情况会需要写这样的代码呢?

MySQL实现row_number第二部分

接上篇:MySQL实现row_number

为分组增加row number

row_number的分析函数怎么样?(原文:How about row_number “over partition by” functionality? ),比如,如果想为每一个组增加row number,并且在每一个新的分组重置它。

查看下图中payments表。

SELECT
    customerNumber, paymentDate, amount
FROM
    payments
ORDER BY customerNumber;

设想下,为每一条customer数据增加一个当前行的编号,并且每当customer的number字段变化的时候行编号都被重置。

为了实现这个,需要使用两个临时变量,一个作为当前行编号,另一个用来存 上一条customer number与当前行的number进行对比,查询语句如下。(这句有点绕,可能翻译的不太准确,请以原文为准。To achieve this, you have to use two session variables, one for the row number and the other for storing the old customer number to compare it with the current one as the following query:)

SELECT 
    @row_number:=CASE
        WHEN @customer_no = customerNumber THEN @row_number + 1
        ELSE 1
    END AS num,
    @customer_no:=customerNumber as CustomerNumber,
    paymentDate,
    amount
FROM
    payments
ORDER BY customerNumber;

在这段查询代码中,我们使用了CASE声明,如果customer的number不变,就给row_number变量+1,否则,重置为1,结果如下:

与为每一行记录生成row_number一样,也可以使用派生表(derived table)和交叉连接(cross join)生成同样的结果。

SELECT 
    @row_number:=CASE
        WHEN @customer_no = customerNumber THEN @row_number + 1
        ELSE 1
    END AS num,
    @customer_no:=customerNumber as CustomerNumber,
    paymentDate,
    amount
FROM
    payments,(SELECT @customer_no:=0,@row_number:=0) as t
ORDER BY customerNumber;

本教程中,我们展示了如何在MySQL中模仿row_number方法。

原文地址:MySQL row_number, This Is How You Emulate It

MySQL实现row_number

在本教程中,我们将在MySQL中实现一个非常实用的row_number功能。

row_number是一个返回数据排序编号的排名方法,从1开始。我们经常需要用到row_number去生成某些报表,不幸的是,MySQL并不像MSSQL,Oracle一样支持这个方法。在MySQL中如果想实现这个功能需要临时变量。

为了在MySQL中实现row_number,需要在查询中实用临时变量,下图中从employees表中查询出5条数据,并且从1开始,为每一行添加了编号(row number)。

SET @row_number = 0;
 
SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees
LIMIT 5;

在上面的查询中:

首先,我们定义了一个变量叫做row_number,初始值为0,row_number是以@为前缀的一个临时变量。

然后,在查询中,我们为这个变量每次+1,LIMIT分句是为了限制返回的结果数,此处设为5.

另一个方法是使用临时变量作为派生表,联合主表。看下边的查询语句:

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees,(SELECT @row_number:=0) AS t
LIMIT 5;

需要注意,派生表必须使用别名,以使查询语句在语法上没有问题。

由于时间关系(回家过年),暂时到这里,文中部分内容可能翻译的有点不太顺,可以直接看下原文,原文中还有一部分讲分组查询中实用row_number。

这个之后再写。

原文地址:MySQL row_number, This Is How You Emulate It

ECharts饼图示例

今天写一个做地区分布的饼图的东西。

由于数据库里本身没有存储地区数据,只有IP,所以也用到了高春辉老师目前的项目里提供的IP地址数据库。可以在数据库下载页面下载到免费的数据库文件和一个PHP处理类。

还用到了百度团队开源的ECharts,很简单很好用。

其实事情本身并没有难度。

约定

默认为:此时已有一个静态页面,并且已引入echarts的js文件。

先读取数据


	$sql = "SELECT id,ips FROM table order by id desc limit 100000";
	
	$pdo = new PDO('mysql:host=127.0.0.1;dbname=db1','root','root');
	
	$query = $pdo -> query($sql);
	$query->setFetchMode(PDO::FETCH_ASSOC);
	$rs = $query->fetchAll();	
	

根据IP得出所在地信息


	include './IP.class.php';//ipip.net提供的IP处理类
	$IP = new IP();
	foreach ($rs as $key => $value) {
		$a = $IP->find($value["ips"]);
		$arr[$key] = $a[1].$a[2]; //正确的IP返回的数据为:Array ( [0] => 中国 [1] => 黑龙江 [2] => 鹤岗 [3] => ) ,这里根据实际情况取对应字段即可。
	}
	

生成echarts所需的数据

	$data = array_count_values($arr);

嗯,一个非常简单粗暴的把同名地区数量算出来的方法。这样就生成了如array(‘北京’=>2,‘天津’=>3)这样的数据。正是echarts所需的。

饼图区域

	<div id="main" style="width: 1400px;height:1400px;"></div>

正经的JS来了

	var myChart = echarts.init(document.getElementById('main'));
	
	var option = {
       
        title : {
	        text: 'IP分布地区',
	        x:'center'
	    },
	    tooltip : {
	        trigger: 'item',
	        formatter: "{a} <br/>{b} : {c} ({d}%)"
	    },
	    legend: {
	        orient: 'vertical',
	        left: 'left',
	        //简单起见此处直接foreach了
	        data: [<?php foreach ($keys as $key => $value) { echo '"'.$value.'",'; }?>]
	    },
	    series : [
	        {
	            name: 'IP所在地区',
	            type: 'pie',
	            radius : '40%',
	            center: ['50%', '60%'],
	            data:[
	            	//同上,
	            	<?php 
	            		//reset($data);
						while (list($key, $val) = each($data)){
							echo '{value:'.$val.',name:"'.$key.'"},';
						}
	            	?>
	            ],
	            itemStyle: {
	                emphasis: {
	                    shadowBlur: 10,
	                    shadowOffsetX: 0,
	                    shadowColor: 'rgba(0, 0, 0, 0.5)'
	                }
	            }
	        }
	    ]
    };
    
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option);

打开页面看一下吧。

一个简单的input获得焦点时的小动画效果

以前经常见到的一种页面表单效果,但是并没有在项目中用过,前几天在sf上看到相关问题,顺手写了一个,还挺简单,没什么含量。

此处省略HTML头信息等内容。

HTML

	<div>
	  <input type="text" id="uname"/>
	  <label>Name</label>
	</div>

CSS

div{
  position:relative;
  top:20px;
}
input{
  width:200px;
  height:30px;
  line-height:30px;
  padding:0 3px;
  border:none;
  border-bottom:1px solod #666;

}
label{
  position:absolute;
  top:0;
  left:5px;
  color:#ccc;
  height:30px;
  line-height:30px;
}

JS

$(document).on('focus','input',function(){
  $(this).siblings('label').animate({top:'-20px'});
})
.on('focusout','input',function(){
  $(this).siblings('label').animate({top:'0'});
})

RESULT

不足

没有考虑label的鼠标点击事件。

crontab实现定时备份数据库

crontab命令之前写过了,在Linux crontab 访问PHP URL完成定时任务,今天写了一个用来备份数据库的脚本。

主要会用到以下几个命令:

mysqldump

参考文章:mysqldump导入导出数据库总结

创建.sh文件:

cd ~
vi backup.sh

backup.sh内主要内容如下:

mysqldump -hlocalhost -uroot -p'root' --databases database1 | gzip > /var/backups/databases-database1`date +'%Y%m%d_%H%M%S'`.sql.gz

首先用mysqldump命令

	1.连接数据库
	2.选择要备份数据库
	3.选择存储备份文件的方式,这里使用了gzip了生成一个压缩包

根据文档,如果想备份所有数据库,可以使用

mysqldump -hlocalhost -uroot -p'root' --all-databases | gzip > /var/backups/databases-all-database`date +'%Y%m%d_%H%M%S'`.sql.gz

保存。

有备份,就会有备份后的处理,显而易见的问题是备份多了会比较占空间,并且也用不到那么多备份。所以备份完成删除掉一段时间以前的就可以了。这步也可以在备份前做,无所谓。

这里又用到了find命令

参考文章:Linux中find常见用法示例

删除之前的备份文件

在刚才的backup.sh中继续输入:

cd /var/backups/
rm -rf `find . -name '*.sql.gz' -mtime +10`

这句命令有两部分,

第一部分是删除命令:’rm -rf’。就是那句一定要慎用的命令了.

第二部分是找到:当前目录,名字以’.sql.gz’结尾的,更改时间在10天以前的文件。

’.‘表示当前目录,由于上一句是’cd /var/backups/‘,所以这里使用当前目录即可。

’-name ‘和’-mtime’参数是find命令的条件。

具体的说明,和其他条件可以参考前边说到的文章。

到这里基本上备份脚本就完成了。

但是为了什么时候突然想看一下日志,或者备份出错的时候查问题,还可以在脚本里加上记录日志的命令:

日志

echo 'Begin Backup Database At :' `date +'%Y-%m-%d %H:%M:%S'`

这里又用到了date。

参考文章:Linux下date命令,格式化输出,时间设置

脚本保存之后,记得添加执行权限;

sudo chmod +x backup.sh

接下来就是在系统里添加crontab任务了。

cd /etc/
vi crontab

在文件末尾,加上

m  h dom mon dow user	command
00 5 * * * root /home/yourname/backup.sh >> /var/log/backup.log

这样,backup.sh里的echo就会输出到/var/log/backup.log中了。

Over。

使用Dropbox同步hexo文章

继之前那次失败的尝试之后(只在当时写的时候实验过几次,每次都以服务器卡死结束),后来在又多了几篇日志之后连generate也不能愉快的完成了。索性就在本地生成然后git push到服务器。

现在想更激进一些,git只管理日志以外的东西,比如hexo的升级,或模板的调整和日志源文件。而生成的静态文件直接通过Dropbox客户端同步到服务器。

话不多说。

以下为前提:

本地已安装hexo,和Dropbox客户端,并且客户端的同步目录已经选择到hexo的目录。
服务器已安装dropbox服务,及相应的用户。

Dropbox的同步目录选hexo根目录或public都行,只是在服务器的处理脚本那同步修改下就行了。

以下内容假设已在服务器添加dbox用户用于dropbox服务的同步处理。并且也已经设置了与dropbox账户的关联。

启动dropboxd

用dbox用户登录服务器。

然后,启动dropboxd进程。

~/dropbox-dist/dropboxd &

设置文件夹监测

先安装incron服务。
apt-get install incron
yum install incron
开机启动

安装sysv-rc-conf,用于管理服务的启动

apt-get install sysv-rc-conf
sysv-rc-conf incron on
sysv-rc-conf --list //用于查看所有服务的状态

创建监测服务

先修改下incron的编辑器

sudo vi /etc/incron.conf

在文件的最后一行,去掉editor = vi前的#,保存退出。

输入:

incrontab -e

如果当前登录的不是dbox用户,可以使用

incrontab -udbox -e

然后输入:

/home/dbox/Dropbox/yourfolder/ IN_ATTRIB,IN_MOVE /home/dbox/dbox.sh

第一个参数:用来接收Dropbox同步的文件夹

第二个参数:指监测的动作

第三个参数:处理脚本

监测的动作可以用:

IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
#上面所说的文件也包括目录。
处理同步后的文件的脚本
cd /home/dbox/
vi dbox.sh

cd /home/dbox/Dropbox/yoursite/
cp -R public/ /var/www/yoursite/

最后一句要注意看你本地同步了哪些内容,还要注意与网站的目录对应。

还要注意dbox.sh要有执行权限,和yoursite的写入权限。

至此,完成。

使用Let's Encrypt制作数字证书

Let’s Encrypt是一个新的CA机构,提供非常简单并且免费的TLS/SSL证书服务。

可谓万众期待。

说下我的环境:

Ubuntu 14.04.3
Nginx 1.4.6

首先你要有个域名,其次要有个服务器。

STEP 0 - 安装Let’s Encrypt 客户端

安装git

先更新下系统

sudo apt-get update
sudo apt-get -y install git
Clone Let’s Encrypt
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

这里把下载的文件放到了/opt/letsencrypt文件夹

STEP 1-生成证书

首先需要关闭80端口

sudo nginx -s stop

接下来运行Let’s Encrypt

cd /opt/letsencrypt
./letsencrypt-auto certonly --standalone

注意:需要超级用户权限,所以需要输入密码

之后会出现提示框,需要输入邮箱,用于接收提醒和,如果不幸丢失了Key,找回的时候也需要邮箱。

根据提示进行操作即可,接下来啥同意用户协议。

下一步则是输入你需要生成证书的域名了,如果需要一个证书给多个域名使用,这些域名则要全部输入,(example.com和www.example.com是不同的域名),域名之间用空格,逗号,左斜杠 分隔。

成功后会有如下提示:

IMPORTANT NOTES:

 - If you lose your account credentials, you can recover through
   e-mails sent to sammy@digitalocean.com
 - Congratulations! Your certificate and chain have been saved at
    /etc/letsencrypt/live/example.com/fullchain.pem. Your
   cert will expire on 2016-03-15. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

证书位置:/etc/letsencrypt/live/example.com/fullchain.pem

过期时间:2016-03-15

至于过期时间为什么这么短则是出于安全考虑。不宜使用同一个证书太长时间。

证书文件:

上一步成功后,会有如下4个文件生成。

sudo ls /etc/letsencrypt/live/your_domain_name
cert.pem: 证书
chain.pem: The Let's Encrypt chain certificate(不知道什么鬼)
fullchain.pem: cert.pem and chain.pem combined
privkey.pem: 证书私钥

STEP 2-配置Web服务器(Nginx)

证书生成完毕,现在可以配置Web服务器了。

编辑配置文件:

sudo vi /etc/nginx/sites-available/default

在server区块中,增加如下代码:

listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

如果之前是绑定的80端口,直接改为443即可,后边增加 ssl 。

如果想使用最安全的SSL协议,增加如下代码:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;

最后,在server区块后再增加一段,用于从HTTP跳转到HTTPS。

server {
    listen 80;
    server_name example.com;
    rewrite ^/(.*) https://example.com/$1 permanent;
}

保存,退出。

重启Nginx服务器。

sudo nginx -s reopen

现在已经可以通过HTTPS访问网站了。

原文中还有设置自动生成证书的部分,出于懒的原因此处不再写。

参考资料: How To Secure Nginx with Let’s Encrypt on Ubuntu 14.04

PHP使用Redis作为缓存使用PDO读取MySQL数据

图片转自:Using PDO::fetchAll – Examples with codes and output results

一个简单的例子。

实现功能:

1.使用PDO读取数据

2.使用Redis缓存结果

3.再次查询时会从Redis查询,减少MySQL查询

没有注意代码的逻辑,仅实现思路。

<?php

//一上来肯定是配置pdo的连接信息。其实这步在这个示例中可以放到第一个else中。
$dsn = 'mysql:dbname=node;host=127.0.0.1';
$user = 'root';
$password = '';

//连接Redis
$redis= new Redis();
$redis->connect('127.0.0.1',6379);

//接收查询参数
$id = intval($_GET['id']);

//设置在Redis中存储的KEY
$MY_NODE_KEY_ = 'TEST_PDO_REDIS_ID_';

//拼接KEY和查询ID,读取Redis,
$cache = $redis->get($MY_NODE_KEY_.$id);

//用来插入log的时间参数
$date = date("Y-m-d H:i:s");
if($cache){
	//Redis缓存存在则直接输出
	print_r(json_decode($cache,true));
	//并记录log
	error_log($date."---read from redis \r\n", 3, './debug.txt');
}else{
	//缓存不存在,则连接PDO
	try {
    	$pdo = new PDO($dsn, $user, $password);
    	error_log($date."---Connection Succcess \r\n", 3, './debug.txt');
		
		//查询
	    $query = $pdo -> query("select * from news where id = '$id'");
		//设置结果集为数组
	    // [PDOStatement::fetch](http://php.net/manual/zh/pdostatement.fetch.php)
	    $query->setFetchMode(PDO::FETCH_ASSOC);
	    
	    $rs = $query->fetch();
	
	    if (is_array($rs)) {
	    	//查询完成,以json格式写入Redis中。
			$redis->set($MY_NODE_KEY_.$rs['id'],json_encode($rs));
			print_r($rs);
			error_log($date."read from mysql \r\n", 3, './debug.txt');
	    }
	    
	//PDO连接出错
	} catch (PDOException $e) {
		//输出错误信息,并记录log中
    	echo 'Connection failed: ' . $e->getMessage();
    	error_log($date."---Connection failed \r\n", 3, './debug.txt');
	}
}

写的很糙,逻辑已经简单到没有逻辑了。

一句话说一下思路就是: 先看有没有缓存 木有就查数据库,写入缓存

参考资料: PHP 数据对象 PHP PDO的简单使用(query(),exec(),prepare(),Transaction,行锁)

Node.js连接MySQL并读取数据

文章部分代码与之前整理的一篇文章一样。

本文主要实现node连接MySQL并读取指定表的数据输出到ejs模板中。

MySQL是一款非常常用的开源数据库,npm中也有MySQL的包

###MySQL测试库的表结构:

DROP TABLE IF EXISTS `news`;
CREATE TABLE `news` (
  `id` int(11) NOT NULL DEFAULT '0',
  `title` varchar(45) NOT NULL DEFAULT '',
  `createtime` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `news` WRITE;
INSERT INTO `news` VALUES (1,'news one',1449064787),(2,'news two',1449064790),(3,'news three',1449064900);
UNLOCK TABLES;

安装Node.js MySQL包

npm install mysql

server.js

//server.js
var express = require('express');
var mysql = require('mysql');
var moment = require('moment');//用来格式化UNIX时间戳
var app = express();

配置MySQL数据库

var connection = mysql.createConnection({
	host:'localhost',
	user:'root',
	password:'root',
	database:'node'
});

连接数据库,并在成功或失败时输出log

connection.connect(function(err){
	if(!err){
		console.log('Database is connected...\n\n');
	}else{
		console.log('Error Connecting Database...\n\n');
	}
});

定义表

var TABLE = 'news';

设定模板引擎

app.set('view engine','ejs');

这就是很普通的路由,表示接收访问首页的请求

app.get('/',function(req,res){
	//定义一组数据
	var data = [
					{ name : 'Bloody Mary' , drunkness : 3 },
					{ name : 'Martini' , drunkness : 5 },
					{ name : 'Scotch' , drunkness : 10 }
				];

	//MySQL查询
	connection.query(
		//普通的SQL
		'select * from ' + TABLE,
		//查询回调
		function(err,results,fields){
			if(err){
				//输出错误
				throw err;
			}
			
			if(results){
				console.log(results);
				//express render一个页面
				res.render('pages/index',{
					title:'test',
					results:results,
					data:data,
					moment:moment
				});
			}
		}
	)

});

app.listen(8888);
console.log('8888 is the magic port');

index.ejs

<% include ../partials/head %>

<body class="container">
	<main>
		<div class="jumbotron">
			<h2>data from static</h2>
			<ul>
				<% data.forEach(function(d) { %>
				<li>
					<%= d.name %>
					<span><%= d.drunkness %></span>
				</li>
			    <% }); %>
			</ul>
		</div>
		<div class="jumbotron">
			<h2>data from mysql</h2>
			<ul>
				<% results.forEach(function(result) { %>
				<li>
					<%= result.id %>
					<span><%= result.title %></span>
					<span><%= moment(result.createtime*1000).format('YYYY-MM-DD, hh:mm:ss') %></span>
				</li>
			    <% }); %>
			</ul>
		</div>
		
	</main>

	<footer>
		<% include ../partials/footer %>
	</footer>
	
</body>
</html>

至此,就可以正常输出从数据库里读取出来的数据了。

小插曲

之前在练习的时候,require MySQL 之后npm install 只安装了require的几个包,昨天再写的时候装了一大堆。 另外之前在使用moment的时候并没有在render页面的时候传入moment,直接就可以用了,昨天也报错了。