| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 685 人关注过本帖
标题:接《SQL-SELECT命令与OCCURS()函数查询时间对比的探讨》的探討
取消只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏
 问题点数:0 回复次数:8 
接《SQL-SELECT命令与OCCURS()函数查询时间对比的探讨》的探討
原帖鏈接:https://bbs.bccn.net/thread-429032-1-1.html

先貼SQL代碼:
程序代码:
*-SQL命令-*
T1=SECONDS()
SELECT A,COUNT(*) 次数 FROM (SELECT A FROM SJ ;
UNION ALL SELECT B FROM SJ ;
UNION ALL SELECT C FROM SJ ;
UNION ALL SELECT D FROM SJ ;
UNION ALL SELECT E FROM SJ ;
UNION ALL SELECT F FROM SJ) A GROUP BY 1 ORDER BY 2 INTO CURSOR TMP
? "SQL命令运行时间:"  + TRANSFORM(SECONDS()  - T1) + "秒"


用原貼附件的數據表,在本人電腦上的運行時間通常有三個値:0.31s,0.47s,0.16s,較常出現的是0.31s,關於這種時間差異的分析,參見原帖本人的跟帖。下面將貼出本人使用傳統VFP代碼的結果。
收到的鲜花
  • tlliqi2014-03-26 07:35 送鲜花  40朵   附言:向你学习
2014-03-25 22:47
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
使用傳統VFP代碼編寫的程序:

程序代码:
*--------------------------------
* 现有一数据表SJ,字段A、B、C、D、E、F中的记录是01-33共33个数据中的其中任意6个数据。
* 要求:统计出字段A、B、C、D、E、F中的所有记录01-33每个数据出现的次数
* 方法:SQL-SELECT命令与OCCURS()函数两种方法,最后得出每种方法的查询时间。
*
* 原帖鏈接:http://bbs.bccn.net/thread-429032-1-1.html
*--------------------------------

#DEFINE FIELD_NUM    6
#DEFINE VALUE_NUM    33

CLEAR ALL
CLEAR
DIMENSION aFieldList[FIELD_NUM]
FOR nIndex = 1 TO FIELD_NUM
    aFieldList[nIndex] = CHR(ASC('A') + nIndex - 1)
NEXT
DIMENSION aResult[VALUE_NUM]
STORE 0 TO aResult
USE "SJ" ALIAS "sj" EXCLUSIVE IN 0
SELECT "sj"
nStartTime = SECONDS()
SCAN ALL
    FOR nIndex = 1 TO FIELD_NUM
        nValue = INT(VAL(EVALUATE("sj." + aFieldList[nIndex])))
        aResult[nValue] = aResult[nValue] + 1
    NEXT
ENDSCAN
nEndTime = SECONDS()
USE IN "sj"
FOR nIndex = 1 TO VALUE_NUM
    ? TRANSFORM(nIndex, "99"), "次數 = ", aResult[nIndex]
NEXT
?
? "耗時(s) = ", nEndTime - nStartTime
?
CLEAR ALL
RETURN


這個代碼的執行時間,結果與前相同,也是這種時間段及出現規律。由於這是在同一機器上的執行結果,所以不需考慮機器差異問題。下文將給出該程序的詳細解述並與前一程序進行比較分析。

授人以渔,不授人以鱼。
2014-03-25 22:53
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
程序代码:
*--------------------------------
* 现有一数据表SJ,字段A、B、C、D、E、F中的记录是01-33共33个数据中的其中任意6个数据。
* 要求:统计出字段A、B、C、D、E、F中的所有记录01-33每个数据出现的次数
* 方法:SQL-SELECT命令与OCCURS()函数两种方法,最后得出每种方法的查询时间。
*
* 原帖鏈接:http://bbs.bccn.net/thread-429032-1-1.html
*--------------------------------

#DEFINE FIELD_NUM    6        && 需要統計的字段數
#DEFINE VALUE_NUM    33       && 字段的値域

CLEAR ALL
CLEAR 

*--------------------------------
* 構造並初始化一個字段清單數組,每個元素是需要統計的字段名字符串,這是為適應字段名及其在表中排佈沒有規律的情形,是迴避硬編碼的措施
* 不管將來需要統計的字段如何變化,都不用再修改下面的統計核心代碼,調整此數組即可,也可以用外部表替代數組,具備更強的零活性
DIMENSION aFieldList[FIELD_NUM]
FOR nIndex = 1 TO FIELD_NUM
    aFieldList[nIndex] = CHR(ASC('A') + nIndex - 1)
NEXT
*--------------------------------

*--------------------------------
* 構造一個統計結果數組,每個元素是統計獲得的次數,元素的下標即字段値,初始化每個元素的値為零
* 比如,某記錄某字段的値是03,則在此數組的3號元素上加1
DIMENSION aResult[VALUE_NUM]
STORE 0 TO aResult
*--------------------------------

USE "SJ" ALIAS "sj" EXCLUSIVE IN 0
SELECT "sj"
nStartTime = SECONDS()
*---------------------------------
* 統計的核心算法
SCAN ALL
    FOR nIndex = 1 TO FIELD_NUM
        nValue = INT(VAL(EVALUATE("sj." + aFieldList[nIndex])))        && 獲取每條記錄指定待統計字段的値,並把字符型數據轉化為整數,作爲數組下標
        aResult[nValue] = aResult[nValue] + 1                          && 出現次數加1
    NEXT
ENDSCAN
*---------------------------------
nEndTime = SECONDS()
USE IN "sj"

*---------------------------------
* 輸出統計結果
FOR nIndex = 1 TO VALUE_NUM
    ? TRANSFORM(nIndex, "99"), "次數 = ", aResult[nIndex]
NEXT
*---------------------------------

?
? "耗時(s) = ", nEndTime - nStartTime
?
CLEAR ALL
RETURN

補充註釋:函數EVALUATE()的功能,是對用字符串形式給出的字段名求出字段値,相當於&宏替換,但這個函數比&宏替換零活及好用得多,因爲參數字符串可以是任意表達式,含運算過程,衹要返回的結果是字符串即可,而&宏要求一個事先賦値的字符型變量。

[ 本帖最后由 TonyDeng 于 2014-3-25 23:26 编辑 ]

授人以渔,不授人以鱼。
2014-03-25 23:18
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
現在我們來看看那個SQL代碼。這個代碼對字段A~F,重覆了6次,即不厭其煩地寫了6行,本來應該是循環能做的機械行爲,變成人工勞動,背離了人類使用計算機的初衷。可以想象一下,假如我們需要統計的字段數目不止是6個而是十多、數十個的時候,這個勞動量有多大。問題還不在於重覆多少次,因爲祗要是確定的、有限的數目,都可以吃虧點多做一些事情,但若是事先無法知道確定統計對象的時候,這個代碼就難寫了——可以做到的,就是編程動態構造這整條命令的字符串,不過看起来更繞。

後面的傳統代碼,把SQL封裝的糖衣剝了開來,還原它內部的真實動作——它就是這樣做的!這也是我堅信兩者的效率絕不可能有巨大差異的原因。

授人以渔,不授人以鱼。
2014-03-27 09:30
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
關於效率。從實際結果看,兩者的時間效率是相當的,但效率並不僅僅看時間,它們之間實際上仍然有差異的。

從語法上看,那條SQL指令,使用了嵌套SELECT,即從一個查詢結果中再篩選結果,而被嵌套的那個SELECT查詢,又使用了多個合併(UNION)操作的結果集,也就是說,它有3層查詢,而且中間的一層有6次一樣的操作,對這麽一個相對簡單的統計功能來說,繞這麽大一個圈實現,確實有點難了——這裡的難一是指難以想到這種方案,二是指閱讀者難以理解。虽然SQL指令體系做了卓越的優化,把這樣複雜的查詢語句精簡為內部很簡單的循環動作(這一點是讓人不得不服的),但它仍然遺留了一點垃圾,即使用工作區生成了一個臨時視圖,這個視圖在原則上必須主動釋放並交還工作區(我不知道有多少人在大程序中或需要反覆使用SQL查詢的情形中有清理臨時數據的習慣)。

純粹從執行時間上比較效率沒什麽意義,兩種方案真正的區別是在網絡環境中。當代碼在本機上執行時,兩者的效率一樣,但若在客戶機上發送指令到服務器上查詢,差異就體現出來了。客戶機通過網絡向服務器發送一條字符串(即那條SQL指令),服務器上的SQL服務程序就會解釋執行它,然後把查詢結果集通過網絡發送回到客戶端。在服務器上,這查詢就是在本地機上的執行時間,相當快,然後就是視查詢結果有多少數據來看反饋的時間,即看有多少數據要流經網路回到客戶手中,這效率由數據量和網絡硬件速度決定(諸如如何優化SQL查詢效率一類的文章建議儘量避免搞出全表掃描和返回大量數據,原因正在這裡)。而用傳統代碼,指令是由客戶機讀取遠程服務器上的數據到本地上執行的,這時間效率就取決於即時網絡速度,一般在沒有特殊優化的情形下,這與被查詢源數據量的多少有關,因爲它可能需要把源數據都通過網絡讀一遍到客戶本地機上。所以,在網絡環境,尤其是廣域網上,SQL查詢的效率通常是比傳統方案高的(查詢消耗在服務器上,受併發查詢數量和服務器性能制約,而傳統方案是分佈式的,消耗在客戶端)。但這僅僅是問題的一個方面,另一個方面是安全,當通過網絡傳送SQL查詢字符串的時候,它有可能被截獲、破解及篡改,這就是WEB網站上最令人頭痛的SQL注入攻擊問題。



[ 本帖最后由 TonyDeng 于 2014-3-27 11:54 编辑 ]

授人以渔,不授人以鱼。
2014-03-27 10:12
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
關於SQL注入攻擊可參看如右資料:http://wenku.baidu.com/view/7656374ce518964bcf847cb0.html

授人以渔,不授人以鱼。
2014-03-27 10:20
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
搞明白了用傳統方法解決這個問題的思路,諸如分數段統計之類的問題,其實都是同類,根本不用一個一個地針對性求解,那樣是永遠學不完的。

授人以渔,不授人以鱼。
2014-03-27 11:16
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
當今微軟在.NET框架中弱化SQL的作用,推行Ling,也是基於SQL容易被注入攻擊的原因。我一直避免使用SQL語句,其實也是因爲上述的諸多問題。

授人以渔,不授人以鱼。
2014-03-27 11:22
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
總有太多的人以爲代碼短執行效率就高,這是錯覺,把自己閱讀的字數少當作機器執行的指令少,殊不知機器執行的是最終機器碼(這裡的最終是指有實際動作的直接代碼,中斷和API之類要把所調用的代碼算進來),它不是你所看到的源代碼,往往“一條指令”之類的東西正是包裹了龐大內涵的東西,由於各種原因可能存在比實際所需還多的動作指令,其效率一定比針對性的定制代碼低,這是顯而易見的。用C寫一下程序就知道了,用API的方式寫,有很多語句,但最終的執行效率比那些封裝過的高級函數要高得多,就是這個道理。

太多的初學者喜歡求“一條指令”,我祗能冷眼旁觀,看他最終能走多遠。

授人以渔,不授人以鱼。
2014-03-31 22:05
快速回复:接《SQL-SELECT命令与OCCURS()函数查询时间对比的探讨》的探討
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.031138 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved