本作代码下载:https://files.cnblogs.com/files/xiandedanteng/LeftInnerNotExist20191222.rar

人们总是喜欢给出或是得到一个简单明了甚至有些粗暴的结论,但现实之复杂往往不是几句简单的话语所能描述。(版权所有)

下结论前,先让我们看一个对比实验。

有一张表delivery_history,表结构如下:

CREATE TABLE delivery_history
(
    id ,) not null primary key,
    name NVARCHAR2() not null,
    order_no ,)  not null ,
    shipper_code ,)  not null ,
    createtime ) not null
)

在这张表里,前面两个字段可以忽略,重要的是order_no,shipper_code,createtime三个字段,order_no代表订单号,shipper_code代表运输者代号,createtime是这条记录创建的时间戳,而我们的主要任务是快速找出order_no和shipper_code相同时,createtime最近的那条记录。delivery_history表中目前有五十万条数据,往后可能更多,因此多要对SQL的执行效率多加考虑。(往下的实验中,考虑到在一张表上反复试验太耗时,也不利于数据的存留,表名会加上数字编号后缀,大家知道它们都是delivery_history表的替身就好。)

为此任务,我书写了下面三种SQL:

方案一:左连接方案
  SELECT
      DH1.ORDER_NO,
      DH1.SHIPPER_CODE
  from
      delivery_history DH1
      left JOIN delivery_history DH2 on
      DH1.SHIPPER_CODE = DH2.SHIPPER_CODE
      and DH1.ORDER_NO = DH2.ORDER_NO
      and DH2.createtime > DH1.createtime
  where DH2.createtime IS NULL  
方案二:groupby内连接方案
  select
      DH1.ORDER_NO,
      DH1.SHIPPER_CODE
  from
      delivery_history dh1 ,
      (select SHIPPER_CODE,ORDER_NO,max(createtime) as utime from delivery_history
             group by SHIPPER_CODE,ORDER_NO) dh2
  where
      dh1.SHIPPER_CODE=dh2.SHIPPER_CODE and
      dh1.ORDER_NO=dh2.ORDER_NO and
      dh1.createtime=dh2.utime   
方案三:not exist方案
select
    a.ORDER_NO,
    a.SHIPPER_CODE
from delivery_history a                                            

                  from delivery_history b
                  where b.SHIPPER_CODE=a.SHIPPER_CODE and
                        b.ORDER_NO=a.ORDER_NO and
                        b.createtime>a.createtime)     

经过仔细比对,这三种方案均能完成任务,那么哪种速度最快呢?

在给出最终结论之前,让我们来看看数据的情况:

上图中,我已经用红框将数据分组了,可以观察得知,基本上是一条记录对应order_no和shipper_code都相同的一组,跨多条记录的组仅有三组。

对这样的数据,三种方案谁更强呢?

性能测试结果如下:

 ::, INFO[main]-Compare query in table'delivery_history01'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.

说明一下,deliery_history表中有五十万条数据,分成了389755组,leftMap,innerMap,notExistMap存的都是组内时间最靠近现在的记录。

对比表格如下:

左连接 groupby内连接 not exist方式
5s431ms 6s648ms 6s211ms

可以看出,在基本是一条记录对应一组的情况下,左连接胜出。

从这里我们可以得出结论,如果数据分组很小,导致主从表记录数差别不大时,左连接是最快的。

再造一次数据,这回力图减少一条记录对一组的情况:

可以看出,包含多条数据的组越来越多了。

再测试一下:

 ::, INFO[main]-Compare query in table'delivery_history02'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.

这回,五十万条记录分了19万2062组,组内扩大了,分组减少了。

对比表格如下:

左连接 groupby内连接 not exist
4s134ms 3s644ms 4s184ms

这回的胜出者是内连接方案,它相对另外两种有数百毫秒的优势。

下面我让组内部扩大些,同时分组数就更少了。

这把1-44条记录为第一组,45-83位第二组,组内扩大很多。

测试结果:

 ::, INFO[main]-Compare query in table'delivery_history03'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.

这次五十万记录缩成了一万五千四百多组,而内联方案再次胜出,领先优势接近一倍,not exist方案也开始超越左连接。

左连接 groupby内连接 not exist
5s434ms 2s979ms 4s479ms

此时我们可以推断出,随着组的扩大,内联方案中经过group by后的从表的规模急剧缩小,再与主表连接后结果集就比其它方案数据少,因此而胜出了。

让我们再次扩大组以验证这个理论。

这一把近一百条都归到一组内,结果还会是内联方案胜出吗?

 ::, INFO[main]-Compare query in table'delivery_history'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.

上面的理论是对的,组越大,group by后的从表就越小,因而形成的结果集就小,内联的领跑优势越来越明显,而not exist方式也与第三名拉开了差距。

左连接 groupby内连接 not exist
10s190ms 2s80ms 7s709ms

是不是内联就稳了呢?不着急下结论,让我们增加shipper看看。

这种情况下,order_no相同,但shipper不同的情况增加了,组被进一步细化。

测试结果:

 ::, INFO[main]-Compare query in table'delivery_history04'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.

你可以发现,左连接方案依然落后,但前两名差距不大了。

左连接 groupby内连接 not exist
4s744ms 3s186ms 3s637ms

我们发现在增加shipper情况下,not exist方案开始跟上了!

我再次增加shipper,终于让notExist方案成为第一名:

 ::, INFO[main]-Compare query in table'delivery_history06'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.
左连接 groupby内连接 not exist
4s169ms 3s425ms 3s206ms

再次增加十个shipper,notExist方案优势就出来了:

 ::, INFO[main]-Compare query in table'delivery_history07'.
 ::, INFO records.
 ::, INFO records.
 ::, INFO records.
 ::, INFO[main]-There are same elements in leftMap and innerMap.
 ::, INFO[main]-There are same elements in leftMap and notExistMap.
左连接 groupby内连接 not exist
4s144ms 4s615ms 3s994ms

依照上面的实验,我们可以得出以下结论:

数据零散不成组时,左连接最快;

数据按order_no分组越大,shipper数量不多时,groupby内连接方案最快;

shipper数量越多,not exist方案优势越大。

从这些实验可以看出来,不同的数据,会导致不同的方案胜出;或者说,没有最快的sql方案,只有最适配数据的方案。

没想到吧,SQL优化工作最后成了数据分析工作。

以下是用到的代码:

关于建表的代码:

package com.hy;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.log4j.Logger;

// Used to create a table in oracle
public class TableCreater {
    private static Logger log = Logger.getLogger(TableCreater.class);
    private final String table="delivery_history07";

    public boolean createTable() {
        Connection conn = null;
        Statement stmt = null;

        try{
            Class.forName(DBParam.Driver).newInstance();
            conn = DriverManager.getConnection(DBParam.DbUrl, DBParam.User, DBParam.Pswd);
            stmt = conn.createStatement();

            String createTableSql=getCreateTbSql(table);
            stmt.execute(createTableSql);

            if(isTableExist(table,stmt)==true) {
                log.info("Table:'"+table+"' created.");
                return true;
            }

        } catch (Exception e) {
            System.out.print(e.getMessage());
        } finally {
            try {
                stmt.close();
                conn.close();
            } catch (SQLException e) {
                System.out.print("Can't close stmt/conn because of " + e.getMessage());
            }
        }

        return false;
    }

    /**
     * Get a table's ddl
     * @param table
     * @return
     */
    private String getCreateTbSql(String table) {
        StringBuilder sb=new StringBuilder();
        sb.append("CREATE TABLE "+table);
        sb.append("(");
        sb.append("id NUMBER(8,0) not null primary key,");
        sb.append("name NVARCHAR2(60) not null,");
        sb.append("order_no NUMBER(10,0) DEFAULT 0 not null ,");
        sb.append("shipper_code NUMBER(10,0) DEFAULT 0 not null ,");
        sb.append("createtime TIMESTAMP (6) not null");
        sb.append(")");

        return sb.toString();
    }

    // Execute a sql
    //private int executeSql(String sql,Statement stmt) throws SQLException {
    //    return stmt.executeUpdate(sql);
    //}

    // If a table exists
    private boolean isTableExist(String table,Statement stmt) throws SQLException {
        String sql="SELECT COUNT (*) as cnt FROM ALL_TABLES WHERE table_name = UPPER('"+table+"')";

        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            int count = rs.getInt("cnt");
            return count==1;
        }

        return false;
    }

    // Entry point
    public static void main(String[] args) {
        TableCreater tc=new TableCreater();
        tc.createTable();
    }
}

用于创建数据的代码:

package com.hy;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.apache.log4j.Logger;

// Used to insert ten thousands of records to table 'delivery_hisotry'
public class TableRecordInserter {
    private static Logger log = Logger.getLogger(TableCreater.class);

    private final String Table="delivery_history07";
    private final int Total=500000;

    public boolean fillTable() {
        Connection conn = null;
        Statement stmt = null;

        try{
            Class.forName(DBParam.Driver).newInstance();
            conn = DriverManager.getConnection(DBParam.DbUrl, DBParam.User, DBParam.Pswd);
            conn.setAutoCommit(false);
            stmt = conn.createStatement();

            long startMs = System.currentTimeMillis();
            clearTable(stmt,conn);
            List<String> insertSqls=generateInsertSqlList();
            betachInsert(insertSqls,stmt,conn);
            long endMs = System.currentTimeMillis();
            log.info("It takes "+ms2DHMS(startMs,endMs)+" to fill "+Total+" records.");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                stmt.close();
                conn.close();
            } catch (SQLException e) {
                System.out.print("Can't close stmt/conn because of " + e.getMessage());
            }
        }

        return false;
    }

    private void clearTable(Statement stmt,Connection conn) throws SQLException {
        stmt.executeUpdate("truncate table "+Table);
        conn.commit();
        log.info("Cleared table:'"+Table+"'.");
    }

    private int betachInsert(List<String> insertSqls,Statement stmt,Connection conn) throws SQLException {
        int inserted=0;
        final int BatchSize=250;
        int count=insertSqls.size();
        int index=0;
        int times=count/BatchSize;
        for(int i=0;i<times;i++) {
            StringBuilder sb=new StringBuilder();
            sb.append("INSERT ALL ");

            for(int j=0;j<BatchSize;j++) {
                index=i*BatchSize+j;
                sb.append(insertSqls.get(index));
            }

            sb.append(" select * from dual");
            String sql = sb.toString();

            int n=stmt.executeUpdate(sql);
            inserted+=n;
            conn.commit();

            log.info("#"+i+" inserted " +n+" records.");
        }

        return inserted;
    }

    private List<String> generateInsertSqlList() {
        List<String> sqlList=new ArrayList<String>();
        int index=0;
        do {
            int orderNoRange=getRandom(1,100);// 调整order_no,L89
            int orderNo=index*1000+orderNoRange;
            for(int i=0;i<orderNoRange;i++) {
                int shipper_code=getShipperCode();

                String insertSql=getInsertSql(index,orderNo,shipper_code);
                sqlList.add(insertSql);

                index++;
            }
        }while(index<Total);

        log.info("generated "+sqlList.size()+" insert sqls.");

        return sqlList;
    }

    // get partial insert sql
    private String getInsertSql(int id,int orderNo,int shipperCode) {
        String raw=" INTO {0}(id,name, order_no,shipper_code,createtime) values(''{1}'',''{2}'',''{3}'',''{4}'',sysdate) ";

        String ids=String.valueOf(id);
        String name="N_"+ids;

        Object[] arr={Table,ids,name,String.valueOf(orderNo),String.valueOf(shipperCode)};

        return MessageFormat.format(raw, arr);
    }

    // get a random shipper-code
    private int getShipperCode() {
        int[] arr= {1111,2222,3333,4444,5555,6666,7777,8888,9999,1010,2020,3030,4040,5050,6060,7070,8080,9090,1011,2022,3033,4044,5055,6066,7077,8088,9099,1811,2822,3833,4844,5855,6866,7877,8888,9899};// 调整shipper_code,L120
        int seed=getRandom(0,arr.length-1);
        return arr[seed];
    }

    // get a random integer between min and max
    public static int getRandom(int min, int max){
        Random random = new Random();
        int rnd = random.nextInt(max) % (max - min + 1) + min;
        return rnd;
    }

    // change seconds to DayHourMinuteSecond format
    private static String ms2DHMS(long startMs, long endMs) {
        String retval = null;
        long secondCount = (endMs - startMs) / 1000;
        String ms = (endMs - startMs) % 1000 + "ms";

        long days = secondCount / (60 * 60 * 24);
        long hours = (secondCount % (60 * 60 * 24)) / (60 * 60);
        long minutes = (secondCount % (60 * 60)) / 60;
        long seconds = secondCount % 60;

        if (days > 0) {
            retval = days + "d" + hours + "h" + minutes + "m" + seconds + "s";
        } else if (hours > 0) {
            retval = hours + "h" + minutes + "m" + seconds + "s";
        } else if (minutes > 0) {
            retval = minutes + "m" + seconds + "s";
        } else {
            retval = seconds + "s";
        }

        return retval + ms;
    }

    // Entry point
    public static void main(String[] args) {
        TableRecordInserter tri=new TableRecordInserter();
        tri.fillTable();
    }
}

用于比较的代码:

package com.hy;

import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

//Used for hold columns
class DhItem {
    String order_no;
    String shipper_code;

    public String toString() {
        List<String> ls = new ArrayList<String>();

        ls.add(order_no);
        ls.add(shipper_code);

        return String.join(",", ls);
    }
}

public class Comparer {
    private static Logger log = Logger.getLogger(Comparer.class);
    private final String Table="delivery_history07";

    // print three plan comparison
    public void printComparison() {
        Connection conn = null;
        Statement stmt = null;

        try {
            Class.forName(DBParam.Driver).newInstance();
            conn = DriverManager.getConnection(DBParam.DbUrl, DBParam.User, DBParam.Pswd);
            stmt = conn.createStatement();

            log.info("Compare query in table'"+Table+"'.");

            long startMs = System.currentTimeMillis();
            Map<String,DhItem> leftMap=fetchMap(getLeftjoinSql(),stmt);
            long endMs = System.currentTimeMillis();
            log.info("It takes "+ms2DHMS(startMs,endMs)+" to run LeftjoinSql and fetch "+leftMap.size()+" records.");

            startMs = System.currentTimeMillis();
            Map<String,DhItem> innerMap=fetchMap(getInnerSql(),stmt);
            endMs = System.currentTimeMillis();
            log.info("It takes "+ms2DHMS(startMs,endMs)+" to run innerJoinSql and fetch "+innerMap.size()+" records.");

            startMs = System.currentTimeMillis();
            Map<String,DhItem> notExistMap=fetchMap(getNotExistSql(),stmt);
            endMs = System.currentTimeMillis();
            log.info("It takes "+ms2DHMS(startMs,endMs)+" to run notExistSql and fetch "+notExistMap.size()+" records.");

            if(compare(leftMap,innerMap)==true) {
                log.info("There are same elements in leftMap and innerMap.");
            }

            if(compare(leftMap,notExistMap)==true) {
                log.info("There are same elements in leftMap and notExistMap.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                stmt.close();
                conn.close();
            } catch (SQLException e) {
                System.out.print("Can't close stmt/conn because of " + e.getMessage());
            }
        }
    }

    // Compare the elements in two map
    private boolean compare(Map<String,DhItem> scrMap,Map<String,DhItem> destMap) {
        int count=0;
        for(String key:scrMap.keySet()) {
            if(destMap.containsKey(key)) {
                count++;
            }
        }

        return count==scrMap.size() && count==destMap.size();
    }

    private Map<String,DhItem> fetchMap(String sql,Statement stmt) throws SQLException {

        Map<String,DhItem> map=new HashMap<String,DhItem>();

        ResultSet rs = stmt.executeQuery(sql);
        while (rs.next()) {
            DhItem dhItem=new DhItem();
            dhItem.order_no=rs.getString("order_no");
            dhItem.shipper_code=rs.getString("shipper_code");
            map.put(toMD5(dhItem.toString()), dhItem);
        }

        return map;
    }

    // DH表自己和自己进行左连接方案(三种方案都是为了获得ORDER_NO,SHIPPER_CODE相同时创建时间最新的记录)
    private String getLeftjoinSql() {
        StringBuilder sb = new StringBuilder();
        sb.append("  SELECT                                              ");
        sb.append("      DH1.ORDER_NO,                                   ");
        sb.append("      DH1.SHIPPER_CODE                                ");
        sb.append("  from                                                ");
        sb.append("      "+Table+" DH1                                   ");
        sb.append("      left JOIN "+Table+" DH2 on                      ");
        sb.append("      DH1.SHIPPER_CODE = DH2.SHIPPER_CODE             ");
        sb.append("      and DH1.ORDER_NO = DH2.ORDER_NO                 ");
        sb.append("      and DH2.createtime > DH1.createtime             ");
        sb.append("  where DH2.createtime IS NULL                        ");
        String sql = sb.toString();
        return sql;
    }

    // DH表先自己分组方案(三种方案都是为了获得ORDER_NO,SHIPPER_CODE相同时创建时间最新的记录)
    private String getInnerSql() {
        StringBuilder sb = new StringBuilder();
        sb.append("  select                                                                                    ");
        sb.append("      DH1.ORDER_NO,                                                                         ");
        sb.append("      DH1.SHIPPER_CODE                                                                      ");
        sb.append("  from                                                                                      ");
        sb.append("      "+Table+" dh1 ,                                                                       ");
        sb.append("      (select SHIPPER_CODE,ORDER_NO,max(createtime) as utime from "+Table+"                 ");
        sb.append("             group by SHIPPER_CODE,ORDER_NO) dh2                                            ");
        sb.append("  where                                                                                     ");
        sb.append("      dh1.SHIPPER_CODE=dh2.SHIPPER_CODE and                                                 ");
        sb.append("      dh1.ORDER_NO=dh2.ORDER_NO and                                                         ");
        sb.append("      dh1.createtime=dh2.utime                                                              ");

        String sql = sb.toString();

        return sql;
    }

    // ‘不存在’最新方案(三种方案都是为了获得ORDER_NO,SHIPPER_CODE相同时创建时间最新的记录)
    private String getNotExistSql() {
        StringBuilder sb = new StringBuilder();
        sb.append("    select ");
        sb.append("        a.ORDER_NO,                                             ");
        sb.append("        a.SHIPPER_CODE                                          ");
        sb.append("    from "+Table+" a                                            ");
        sb.append("    where not exists( select 1                                  ");
        sb.append("                      from "+Table+" b                          ");
        sb.append("                      where b.SHIPPER_CODE=a.SHIPPER_CODE and   ");
        sb.append("                            b.ORDER_NO=a.ORDER_NO and           ");
        sb.append("                            b.createtime>a.createtime)          ");

        String sql = sb.toString();
        return sql;
    }

    /**
     * change seconds to DayHourMinuteSecond format
     *
     * @param startMs
     * @param endMs
     * @return
     */
    private static String ms2DHMS(long startMs, long endMs) {
        String retval = null;
        long secondCount = (endMs - startMs) / 1000;
        String ms = (endMs - startMs) % 1000 + "ms";

        long days = secondCount / (60 * 60 * 24);
        long hours = (secondCount % (60 * 60 * 24)) / (60 * 60);
        long minutes = (secondCount % (60 * 60)) / 60;
        long seconds = secondCount % 60;

        if (days > 0) {
            retval = days + "d" + hours + "h" + minutes + "m" + seconds + "s";
        } else if (hours > 0) {
            retval = hours + "h" + minutes + "m" + seconds + "s";
        } else if (minutes > 0) {
            retval = minutes + "m" + seconds + "s";
        } else {
            retval = seconds + "s";
        }

        return retval + ms;
    }

    public static String toMD5(String key) {
        char hexDigits[] = {
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };
        try {
            byte[] btInput = key.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        Comparer c = new Comparer();
        c.printComparison();
    }
}

--END--2019年12月22日13:05:12

[SQL]用于提取组内最新数据,左连接,内连接,not exist三种方案中,到底谁最快?的更多相关文章

  1. 一次MySQL两千万数据大表的优化过程,三种解决方案

    问题概述 使用阿里云rds for MySQL数据库(就是MySQL5.6版本),有个用户上网记录表6个月的数据量近2000万,保留最近一年的数据量达到4000万,查询速度极慢,日常卡死.严重影响业务 ...

  2. MySQL冗余数据的三种方案

    一,为什么要冗余数据 互联网数据量很大的业务场景,往往数据库需要进行水平切分来降低单库数据量. 水平切分会有一个patition key,通过patition key的查询能够直接定位到库,但是非pa ...

  3. 针对Hbuilderx内置终端无法输入问题,总结了三种方法供大家参考

    下图,是内置终端无法输入的现象(本人使用的第三种方案,解决了该问题) 第一种解决方案,也是网上推荐最多的方案: 打开Hbuilder安装路径下插件文件夹中的main.js文件:HBuilderX\pl ...

  4. mongo数据同步的三种方案

    (一)直接复制data目录(需要停止源和目标的mongo服务)1.针对目标mongo服务已经存在,并正在运行的(mongo2-->mongo).执行步骤:(1).停止源/目标服务器的mongo服 ...

  5. ios学习网络------4 UIWebView以三种方式中的本地数据

    UIWebView这是IOS内置的浏览器.能够浏览网页,打开文档  html/htm  pdf   docx  txt等待格文档类型. safari浏览器是通过UIWebView制作. server将 ...

  6. 将Excel文件数据导入到SqlServer数据库的三种方案

    方案一: 通过OleDB方式获取Excel文件的数据,然后通过DataSet中转到SQL Server,这种方法的优点是非常的灵活,可以对Excel表中的各个单元格进行用户所需的操作. openFil ...

  7. 通过SQL脚本导入数据到不同数据库避免重复导入三种方式

    前言 无论何种语言,一旦看见代码中有重复性的代码则想到封装来复用,在SQL同样如此,若我们没有界面来维护而且需要经常进行的操作,我们会写脚本避免下次又得重新写一遍,但是这其中就涉及到一个问题,这个问题 ...

  8. SQL后台分页三种方案和分析

    建立表:CREATE TABLE [TestTable] ( [ID] [int] IDENTITY (1, 1) NOT NULL , [FirstName] [nvarchar] (100) CO ...

  9. url地址数据参数转化JSON对象(js三种方法实现)

    当我们用get方法提交表单时,在url上会显示出请求的参数组成的字符串,例如:http://localhost:3000/index.html?phone=12345678901&pwd=12 ...

随机推荐

  1. python高级编程之 web静态服务器

    返回固定数据 import socket def request_handler(new_client_socket): """ 响应客户端请求的核心函数 "& ...

  2. python 使用Anaconda管理项目环境

    Pycharm没有内置的python解释器,需要我们自己下载python解释器. 在很多python项目中,会导入第三方的模块,逐个去下载导入很不方便. 我们通常使用Anaconda来管理python ...

  3. 非root安装rpm时,mockbuild does not exist - using root

    1.现象 [fedora@k8s-cluster--ycmwlao4q5wz-minion- ~]$ [fedora@k8s-cluster--ycmwlao4q5wz-minion- ~]$ sud ...

  4. 网络编程系统化学习(1.1.)--socket基础

    大纲 学完该阶段内容,你将会很好的完成如下的面试题 socket面试试题相关: 1.写一个简单的socket程序,实现客户端发送数据,服务端收到数据后,将该内容转发给客户端 2.简要概述一下socke ...

  5. Pycharm安装模块提示module 'pip' has no attribute 'main'的问题

    解决pycharm问题:module 'pip' has no attribute 'main' 转自: <解决pycharm问题:module 'pip' has no attribute ' ...

  6. Webpack 打包工具

    1. webpack 概念 [文档地址](https://www.webpackjs.com/concepts/) 2. webpack 命令使用及相关工具包 1. webpack 安装和打包命令: ...

  7. 云服务器 - 定时备份MariaDB/MySQL

    数据库数据备份尤为重要,而我们不会人工手动去备份,这样会很麻烦,我们都是通过服务器每日自定运行来做的,设置一个定时时间即可 首先我们看一下mysqldump这个文件的位置: 可以看到目录在 /usr/ ...

  8. Ubuntu中如何为wine程序创建快捷方式

    1.安装一个gnome组件及其应用 sudo apt-get install gnome-panel 2.运行以下命令弹出创建快捷方式的窗口 sudo gnome-desktop-item-edit ...

  9. Haproxy 让后端RS记录真实IP

    一.修改haproxy.cfg配置文件,在defaults中加入如下两行,并重启haproxy. vim /etc/haproxy/haproxy.cfg defaults option http-s ...

  10. C++学习(11)—— 友元

    生活中你的家有客厅(Public),有你的卧室(Private) 客厅所有来的客人都可以过去,但是你的卧室是私有的,也就是说只有你能进去 但是呢,你也可以允许你的好闺蜜好基友进去   在程序里,有些私 ...