SQLite中获取随机记录的两种方法以及效率比较

SQLite这个数据库我自己也用的很多。比起mysql它的好处在于不用设置繁琐的host啊username,password,database什么的,直接一个文件上去就好了,类似于Access的方式哈(Access和SQLite能比么~!)。

之前也研究过MySQL上的随机数据获取方法,Google一下一大堆,我就不多说了。总之看了很多MySQL上的文章,SQLite的却没有多少,于是我就自己研究了一下。

我用的环境是Apache/2.2.16 (Win32) PHP/5.2.14,加上PDO的SQLite3接口。还用到一个类库是我自己写的LFPSQlite库,这个库我没有发布过,因为实在是写的太坑了,不想丢丑什么的。

言归正传。今天说的两种获取随机记录的方法,第一种是这样的:

SELECT COUNT(*) FROM 'test';
SELECT * FROM 'test' LIMIT 1 OFFSET #;

其中#是rand(0,$count-1)。

第二种是这样的

SELECT * FROM 'test' ORDER BY random() LIMIT 1;

两种方法的效率究竟如何呢?先来理论分析下。

首先第一种方法是查出数据行数,然后用偏移值来实现读取数据。这两条SQL命令应当是非常快的。第二种方法是用random()排序。虽然有LIMIT 1来限定,但是SQLite应该会将整个表排一次序,那么这个方法就应当比上面慢很多。

具体如何呢?我们来分析一下。我写了一个小脚本,因为用到了我那个没公开的库,所以脚本也就不公开了。我很无聊的把它传到了一个免费空间上,访问地址是:http://jiaowo33.cwsurf.de/sqlite/test.php?times=5 最后那个5可以改成1~200的任意值。用的数据库比较小,只有2W多行。如果你感兴趣可以下载过来看看?在/sqlite/demo.db(406KB)这个位置。

分别执行1,10,100,200次,然后我们来看看程序运行的结果:

信息: 数据库记录数:20915.
信息: 执行1次 COUNT(*)+OFFSET所需时间为0.000886917114258s.
信息: 执行1次OREDER BY RANDOM所需时间为0.0190088748932s.
信息: 效率差值:0.0181219577789s
信息: 执行10次 COUNT(*)+OFFSET所需时间为0.00564503669739s.
信息: 执行10次OREDER BY RANDOM所需时间为0.193027973175s.
信息: 效率差值:0.187382936478s
信息: 执行100次 COUNT(*)+OFFSET所需时间为0.0537490844727s.
信息: 执行100次OREDER BY RANDOM所需时间为1.947660923s.
信息: 效率差值:1.89391183853s
信息: 执行200次 COUNT(*)+OFFSET所需时间为0.105569839478s.
信息: 执行200次OREDER BY RANDOM所需时间为4.20832800865s.
信息: 效率差值:4.10275816917s

效果显而易见。count(*)的执行效率明显比order by random()要高得多。

连接ssh出现Permission denied之一解

问题描述:

在一台centos 6.0的vps上使用ssh无论连接什么样的主机都连接不上,提示信息为:

Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

排除了防火墙等原因,百思不得其解。后来在man ssh里面发现了一个参数-v

     -v      Verbose mode.  Causes ssh to print debugging messages about its
             progress.  This is helpful in debugging connection, authentica‐
             tion, and configuration problems.  Multiple -v options increase
             the verbosity.  The maximum is 3.

于是使用ssh -v localhost试图连接。在debug信息里面发现一条很奇怪的信息,如下:

debug1: Next authentication method: password
debug1: read_passphrase: can't open /dev/tty: No such file or directory

尝试使用password模式时,没办法调用read_passphrase来获取密码。提示/dev/tty这个文件缺失。

于是在本地的cygwin(我是win环境)下用ls -l /dev/tty得到以下信息:

crw-rw-rw- 1 oott123 None 5, 0 十月  5 14:53 /dev/tty

使用mknod /dev/tty c 5 0来创建一个新的设备文件,故障解除。

后来##Orz的atmouse发现了这个页面,和我遇到的问题一模一样:【这里】,解决方式也是一样的。

【友情提醒】DiscuzX2.5 R20120901 切勿轻易升级!

昨天我登录卡院的后台,发现DZX2.5出了个更新。出于习惯我备份了数据库和文件,然后直接关闭网站进行后台更新了。可是这次更新并不顺利。

首先我更新到一半就无法下载/source/language/admincp.lang.php(印象中是这样的)这个文件。处于无奈我就手动更新了一下dzx。更新过程没有任何问题,后台显示为20120901版本,我就放心的打开论坛了。

但是晚上就有很多用户给我反馈说回帖的时候显示内部错误,没法显示帖子;充值卡的地方验证码输入不了等问题。我上DiscuzX官方论坛看了一眼,好像问题还不止这些。因为没更新数据库,所以我连忙把文件直接还原了。还原以后一切正常,卡院也算恢复了。

嘛。下次更新的话,一定要先去DiscuzX论坛里面看看bug反馈啥的……不然伤不起啊!

在BAE下利用BCms给手机发送短信

最近拿到了bae的邀请码,自然要好好折腾一下。发现bae中有个好功能叫做bcms,可以给任意手机号码发短信,免费配额是1k条/月,应该足够使用了。

代码在bae上部署很方便,如下:

<?php
require_once 'Bcms.class.php';
$bcms = new Bcms();
$bcms->sms(<队列名字>,<要发送的消息>,json_encode(<收件人数组>));

如果不在BAE上部署BCMS的代码,可以参考CplusHua在他的博文:BAE消息队列发送短信中提到的方法。

【消息速递&360安全快递】私有API

最近在研究如何给自己的手机方便的Po信息,于是发现360安全快递和消息速递两款产品。

简单的对网页进行抓包后给出api分析如下:(没有研究文件上传)

消息推送
 
content=内容
domains=推送号
nickname=推送签名
permitcode=授权码
 

360安全快递
 
content=内容
toid=原始快递号(系统分配的那个,可以通过http://kuaidi.360.cn/dev/get/?tokey=来获取)
tokey=快递号,建议使用和toid相同的数据
permitcode=授权码的md5值
signature=目前无用,推测和消息推送一样用于签名

消息推送的服务器只接受post的请求,而360安全快递可以接收POST/GET的请求。

ps.消息推送推出的discuzx插件中也有一个供discuzx专用的api,具体懒得研究了。

DiscuzX2.5 群组开放回帖

DZX的群组权限控制一直很犯二的样子,可以限制加入和浏览权限,但是不加入就死活没法发帖回帖。我把DiscuzX2.5的文件修改了一下,让它做到“能浏览即可回帖”。

修改方法:

1.找到./source/function/function_group.php第39行左右

	if($action == 'post' && !$isgroupuser) {
		return 4;
	}

改为:

	if($action == 'post' && !$isgroupuser) {
		return '';
	}

 

2.找到./source/moudle/forum/forum_viewthread.php第286行左右

$fastpost = $_G['setting']['fastpost'] && !$_G['forum_thread']['archiveid'] && ($_G['forum']['status'] != 3 || $_G['isgroupuser']);

改为:

$fastpost = $_G['setting']['fastpost'] && !$_G['forum_thread']['archiveid'];

经过以上两步,你的dzx群组就可以做到有查看权限的用户都能回帖发帖了~

ps.自己研究的方法,因为前两天弄的,所以不一定包括了所有要改的地方= =如果还是出不来……那我也没辙了,印象中就改了这两个地方。

pps.这次改代码发现dzx的代码写得真不是盖的诶,逻辑很清晰的。

ppps.最近论坛忙死了= = 所以依然没空更新博客什么的……

疑难杂症+乱七八糟的论坛手札

这几天一直在忙这个该死的DiscuzX搭建的卡院论坛,乱七八糟的事情好多好多……所以都没能更新博客个说。

于是今天有空上来写写遇到的一些琐碎的事情。

首先是免费CDN。本来我用的是Incapsula的CDN系统,只需要更新cname的那个。后来ivmm说Cloudflare更好,于是我就去换了CloudFlare。结果呢?……我自己测试结果,感觉速度一般般,和Incapsula差不多,会员们不乐意了,在群里翻腾说连不上。找了一两个典型用户让他们协助测试,结果CloudFlare很多地方没法访问到ns。虽然可以替换NS,但是懒得折腾了。

然后就是“个性签名”不显示的问题。这个大部分DZX用户可能没有碰到过,但是我确实碰到这种诡异的问题,用户中心无法修改个性签名。百度了很多,就是没有解决办法。偶然翻到“用户栏目”这个管理项目才发现不知道什么时候用户栏目中的“个性签名”被我取消掉了。于是勾上,修复完毕。

最后……我在写论坛管理方案,于是又是很久不能更新博客个说……

innerTEXT,outerHTML,outerTEXT和firefox

Firefox 不支持 DOM 对象的 outerHTML innerText outerText 属性……这是个一直困扰大家(包括我)很久的问题,网上也有不少的解决方法,这里摘录如下,收藏备用。(源链接:http://www.w3help.org/zh-cn/causes/SD9017)

解决方案

在 Firefox 中,可通过扩展 HTMLElement 的原型 (prototype) 来实现相关属性。

  1. 扩展 Firefox 中 DOM 元素的 outerHTML 属性:

    if (typeof(HTMLElement) != "undefined") {
       HTMLElement.prototype.__defineSetter__("outerHTML", function(s) {
            var r = this.ownerDocument.createRange();
            r.setStartBefore(this);
            var df = r.createContextualFragment(s);
            this.parentNode.replaceChild(df, this);
            return s;
        });
       HTMLElement.prototype.__defineGetter__("outerHTML", function(){
            var a = this.attributes, str = "<" + this.tagName, i = 0;
            for (; i < a.length; i++)
                if (a[i].specified)
                    str += " " + a[i].name + '="' + a[i].value + '"';
            if (!this.canHaveChildren)
                return str + " />";
            return str + ">" + this.innerHTML + "</" + this.tagName + ">";
        });
    
        HTMLElement.prototype.__defineGetter__("canHaveChildren", function(){
            return !/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(this.tagName.toLowerCase());
        });
    }
  2. 扩展 Firefox 中 DOM 元素的 innerText 属性:

    if (!!document.getBoxObjectFor || window.mozInnerScreenX != null) {
        HTMLElement.prototype.__defineSetter__("innerText", function(sText) {
            var parsedText = document.createTextNode(sText);
            this.innerHTML = "";
            this.appendChild(parsedText);
            return parsedText;
        });
        HTMLElement.prototype.__defineGetter__("innerText", function() {
            var r = this.ownerDocument.createRange();
            r.selectNodeContents(this);
            return r.toString();
        });
    }
  3. 扩展 Firefox 中 DOM 元素的 outerText 属性:

    if (!!document.getBoxObjectFor || window.mozInnerScreenX != null) {
        HTMLElement.prototype.__defineSetter__("outerText", function(sText) {
            var parsedText = document.createTextNode(sText);
            this.parentNode.replaceChild(parsedText, this);
            return parsedText;
        });
        HTMLElement.prototype.__defineGetter__("outerText", function() {
            var r = this.ownerDocument.createRange();
            r.selectNodeContents(this);
            return r.toString();
        });
    }