數學-高精度

1.Integer Inquiry

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1047

解法:一道求大數之和,我們利用java里的BigInteger就可以輕鬆的解決,不過需要注意下格式的問題,每輸出一個就要空一行,最後一個不用

代碼:

import java.math.BigInteger;
import java.util.Scanner;
public class Main {
    public static void main(String[] arg)
    {
        Scanner in=new Scanner(System.in);
        int t;
        while(in.hasNext())
        {
            t=in.nextInt();
            while(t-->0)
            {
                BigInteger b=new BigInteger("0");
                while(in.hasNextBigInteger())
                {
                    BigInteger c=new BigInteger("0");
                    c=in.nextBigInteger();
                    if(!c.equals(BigInteger.ZERO))
                    {
                        b=b.add(c);
                    }
                    else
                    {
                        System.out.println(b);
                        if(t!=0)
                            System.out.println();
                        break;
                    }
                }
            }
        }
    }
}

 

2.Exponentiation

 題目:https://vjudge.net/problem/POJ-1001/origin

解法:這道是一個浮點數的次冪求解,通過java里的BigDecimal的pow方法,即可完成對應的求解。

對於輸出有特殊要求,小數點后捨去無意義的0,用到stripTrailingZeros()方法去除所有尾部的0,然後再用toPlainString()將BigDecimal轉化為String

之後,在比對轉化的字符串的第一個字符是否為“0”,如果為0,則去掉它只保留小數點之後的數,利用substring(1),截取字符串

代碼:

import java.math.BigDecimal;
import java.util.Scanner;

public class Main {
    public static void main(String[] arg)
    {
        Scanner in=new Scanner(System.in);
        while(in.hasNext())
        {
            BigDecimal a=in.nextBigDecimal();
            int k=in.nextInt();
            a=a.pow(k);
            String str=a.stripTrailingZeros().toPlainString();
            if(str.charAt(0)=='0')
                System.out.println(str.substring(1));
            else
                System.out.println(str);
        }
    }
}

 

3.How many Fibs?

題目:https://vjudge.net/problem/POJ-2413/origin

思路:這是一道求在一定的範圍內,包含的斐波那契數的個數。由於範圍是10的100次方,因此我們設置大整數數組的長度為10000,足以包括。

代碼:

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    static BigInteger a[]=new BigInteger[10000];
    public static void main(String[] arg)
    {
        Scanner in=new Scanner(System.in);
        for(int i=0;i<10000;i++)
            a[i]=BigInteger.ZERO;
        a[0]=BigInteger.valueOf(1);
        a[1]=BigInteger.valueOf(2);
        for(int i=2;i<10000;i++)
            a[i]=a[i].add(a[i-1].add(a[i-2]));
        BigInteger b,c;
        while(true)
        {
            b=in.nextBigInteger();
            c=in.nextBigInteger();
            if(b.equals(BigInteger.ZERO)&&c.equals(BigInteger.ZERO))
                break;
            int res=0;
            for(int i=0;i<10000;i++)
            {
                if((a[i].compareTo(b)==1||a[i].compareTo(b)==0)&&(a[i].compareTo(c)==-1||a[i].compareTo(c)==0))
                    res++;
                if(a[i].compareTo(c)==1)
                    break;
            }
            System.out.println(res);
        }
    }
}

 

4.Segment

 題目:http://acm.hdu.edu.cn/showproblem.php?pid=5666

解法:這是一道求在一個三角形區域內的整數點的個數,不包含在線上的點。通過遞推分析,找規律後知道,個數為:(q-1)*(q-2)/2%p

由於q很大,所以我們需要用到大整數來進行求解。

代碼:

import java.math.BigInteger;
import java.util.Scanner;

public class Main{
    public static void main(String[] arg)
    {
        Scanner in=new Scanner(System.in);
        int T=in.nextInt();
        while(T-->0)
        {
            BigInteger q=in.nextBigInteger();
            BigInteger p=in.nextBigInteger();
            BigInteger f=q.subtract(BigInteger.valueOf(2));
            BigInteger res=q.subtract(BigInteger.ONE).multiply(f).divide(BigInteger.valueOf(2));
            res=res.mod(p);
            System.out.println(res);
        }
    }
}

 

5.Problem B

題目:https://vjudge.net/problem/HDU-5686/origin

解法:這應該是百度的某道題,模擬幾組數據后發現竟然是一個斐波那契數列,因此就很簡單了,N<=200的因此普通的longlong滿足不了,我們需要使用BigInteger

代碼:

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String[] arg)
    {
        Scanner in=new Scanner(System.in);
        BigInteger a[]=new BigInteger[10000];
        for(int i=0;i<10000;i++)
            a[i]=BigInteger.ZERO;
        a[0]=BigInteger.valueOf(1);
        a[1]=BigInteger.valueOf(1);
        for(int i=2;i<10000;i++)
            a[i]=a[i].add(a[i-1].add(a[i-2]));
        while(in.hasNext())
        {
            int k=in.nextInt();
            if(k>=1&&k<=200)
                System.out.println(a[k]);
        }
    }
}

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

QT槽函數獲取信號發送對象

Qt 在槽函數中獲取信號發送對象

Qt中提供了一個函數 qobject_cast(QObject *object),可以通過這個函數判斷信號發出對象

Qt 幫助文檔的解釋:
Returns the given object cast to type T if the object is of type T (or of a subclass); otherwise returns nullptr. If object is nullptr then it will also return nullptr.
The class T must inherit (directly or indirectly) QObject and be declared with the Q_OBJECT macro.
A class is considered to inherit itself.
The qobject_cast() function behaves similarly to the standard C++ dynamic_cast(), with the advantages that it doesn’t require RTTI support and it works across dynamic library boundaries.

  • 簡單來說 就是使用這個函數會得到你轉型的的一個實例,但是這個類必須繼承自QObject 或者其子類並且聲明Q_OBJECT 這個宏
  • QT 幫助文檔中一個Example
    QObject *obj = new QTimer; 
    QTimer *timer = qobject_cast<QTimer *>(obj);
    

這裏順便記錄一下RTTI 與RAII,RAII 通常應用於對象資源管理,RTTI 可以動態判斷對象類型,但使用RTTI會增加程序運行時間,這裏簡單記錄區分一下 。

  1. RTTI : Run-time type information
    #include <iostream>
    #include <typeinfo>
    
    class Base {
    public:
        virtual ~Base() = default;
    };
    
    class Derived : public Base {};
    
    int main() {
        Base base;
        Derived derived;
        Base* ptr = &derived;
        Base& ref = derived;
        std::cout << typeid(base).name()<< std::endl;  // class Base 
        std::cout << typeid(derived).name()<< std::endl;  // class Derived
        std::cout << typeid(ptr).name()<< std::endl;  // class Base *
        std::cout << typeid(*ptr).name() << std::endl;  //class Derived           
        std::cout << typeid(ref).name() << std::endl;  //class Derived
    
    }
    
  2. RAII : Resource Acquisition Is Initialization
    代碼來源 https://en.cppreference.com/w/cpp/language/raii
    std::mutex m;
    
    void bad() 
    {
        m.lock();                    // acquire the mutex
        f();                         // if f() throws an exception, the mutex is never released
        if(!everything_ok()) return; // early return, the mutex is never released
        m.unlock();                  // if bad() reaches this statement, the mutex is released
    }
    
    void good()
    {
        std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization
        f();                               // if f() throws an exception, the mutex is released
        if(!everything_ok()) return;       // early return, the mutex is released
    }
    

下面是QT通過qobject_cast獲取信號發送對象的一個Demo,通過Qt Desinger 繪製兩個按鈕和一個文本框,將兩個按鈕的點擊事件連接到同一個槽函數,在槽函數裏面判斷信號的發送者並作出不同的響應
主要的代碼如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void onButtonClicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QDebug>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->leftButton,&QPushButton::clicked,this,&MainWindow::onButtonClicked);
    connect(ui->rightButton,&QPushButton::clicked,this,&MainWindow::onButtonClicked);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::onButtonClicked()
{
    QPushButton *button =  qobject_cast<QPushButton *>(sender());

    ui->textLabel->setText(button->text());

    if(ui->leftButton == button)
    {
        qDebug()<<"left Button clicked";
        ui->textLabel->setStyleSheet("background-color:yellow");
        button->setStyleSheet("background-color:yellow");

    }
    else
    {
        ui->textLabel->setStyleSheet("background-color:green");
        button->setStyleSheet("background-color:green");
    }

}


完整的代碼已上傳Github

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

C/C++語言的學習方向

這個世界上有太多的坑,如果我們不具備查閱資料的能力和對現實世界的思考能力,入坑是大概率的事情。

C/C++語言能做什麼?C/C++程序員在做什麼?企業需要什麼樣的C/C++程序員?對初學者來說,要搞清楚這些問題並不容易。

一個农民種菜之前,會考慮菜的行情(包括銷量和價錢),如果您打算學習C/C++語言,也應該先了解C/C++程序員的行情。

了解菜的行情要去菜市場,了解C/C++程序員的行情去人才市場,中國最大的IT人才市是前程無憂。

我們先來查一些資料。

一、中國編程語言的排名情況

登錄前程無憂的官網,分別輸入java、C++、python、javascript、.net、C#、PHP、SQL、IOS關鍵字,城市選擇北京、上海、廣州和深圳,查詢到的招聘職位分別是31032、22842、19274、14091、6956、8018、5284、21302、6014個。(這是2020年6月查到的數據,不同時期的查詢結果略有不同)

數據显示企業的JAVA和C++職位比例約6:4,培訓機構的主流課程是JAVA和Python等,進一步導致了C/C++程序員的缺乏。

培訓機構不願意開設C/C++課程,C/C++語言的教學對老師的能力要求非常高,普通講師難以信任。

編程語言的選擇是人生大事,必須慎重,誰都不希望在工作幾年後被迫從一種編程語言轉向另一種編程語言。

JAVA和C/C++是程序員可選的兩個方向(其它編程語言根本不在考慮範圍),無所謂哪一種更好,我個人偏向C/C++,因為JAVA程序員的工作太累了。

更多的人選擇學習JAVA,在較短的時間內就可以上手,從簡單的WEB頁面做起,所見即所得,容易有成就感,但是C/C++不一樣,學生在學完基礎知識后還是不知道它能做什麼,離企業需求仍然差得很遠,堅持下去的人很少,“從入門到放棄”是多數人的無奈的選擇。

二、企業對C/C++程序員的技能要求

從前程無憂的招聘數據看,中國企業對C/C++的技能要求主要有網絡通信、硬件驅動(嵌入式)、數據庫、視頻音頻、網絡遊戲、桌面圖形、機器學習、大數據、物聯網,如下圖。

以上數據可以從前程無憂的官網查詢,分別輸入“C++ 網絡”、“C++ 嵌入式”、“C++ 數據庫”、“C++ 遊戲”等關鍵字,範圍選北京、上海、廣州和深圳。

值得注意的是,近年來被媒體反覆炒作的人工智能、大數據、物聯網、區塊鏈等高大上的概念,企業需求其實很少,現在股票也不抄這個概念了。

輸入“ C++ Linux”關鍵字搜索,結果有9767條記錄,輸入“C++ Windows”關鍵字搜索,只有2500條記錄。這說明windows作為服務器的操作系統,正在被淘汰中。

目前,大部分的學校和培訓機構讓學生windows平台下學習C/C++語言,而企業要求的卻是linux。

我們應該學習主流技術,不能成為非主流,否則有被淘汰的風險。

三、數據比石油更珍貴

在我們的生活中,數據無處不在,但是,常人對它不敏感。

人口戶藉數據在公安局。

駕照數據和車輛數據也在公安局的交警支隊。

醫院保存了每個病人的基本資料和就診數據。每個產婦在醫院生了小孩子,計劃生育部門也會知道,它們與公安局和醫院有聯網交換數據。

銀行有開戶數據,轉帳記錄。

證券公司有行情數據、股東基本數據、持股數據和交易日誌。

電信公司有開戶數據、通話清單、帳單。

電力局有居民的基本數據和用電帳單。

火車票、機票都是數據。

騰訊公司有QQ帳戶數據、微信帳戶數據、聊天日誌、轉帳日誌等。

阿里公司有淘寶帳戶數據,商戶數據,商品數據、交易日誌等。

煤氣公司、自來水公司……

一個人的身高、體重、學歷、年齡是數據。

圖片、視頻、音頻、文章也是數據。

……

四、C/C++程序員在做什麼

用C/C++做桌面程序的比例約11%,其它的C/C++程序都運行在後台Linux服務器上,普通用戶看不見。

在很多培訓機構的教程中,把貪吃蛇、俄羅斯方塊、打地鼠、球球大作戰等程序當成項目實戰,太過兒戲,這些程序的源代碼網上到處都是,真正的程序員不玩這個。

真正的C/C++程序員在工作,掌握的是謀生的技能,不是在玩過家家。

我們每天都在用微信軟件聊天、付款、轉帳等,對普通用戶來說,只看到微信軟件的操作界面,不清楚有後台系統的存在,以轉帳功能為例。

1)付款人在手機微信中輸入轉帳金額和支付密碼;

2)手機微信軟件把轉帳金額和密碼發送給騰訊公司的微信服務器;

3)微信服務器核對用戶的密碼和轉帳金額,向中國銀聯發起轉帳請求,中國銀聯把轉帳請求轉發給付款方的開戶行銀行,開戶銀行從付款人的帳戶中扣除金額。

4)微信服務器把扣款成功信息返回給手機微信軟件,显示轉帳成功。

5)微信服務器向收款人的手機微信軟件發送收錢提醒,收款人點擊收款后,微信服務器把金額存入收款人的微信帳戶中。

在上述轉帳流程中,蘋果手機的微信軟件採用Objective-C語言開發,其它品牌手機的微信軟件採用JAVA開發;微信服務器、中國銀聯和開戶銀行都是非常龐大的系統,系統之間進行了多次數據傳輸,對數據庫進行了查詢、修改和插入操作,都是採用C/C++語言開發,系統運行在UNIX系統上,效率極高、穩定性極強,工程量巨大,是精英程序員們的心血之作。

對騰訊的開發團隊來說,手機上安裝的微信APP軟件,開發的工作量不會超過整個微信項目的5%。

五、總結一下

1)網絡通信和數據處理以C/C++語言為主,數據輸入、數據展示以java為主。

2)作為服務器的操作系統,必選Linux,Windows已被淘汰,以VC、QT等作為C程序的開發環境極不明智。

3)網絡通信是C/C++程序員的必備技能。

4)數據處理是C/C++程序員的必備技能。

5)嵌入式開發的職位比較多,技術上偏硬件(通信、电子等專業),軟件為輔,無硬件基礎者要慎重考慮。

6)放棄幻想,不要追逐那些高大上的潮流(區塊鏈、大數據、人工智能等),也不要聽那些從入門到精通的鬼話,踏實一點,先成為一個普通程序員,這個世界上從來就沒有捷徑。

7)要學會自己查資料,www.51job.com上的信息量非常大,不要視而不見,不能被別人隨便忽悠。

六、關於視頻教程

2019年五月,我計劃錄製C/C++語言基礎知識的視頻課程,打算採用知名的書藉作為教材,在翻閱多本書藉之後,我非常的失望。作為一個骨灰程序員,二十年來,我非常清楚如何成為一個合格的C/C++程序員,包括學習方向、企業的需求和必備技能等。書店裡能買到的C/C++
書藉,絕大部分是教授和講師編寫出來的,普遍存在三個方面的問題:

1)只介紹知識點,不結合應用的場景,示例代碼和課後作業與實際項目開發嚴重脫節;

2)重點內容不突出,C/C++的知識點非常多,很多內容已經過時,在工作中極少使用,這些內容增加了學習的負擔;

3)紙上談編程,不注重培養程序員的感覺。

從職業程序員的角度出發,我把C/C++語言的基礎知識內容寫成文章,在C語言技術網發布,本教程有以下特點:

1)以Linux平台為學習和開發環境,Linux是C/C++程序員的主流工作平台;

2)斷章取義,重點突出實際開發中常用的內容,略過不常用的知識;

3)設計了大量的課後作業,這些作業與實際開發內容息息相關,把培養程序員的感覺放在首位;

4)本教程的目標是為職業C/C++程序員打基礎,不合適在校大學生的畢業考試和計算機等級考試。

七、獲取視頻教程

百萬年薪程序員錄製,《C/C++高性能服務開發基礎》視頻教程已在CSDN學院發布,優惠價12元,地址如下:
https://edu.csdn.net/course/play/29402

八、版權聲明

C語言技術網原創文章,轉載請說明文章的來源、作者和原文的鏈接。

來源:C語言技術網(www.freecplus.net)

作者:碼農有道

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

SSH免密登錄詳解

SSH免密登錄詳解

SSH(Security Shell)安全外殼協議,是較為可靠的,專為遠程登錄會話和其他網絡服務提供安全保證的協議。

​ 對於傳統的網絡服務程序(例如,FTP,Telnet等)來說,其本質上並不是安全的,主要原因在於,這些網絡應用程序在網絡上都是直接使用明文傳輸口令和數據的,對於別有用心的人來說,這些口令和數據是很容易被截獲的。另外,這些網絡服務程序的安全驗證方式也是存在弱點的,非常容易受到中間人(Man-In-The-Middle)這種方式的攻擊,簡而言之,就是中間人冒充真正的服務器接收你傳輸的數據,然後,再將數據轉發給真正的服務器,通過這種方式中間人就可以神不知鬼不覺地拿到你所有數據。

​ 通過使用SSH,則可以將所有傳輸的數據及口令進行加密,從而防止中間人攻擊,還可以防止DNS和IP欺騙,另外,使用SSH還有加快傳輸速度的好處,原因在於,SSH是可以對數據進行壓縮的。

SSH安裝詳解

SSH是安全外殼協議,而open-ssh則是SSH的開源實現,CentOS通常是默認安裝了open-ssh的。

整個SSH服務是包含SSH服務端(openssh-server)和SSH客戶端(openssh-clients)的,常用的ssh命令就是客戶端一部分。

SSH服務端與SSH客戶端之間的關係:節點A想要控制節點B,則節點A上需要安裝SSH客戶端,而節點B上需要安裝相應的SSH服務端,這樣,節點A才能向節點B發送控制命令和數據。

# 安裝openssh-server
[root@cos1 ~]# yum install -y openssh-server
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.ustc.edu.cn
 * extras: mirrors.ustc.edu.cn
 * updates: mirrors.ustc.edu.cn
Package openssh-server-7.4p1-21.el7.x86_64 already installed and latest version
# 啟動openssh-server
[root@cos1 ~]# systemctl start sshd.service
# 安裝openssh-clients
[root@cos1 ~]# yum install -y openssh-clients
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.ustc.edu.cn
 * extras: mirrors.ustc.edu.cn
 * updates: mirrors.ustc.edu.cn
Package openssh-clients-7.4p1-21.el7.x86_64 already installed and latest version

openssh服務的主配置文件路徑:/etc/ssh/sshd_config

# SSH-Server常見的配置項
# 如果SSH連接提示端口不可用,可以在改文件中查看SSH端口,端口沒問題,就要考慮SSH服務是否正常啟動了
# SSH客戶端與服務端連接的默認端口號
Port 22

默認情況下,SSH服務提供了以下兩個非常有用的功能:

1、類似Telnet遠程連接服務器功能,即SSH服務(安全可靠的遠程登錄會話服務)

# 遠程連接服務
ssh user@hostname/ip

2、類似FTP的sftp-server服務,藉助SSH協議來傳輸數據,提供更安全的SFTP服務(vsftp, proftp).

# 文件拷貝服務
scp files user@hostname:/path/

SSH免密登錄配置

​ 對於分佈式環境組件(例如,Hadoop,HBase等)來說,組件的主/從節點通常需要遠程登錄到集群中其他節點並執行Shell命令,如果不使用SSH,則集群安全就無法保證,使用SSH服務的話,又需要用戶輸入目標服務器的免密,而主/從節點何時訪問其他節點是不可控的,而且,頻繁的需要用戶輸入密碼也是不可行的,這時候,免密登錄就非常重要的。

主機A通過SSH實現免密登錄主機B所需的配置過程大致可以分為以下兩步:

1、主機A在本地通過加密算法生成公鑰和私鑰

通過rsa算法生成公鑰和私鑰key對,默認情況下,身份驗證信息(也就是私鑰)保存在/user/.ssh/id_rsa文件下,而公鑰信息則保存在/user/.ssh/id_rsa.pub文件中。

[root@cos1 .ssh]# pwd
/root/.ssh
[root@cos1 .ssh]# ll
total 8
-rw-------. 1 root root 1679 Jun 22 00:04 id_rsa
-rw-r--r--. 1 root root  391 Jun 22 00:04 id_rsa.pub

2、將主機A的公鑰拷貝到主機B的授權文件(authorized_keys)中

# 通過ssh-copy-id命令將本地服務器公鑰上傳到指定服務器
[root@cos1 ~]# ssh-copy-id root@cos
... ... ... ... ... ... 
Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@cos'"
and check to make sure that only the key(s) you wanted were added.
# 現在,可以通過ssh root@cos訪問主機cos了
[root@cos1 ~]# ssh root@cos
Last login: Thu Jun 18 06:38:25 2020 from 192.168.58.1
[root@cos ~]# 

為了安全起見,通常將保存具體哪些主機可以免密訪問當前主機的授權文件authorized_keys文件設置為及root具有查看和修改的權限,用戶組及其他人無權訪問。

通過ssh-copy-id將主機A的公鑰拷貝到主機B之後,本質上,是在主機B的授權文件中添加了主機A的公鑰。

[root@cos .ssh]# ll
total 8
-rw-------. 1 root root 781 Jun 22 00:16 authorized_keys
-rw-r--r--. 1 root root 176 Jun 18 07:34 known_hosts

注意:

1、免密登錄,是用戶對用戶的,切換為其他用戶時,仍需要輸入密碼

2、免密登錄是單向的,也就是說,主機A將公鑰拷貝到主機B后,主機A可以免密登錄主機A,而主機B登錄主機A時仍然需要輸入密碼

圖解SSH免密登錄原理

通過上面一頓操作,主機A就可以免密登錄到主機A了,感覺有點神奇,那麼,免密登錄到底是怎麼實現的呢?也就是說,SSH協議又到底是什麼樣子呢?且看下圖分解~

上圖便是整個SSH免密登錄的配置及原理圖:

1、ServerA生成公鑰並上傳到ServerB上

2、Step 1: ServerA發送鏈接請求到ServerB

3、Step 2: ServerB在authorized_key中檢索ServerA的公鑰,隨機生成字符串,隨後利用公鑰對字符串進行加密,併發送給ServerA

4、Step 3: ServerA接收到密文後,利用私鑰進行解密,並將內容以明文的形式發送給ServerB

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

2020年年中總結

一、前言

  今年或許是因為疫情的原因吧,感覺時間過的嗖嗖的特別快,不知不覺間2020年已經過去了二分之一,如果把一整年的時間比作我們手機的電量的話,意味着只剩下百分之五十了,不知大家是否心理會有恐慌,在自己手機電量只剩百分之五十的時候,大家是否會找電源來進行充電呢?至少我會,不知大家是否還記得年初定下的目標,是否完成了年度任務的百分之五十?
  六月初的時候,我還在考慮是否要寫一個年中總結,來對我近半年的工作做一個總結,工作中發生的一件事情,讓我覺得還是應該寫這個一篇文章,在每年歲末年初的時候,我都會牽頭來整理我們下一年的年度計劃,然後把年度計劃分解到每個月,在制定月計劃的時候,結合年度計劃及當前實際情況,制定當月的月度計劃,每月統計所有人員月度工作完成情況,以此作為年底考核的一個指標,考核的目的是為了更好的完成工作,既然是為了完成年度目標,那麼在每月初、每季度初大家在制定計劃的時候,就需要看看計劃是否合理、是否合適必要、任務的輕重緩急安排的是否合適,在每月月末、每季度末的時候,對大家完成情況進行統計,對於延期的要了解原因,是工作是安排的不合理,還是其他主管原因造成的,如果延期,應該採取什麼補救措施等,是否要修訂年度工作計劃或者採取措施,在接下來的工作中進行補救;而不是放任不管,到年底統計大家年度工作完成情況,直接進行考核。這個也是我為什麼要寫一下年中總結的原因。

二、2020年上半年總結

  下面我將從工作、學習、生活三個方面對自己上半年做一個總結,我把2020年的年度目標分別歸類到學習與生活中,2020年目標詳見文章2019年總結與2020年展望,首先來說一下工作吧,如果沒有今年的疫情,按照年初的安排,上半年應該是大量的外出調研及廠商交流,4月低5月初就會啟動今年第一批信息化項目建設,由於疫情原因,6月底第一批信息化項目才會啟動進行建設;在接下來的半年中會有兩批信息化項目啟動建設;項目建設壓力還是挺重的;工作中的另外一部分就是自己的日常工作安排,這部分工作比去年有所進步,但是還有較大的進步空間,主要的問題就是工作的先後順序、輕重緩急安排的不合理,對自己的工作應該有側重點,而不是全部平均用力,另外一個就是工作中總結寫的比較少,這部分在下半年需要重點關注了,同時接下來我也會單獨寫一篇文章,對自己上邊工作的心得體會做一個總結,對上半年事務性工作部分進行梳理;其次來說說學習,截止目前,讀書六本,分別是阮一峰的《前方的路》、《來世界的倖存者》、《中台戰略》、《數據中台》、《麥肯錫教我的邏輯思維》、《團隊管理》,整理讀書筆記2篇,分別是前方的路、未來時間的倖存者;按照年度制定的計劃,平均每月最少寫兩篇文章,截止目前,共寫了八篇,還差四篇;項目管理師證書由於疫情原因,上半年的考試取消了,合併到下半年,11月7日、11月8日舉行考試,這個從下半年8月開始準備,一次性考過,不給自己找任何不努力的接口,鯤鵬認證開發工程師由於自己對未來職業的規劃調整,暫時不在考取,軟件開發平台的重構,由於上半年工作安排的太滿及對未來職業的規劃,接下來不在會當做一個主要工作來進行升級,有一個需要改進的地方,就是對手機的依賴太嚴重,短視頻、短新聞佔據了自己大量學習與獨立思考的時間,這個應該就是我們這個年代所面臨的誘惑吧,最後來說一下生活,上半年購買了自己人生的第一輛車,雖說不是豪車,但是是通過自己的努力奮鬥得來的,還是挺欣慰的,周末的時候可以帶着家人到郊區轉轉,確實給生活帶來了諸多便利,年初定計劃的時候說要帶自己的家人出去旅遊1~2次,由於疫情原因,看來要泡湯了,不過可以改為近郊自駕游,在疫情緩解之後再考慮旅遊的地方吧;另外一個受影響比較大的就是自己喜愛的游泳了,不得不暫停,但減肥計劃,依舊不變,今年減肥目標不變,減肥至少15斤,減肥是最考驗一個人自律,我一定會通過減肥,證明自己是一個自律能力強的人,證明自己我可以。

三、結語

  到現在為止,我越來越認為優秀是一種習慣,這也就解釋了為什麼好多優秀的人在這一行業可以做到很好,換到其他行業同樣也可以做的很好,一個人之所以優秀,是因為他有良好的行為習慣、生活習慣,其中包括對時間的安排、解決問題的思路及方法、對待新事物的態度、思考問題的方式方法及相應的學習方法論;同時我也越來越相信性格決定命運,一個人的性格在很大程度上決定了在面對某一件事情的時候會採取的行動,我們在這個社會上都不是孤立存在的,我們一定會與這個社會產生交互,只要產生交互,別人就會依據你的性格、個性、習慣來給你做畫像,做人設,不可避免的會影響到我們未來的發展;
  我們的現狀是過去努力的結果,如果我們對自己的現狀不太滿意,那或許是因為過去你不夠努力或者方向不對吧,要想儘快達到令自己滿意的狀態,應該馬上從力所能級的事情開始做起,就如同我寫的這篇總結一樣,是對我過去半年工作的復盤、反思及總結,我相信人生沒有徒勞的努力,總有一天他會間接或者直接地助你一臂之力。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

一文讀懂:梯度消失(爆炸)及其解決方法

梯度消失問題和梯度爆炸問題,總的來說可以稱為梯度不穩定問題

【要背住的知識】:用ReLU代替Sigmoid,用BN層,用殘差結構解決梯度消失問題。梯度爆炸問題的話,可以用正則化來限制。sigmoid的導數是【0,0.25】.

出現原因

兩者出現原因都是因為鏈式法則。當模型的層數過多的時候,計算梯度的時候就會出現非常多的乘積項。用下面這個例子來理解:

這是每層只有1個神經元的例子,每個神經元的激活函數都是sigmoid,然後我們想要更新b1這個參數。
按照大家都公認的符號來表示:

  • \(w_1\*x_1 + b_1 = z_1\)這就是z的含義;
  • \(\sigma(z_1)=a_1\),這是a的含義。

可以得到這個偏導數:
\(\frac{\partial C}{\partial b_1} = \frac{\partial z_1}{\partial b_1}\frac{\partial a_1}{\partial z_1} \frac{\partial z_2}{\partial a_2}\frac{\partial a_2}{\partial z_2} \frac{\partial z_2}{\partial a_3}\frac{\partial a_3}{\partial z_3} \frac{\partial z_3}{\partial a_4}\frac{\partial a_4}{\partial z_4} \frac{\partial C}{\partial a_4}\)

然後化簡:
\(\frac{\partial C}{\partial b_1}=\sigma'(z_1)w_2\sigma'(z_2)w_3\sigma'(z_3)w_4\sigma'(z_4)\frac{\partial C}{\partial a_4}\)

關鍵在於這個\(\sigma'(z_1)\),sigmoid函數的導數,是在0~0.25這個區間的,這意味着,當網絡層數越深,那麼對於前面幾層的梯度,就會非常的小。下圖是sigmoid函數的導數的函數圖:

因此經常會有這樣的現象:

圖中,分別表示4層隱含層的梯度變化幅度。可以看到,最淺的那個隱含層,梯度更新的速度,是非常小的。【圖中縱軸是指數變化的】。

那麼梯度爆炸也很好理解,就是\(w_j\sigma'(z_j)>1\),這樣就爆炸了。
【注意:如果激活函數是sigmoid,那麼其導數最大也就0.25,而\(w_j\)一般不會大於4的,所以sigmoid函數而言,一般都是梯度消失問題】

【總結】:

  1. 梯度消失和梯度爆炸是指前面幾層的梯度,因為鏈式法則不斷乘小於(大於)1的數,導致梯度非常小(大)的現象;
  2. sigmoid導數最大0.25,一般都是梯度消失問題。

解決方案

更換激活函數

最常見的方案就是更改激活函數,現在神經網絡中,除了最後二分類問題的最後一層會用sigmoid之外,每一層的激活函數一般都是用ReLU。

【ReLU】:如果激活函數的導數是1,那麼就沒有梯度爆炸問題了。

【好處】:可以發現,relu函數的導數在正數部分,是等於1的,因此就可以避免梯度消失的問題。
【不好】:但是負數部分的導數等於0,這樣意味着,只要在鏈式法則中某一個\(z_j\)小於0,那麼這個神經元的梯度就是0,不會更新。

【leakyReLU】:在ReLU的負數部分,增加了一定的斜率:

解決了ReLU中會有死神經元的問題。

【elu】:跟LeakyReLU一樣是為了解決死神經元問題,但是增加的斜率不是固定的:

但是相比leakrelu,計算量更大。

batchnorm層

這個是非常給力的成功,在圖像處理中必用的層了。BN層提出來的本質就是為了解決反向傳播中的梯度問題

在神經網絡中,有這樣的一個問題:Internal Covariate Shift
假設第一層的輸入數據經過第一層的處理之後,得到第二層的輸入數據。這時候,第二層的輸入數據相對第一層的數據分佈,就會發生改變,所以這一個batch,第二層的參數更新是為了擬合第二層的輸入數據的那個分佈。然而到了下一個batch,因為第一層的參數也改變了,所以第二層的輸入數據的分佈相比上一個batch,又不太一樣了。然後第二層的參數更新方向也會發生改變。層數越多,這樣的問題就越明顯。

但是為了保證每一層的分佈不變的話,那麼如果把每一層輸出的數據都歸一化0均值,1方差不就好了?但是這樣就會完全學習不到輸入數據的特徵了。不管什麼數據都是服從標準正太分佈,想想也會覺得有點奇怪。所以BN就是增加了兩個自適應參數,可以通過訓練學習的那種參數。這樣吧每一層的數據都歸一化到\(\beta\)均值,\(\gamma\)標準差的正態分佈上。

【將輸入分佈變成正態分佈,是一種去除數據絕對差異,擴大相對差異的一種行為,所以BN層用在分類上效果的好的。對於Image-to-Image這種任務,數據的絕對差異也是非常重要的,所以BN層可能起不到相應的效果。】

殘差結構

殘差結構,簡單的理解,就是讓深層網絡通過走捷徑,讓網絡不那麼深層。這樣梯度消失的問題就緩解了。

正則化

之前提到的梯度爆炸問題,一般都是因為\(w_j\)過大造成的,那麼用L2正則化就可以解決問題。

喜歡的話請關注我們的微信公眾號~【你好世界煉丹師】。

  • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
  • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
  • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

微信搜索公眾號:你好世界煉丹師。期待您的關注。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

12.DRF-節流

Django rest framework源碼分析(3)—-節流

添加節流

自定義節流的方法

  • 限制60s內只能訪問3次

(1)API文件夾下面新建throttle.py,代碼如下:

# utils/throttle.py

from rest_framework.throttling import BaseThrottle
import time
VISIT_RECORD = {}   #保存訪問記錄

class VisitThrottle(BaseThrottle):
    '''60s內只能訪問3次'''
    def __init__(self):
        self.history = None   #初始化訪問記錄

    def allow_request(self,request,view):
        #獲取用戶ip (get_ident)
        remote_addr = self.get_ident(request)
        ctime = time.time()
        #如果當前IP不在訪問記錄裏面,就添加到記錄
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]     #鍵值對的形式保存
            return True    #True表示可以訪問
        #獲取當前ip的歷史訪問記錄
        history = VISIT_RECORD.get(remote_addr)
        #初始化訪問記錄
        self.history = history

        #如果有歷史訪問記錄,並且最早一次的訪問記錄離當前時間超過60s,就刪除最早的那個訪問記錄,
        #只要為True,就一直循環刪除最早的一次訪問記錄
        while history and history[-1] < ctime - 60:
            history.pop()
        #如果訪問記錄不超過三次,就把當前的訪問記錄插到第一個位置(pop刪除最後一個)
        if len(history) < 3:
            history.insert(0,ctime)
            return True

    def wait(self):
        '''還需要等多久才能訪問'''
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

(2)settings中全局配置節流

#全局
REST_FRAMEWORK = {
    #節流
    "DEFAULT_THROTTLE_CLASSES":['API.utils.throttle.VisitThrottle'],
}

(3)現在訪問auth看看結果:

  • 60s內訪問次數超過三次,會限制訪問
  • 提示剩餘多少時間可以訪問

接着訪問

節流源碼分析

(1)dispatch

def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs
    #對原始request進行加工,豐富了一些功能
    #Request(
    #     request,
    #     parsers=self.get_parsers(),
    #     authenticators=self.get_authenticators(),
    #     negotiator=self.get_content_negotiator(),
    #     parser_context=parser_context
    # )
    #request(原始request,[BasicAuthentications對象,])
    #獲取原生request,request._request
    #獲取認證類的對象,request.authticators
    #1.封裝request
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        #2.認證
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        response = self.handle_exception(exc)

    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

(2)initial

def initial(self, request, *args, **kwargs):
    """
    Runs anything that needs to occur prior to calling the method handler.
    """
    self.format_kwarg = self.get_format_suffix(**kwargs)

    # Perform content negotiation and store the accepted info on the request
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg

    # Determine the API version, if versioning is in use.
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # Ensure that the incoming request is permitted
    #4.實現認證
    self.perform_authentication(request)
    #5.權限判斷
    self.check_permissions(request)
    #6.控制訪問頻率
    self.check_throttles(request)

(3)check_throttles

裏面有個allow_request

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

(4)get_throttles

def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    return [throttle() for throttle in self.throttle_classes]

(5)thtottle_classes

內置節流類

上面是寫的自定義節流,drf內置了很多節流的類,用起來比較方便。

(1)BaseThrottle

  • 自己要寫allow_request和wait方法
  • get_ident就是獲取ip
class BaseThrottle(object):
    """
    Rate throttling of requests.
    """

    def allow_request(self, request, view):
        """
        Return `True` if the request should be allowed, `False` otherwise.
        """
        raise NotImplementedError('.allow_request() must be overridden')

    def get_ident(self, request):
        """
        Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
        if present and number of proxies is > 0. If not use all of
        HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
        """
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()

        return ''.join(xff.split()) if xff else remote_addr

    def wait(self):
        """
        Optionally, return a recommended number of seconds to wait before
        the next request.
        """
        return None

(2)SimpleRateThrottle

class SimpleRateThrottle(BaseThrottle):
    """
    A simple cache implementation, that only requires `.get_cache_key()`
    to be overridden.

    The rate (requests / seconds) is set by a `rate` attribute on the View
    class.  The attribute is a string of the form 'number_of_requests/period'.

    Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')

    Previous request information used for throttling is stored in the cache.
    """
    cache = default_cache
    timer = time.time
    cache_format = 'throttle_%(scope)s_%(ident)s'
    scope = None   #這個值自定義,寫什麼都可以
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

    def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()
        self.num_requests, self.duration = self.parse_rate(self.rate)

    def get_cache_key(self, request, view):
        """
        Should return a unique cache-key which can be used for throttling.
        Must be overridden.

        May return `None` if the request should not be throttled.
        """
        raise NotImplementedError('.get_cache_key() must be overridden')

    def get_rate(self):
        """
        Determine the string representation of the allowed request rate.
        """
        if not getattr(self, 'scope', None):
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)

    def parse_rate(self, rate):
        """
        Given the request rate string, return a two tuple of:
        <allowed number of requests>, <period of time in seconds>
        """
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)

    def allow_request(self, request, view):
        """
        Implement the check to see if the request should be throttled.

        On success calls `throttle_success`.
        On failure calls `throttle_failure`.
        """
        if self.rate is None:
            return True

        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()

    def throttle_success(self):
        """
        Inserts the current request's timestamp along with the key
        into the cache.
        """
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True

    def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False

    def wait(self):
        """
        Returns the recommended next request time in seconds.
        """
        if self.history:
            remaining_duration = self.duration - (self.now - self.history[-1])
        else:
            remaining_duration = self.duration

        available_requests = self.num_requests - len(self.history) + 1
        if available_requests <= 0:
            return None

        return remaining_duration / float(available_requests)

我們可以通過繼承SimpleRateThrottle類,來實現節流,會更加的簡單,因為SimpleRateThrottle裏面都幫我們寫好了

(1)throttle.py

from rest_framework.throttling import SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    '''匿名用戶60s只能訪問三次(根據ip)'''
    scope = 'NBA'   #這裏面的值,自己隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        #通過ip限制節流
        return self.get_ident(request)

class UserThrottle(SimpleRateThrottle):
    '''登錄用戶60s可以訪問10次'''
    scope = 'NBAUser'    #這裏面的值,自己隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        return request.user.username

(2)settings.py

#全局
REST_FRAMEWORK = {
    #節流
    "DEFAULT_THROTTLE_CLASSES":['API.utils.throttle.UserThrottle'],   #全局配置,登錄用戶節流限制(10/m)
    "DEFAULT_THROTTLE_RATES":{
        'NBA':'3/m',         #沒登錄用戶3/m,NBA就是scope定義的值
        'NBAUser':'10/m',    #登錄用戶10/m,NBAUser就是scope定義的值
    }
}

(3)views.py

局部配置方法

class AuthView(APIView):
    .
    .    
    .
    # 默認的節流是登錄用戶(10/m),AuthView不需要登錄,這裏用匿名用戶的節流(3/m)
    throttle_classes = [VisitThrottle,]   .    .
# views.py
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from API import models
from rest_framework.request import Request
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from API.utils.permission import SVIPPremission,MyPremission
from API.utils.throttle import  VisitThrottle

ORDER_DICT = {
    1:{
        'name':'apple',
        'price':15
    },
    2:{
        'name':'dog',
        'price':100
    }
}

def md5(user):
    import hashlib
    import time
    #當前時間,相當於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user,encoding='utf-8'))
    m.update(bytes(ctime,encoding='utf-8'))
    return m.hexdigest()


class AuthView(APIView):
    '''用於用戶登錄驗證'''

    authentication_classes = []      #裏面為空,代表不需要認證
    permission_classes = []          #不裏面為空,代表不需要權限
    # 默認的節流是登錄用戶(10/m),AuthView不需要登錄,這裏用匿名用戶的節流(3/m)
    throttle_classes = [VisitThrottle,]

    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            #為用戶創建token
            token = md5(user)
            #存在就更新,不存在就創建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)


class OrderView(APIView):
    '''
    訂單相關業務(只有SVIP用戶才能看)
    '''

    def get(self,request,*args,**kwargs):
        self.dispatch
        #request.user
        #request.auth
        ret = {'code':1000,'msg':None,'data':None}
        try:
            ret['data'] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)


class UserInfoView(APIView):
    '''
       訂單相關業務(普通用戶和VIP用戶可以看)
       '''
    permission_classes = [MyPremission,]    #不用全局的權限配置的話,這裏就要寫自己的局部權限
    def get(self,request,*args,**kwargs):

        print(request.user)
        return HttpResponse('用戶信息')

說明:

  • API.utils.throttle.UserThrottle 這個是全局配置(根據ip限制,10/m)
  • DEFAULT_THROTTLE_RATES —>>>設置訪問頻率的
  • throttle_classes = [VisitThrottle,] —>>>局部配置(不適用settings裏面默認的全局配置)

總結

基本使用

  • 創建類,繼承BaseThrottle, 實現:allow_request ,wait
  • 創建類,繼承SimpleRateThrottle, 實現: get_cache_key, scope=’NBA’ (配置文件中的key)

全局

   #節流
    "DEFAULT_THROTTLE_CLASSES":['API.utils.throttle.UserThrottle'],   #全局配置,登錄用戶節流限制(10/m)
    "DEFAULT_THROTTLE_RATES":{
        'NBA':'3/m',         #沒登錄用戶3/m,NBA就是scope定義的值
        'NBAUser':'10/m',    #登錄用戶10/m,NBAUser就是scope定義的值
    }
}

局部

throttle_classes = [VisitThrottle,]

所有代碼

認證、權限和節流

# MyProject/urls.py

from django.contrib import admin
from django.urls import path
from API.views import AuthView,OrderView,UserInfoView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/auth/',AuthView.as_view()),
    path('api/v1/order/',OrderView.as_view()),
    path('api/v1/info/',UserInfoView.as_view()),
]
#全局 settings.py
REST_FRAMEWORK = {
    #認證
    "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',],
    #權限
    "DEFAULT_PERMISSION_CLASSES":['API.utils.permission.SVIPPermission'],
    #節流
    "DEFAULT_THROTTLE_CLASSES":['API.utils.throttle.UserThrottle'],   #全局配置,登錄用戶節流限制(10/m)
    "DEFAULT_THROTTLE_RATES":{
        'NBA':'3/m',         #沒登錄用戶3/m,NBA就是scope定義的值
        'NBAUser':'10/m',    #登錄用戶10/m,NBAUser就是scope定義的值
    }
}
# API/models.py


from django.db import models

class UserInfo(models.Model):
    USER_TYPE = (
        (1,'普通用戶'),
        (2,'VIP'),
        (3,'SVIP')
    )

    user_type = models.IntegerField(choices=USER_TYPE)
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

class UserToken(models.Model):
    user = models.OneToOneField(UserInfo,on_delete=models.CASCADE)
    token = models.CharField(max_length=64)
# API/views.py

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from API import models
from rest_framework.request import Request
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from API.utils.permission import SVIPPermission,MyPermission
from API.utils.throttle import  VisitThrottle

ORDER_DICT = {
    1:{
        'name':'apple',
        'price':15
    },
    2:{
        'name':'dog',
        'price':100
    }
}

def md5(user):
    import hashlib
    import time
    #當前時間,相當於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user,encoding='utf-8'))
    m.update(bytes(ctime,encoding='utf-8'))
    return m.hexdigest()


class AuthView(APIView):
    '''用於用戶登錄驗證'''

    authentication_classes = []      #裏面為空,代表不需要認證
    permission_classes = []          #不裏面為空,代表不需要權限
    # 默認的節流是登錄用戶(10/m),AuthView不需要登錄,這裏用匿名用戶的節流(3/m)
    throttle_classes = [VisitThrottle,]

    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            #為用戶創建token
            token = md5(user)
            #存在就更新,不存在就創建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)


class OrderView(APIView):
    '''
    訂單相關業務(只有SVIP用戶才能看)
    '''

    def get(self,request,*args,**kwargs):
        self.dispatch
        #request.user
        #request.auth
        ret = {'code':1000,'msg':None,'data':None}
        try:
            ret['data'] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)


class UserInfoView(APIView):
    '''
       訂單相關業務(普通用戶和VIP用戶可以看)
       '''
    permission_classes = [MyPermission,]    #不用全局的權限配置的話,這裏就要寫自己的局部權限
    def get(self,request,*args,**kwargs):

        print(request.user)
        return HttpResponse('用戶信息')
# API/utils/auth/py

from rest_framework import exceptions
from API import models
from rest_framework.authentication import BaseAuthentication


class Authentication(BaseAuthentication):
    '''用於用戶登錄驗證'''
    def authenticate(self,request):
        token = request._request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed('用戶認證失敗')
        #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
        return (token_obj.user,token_obj)

    def authenticate_header(self, request):
        pass
# utils/permission.py

from rest_framework.permissions import BasePermission

class SVIPPermission(BasePermission):
    message = "必須是SVIP才能訪問"
    def has_permission(self,request,view):
        if request.user.user_type != 3:
            return False
        return True


class MyPermission(BasePermission):
    def has_permission(self,request,view):
        if request.user.user_type == 3:
            return False
        return True
# utils/throttle.py
#
# from rest_framework.throttling import BaseThrottle
# import time
# VISIT_RECORD = {}   #保存訪問記錄
#
# class VisitThrottle(BaseThrottle):
#     '''60s內只能訪問3次'''
#     def __init__(self):
#         self.history = None   #初始化訪問記錄
#
#     def allow_request(self,request,view):
#         #獲取用戶ip (get_ident)
#         remote_addr = self.get_ident(request)
#         ctime = time.time()
#         #如果當前IP不在訪問記錄裏面,就添加到記錄
#         if remote_addr not in VISIT_RECORD:
#             VISIT_RECORD[remote_addr] = [ctime,]     #鍵值對的形式保存
#             return True    #True表示可以訪問
#         #獲取當前ip的歷史訪問記錄
#         history = VISIT_RECORD.get(remote_addr)
#         #初始化訪問記錄
#         self.history = history
#
#         #如果有歷史訪問記錄,並且最早一次的訪問記錄離當前時間超過60s,就刪除最早的那個訪問記錄,
#         #只要為True,就一直循環刪除最早的一次訪問記錄
#         while history and history[-1] < ctime - 60:
#             history.pop()
#         #如果訪問記錄不超過三次,就把當前的訪問記錄插到第一個位置(pop刪除最後一個)
#         if len(history) < 3:
#             history.insert(0,ctime)
#             return True
#
#     def wait(self):
#         '''還需要等多久才能訪問'''
#         ctime = time.time()
#         return 60 - (ctime - self.history[-1])

from rest_framework.throttling import SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    '''匿名用戶60s只能訪問三次(根據ip)'''
    scope = 'NBA'   #這裏面的值,自己隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        #通過ip限制節流
        return self.get_ident(request)

class UserThrottle(SimpleRateThrottle):
    '''登錄用戶60s可以訪問10次'''
    scope = 'NBAUser'    #這裏面的值,自己隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        return request.user.username

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

138 張圖帶你 MySQL 入門

SQL 基礎使用

MySQL 是一種關係型數據庫,說到關係,那麼就離不開表與表之間的關係,而最能體現這種關係的其實就是我們接下來需要介紹的主角 SQL,SQL 的全稱是 Structure Query Language ,結構化的查詢語言,它是一種針對錶關聯關係所設計的一門語言,也就是說,學好 MySQL,SQL 是基礎和重中之重。SQL 不只是 MySQL 中特有的一門語言,大多數關係型數據庫都支持這門語言。

下面我們就來一起學習一下這門非常重要的語言。

查詢語言分類

在了解 SQL 之前我們需要知道下面這幾個概念

  • 數據定義語言: 簡稱DDL (Data Definition Language),用來定義數據庫對象:數據庫、表、列等;
  • 數據操作語言: 簡稱DML (Data Manipulation Language),用來對數據庫中表的記錄進行更新。關鍵字: insert、update、delete等
  • 數據控制語言: 簡稱DCL(Data Control Language),用來定義數據庫訪問權限和安全級別,創建用戶等。關鍵字: grant等
  • 數據查詢語言: 簡稱DQL(Data Query Language),用來查詢數據庫中表的記錄,關鍵字: select from where等

DDL 語句

創建數據庫

下面就開始我們的 SQL 語句學習之旅,首先你需要啟動 MySQL 服務,我這裡是 mac 電腦,所以我直接可以啟動

然後我們使用命令行的方式連接數據庫,打開 iterm,輸入下面

MacBook:~ mr.l$ mysql -uroot -p

就可以連接到數據庫了

在上面命令中,mysql 代表客戶端命令,- u 表示後面需要連接的用戶,-p 表示需要輸入此用戶的密碼。在你輸入用戶名和密碼后,如果成功登陸,會显示一個歡迎界面(如上圖 )和 mysql> 提示符。

歡迎界面主要描述了這些東西

  • 每一行的結束符,這裏用 ; 或者 \g 來表示每一行的結束
  • Your MySQL connection id is 4,這個記錄了 MySQL 服務到目前為止的連接數,每個新鏈接都會自動增加 1 ,上面显示的連接次數是 4 ,說明我們只連接了四次
  • 然後下面是 MySQL 的版本,我們使用的是 5.7
  • 通過 help 或者 \h 命令來显示幫助內容,通過 \c 命令來清除命令行 buffer。

然後需要做的事情是什麼?我們最終想要學習 SQL 語句,SQL 語句肯定是要查詢數據,通過數據來體現出來表的關聯關係,所以我們需要數據,那麼數據存在哪裡呢?數據存儲的位置被稱為 表(table),表存儲的位置被稱為 數據庫(database),所以我們需要先建數據庫後面再建表然後插入數據,再進行查詢。

所以我們首先要做的就是創建數據庫,創建數據庫可以直接使用指令

CREATE DATABASE dbname;

進行創建,比如我們創建數據庫 cxuandb

create database cxuandb;

注意最後的 ; 結束語法一定不要丟掉,否則 MySQL 會認為你的命令沒有輸出完,敲 enter 後會直接換行輸出

創建完成后,會提示 Query OK, 1 row affected,這段語句什麼意思呢? Query OK 表示的就是查詢完成,為什麼會显示這個?因為所有的 DDL 和 DML 操作執行完成后都會提示這個, 也可以理解為操作成功。後面跟着的 **1 row affected ** 表示的是影響的行數,() 內显示的是你執行這條命令所耗費的時間,也就是 0.03 秒。

上圖我們成功創建了一個 cxuandb 的數據庫,此時我們還想創建一個數據庫,我們再執行相同的指令,結果提示

提示我們不能再創建數據庫了,數據庫已經存在。這時候我就有疑問了,我怎麼知道都有哪些數據庫呢?別我再想創建一個數據庫又告訴我已經存在,這時候可以使用 show databases 命令來查看你的 MySQL 已有的數據庫

show databases;

執行完成后的結果如下

因為數據庫我之前已經使用過,這裏就需要解釋一下,除了剛剛新創建成功的 cxuandb 外,informationn_schemaperformannce_schemasys 都是系統自帶的數據庫,是安裝 MySQL 默認創建的數據庫。它們各自表示

  • informationn_schema: 主要存儲一些數據庫對象信息,比如用戶表信息、權限信息、分區信息等
  • performannce_schema: MySQL 5.5 之後新增加的數據庫,主要用於收集數據庫服務器性能參數。
  • sys: MySQL 5.7 提供的數據庫,sys 數據庫裡面包含了一系列的存儲過程、自定義函數以及視圖來幫助我們快速的了解系統的元數據信息。

其他所有的數據庫都是作者自己創建的,可以忽略他們。

在創建完數據庫之後,可以用如下命令選擇要操作的數據庫

use cxuandb

這樣就成功切換為了 cxuandb 數據庫,我們可以在此數據庫下進行建表、查看基本信息等操作。

比如想要看康康我們新建的數據庫裏面有沒有其他表

show tables;

果然,我們新建的數據庫下面沒有任何錶,但是現在,我們還不進行建表操作,我們還是先來認識一下數據庫層面的命令,也就是其他 DDL 指令

刪除數據庫

如果一個數據庫我們不想要了,那麼該怎麼辦呢?直接刪掉數據庫不就好了嗎?刪表語句是

drop database dbname;

比如 cxuandb 我們不想要他了,可以通過使用

drop database cxuandb;

進行刪除,這裏我們就不進行演示了,因為 cxuandb 我們後面還會使用。

但是這裏注意一點,你刪除數據庫成功後會出現 0 rows affected,這個可以不用理會,因為在 MySQL 中,drop 語句操作的結果都是 0 rows affected

創建表

下面我們就可以對錶進行操作了,我們剛剛 show tables 發現還沒有任何錶,所以我們現在進行建表語句

CREATE TABLE 表名稱
(
列名稱1 數據類型 約束,
列名稱2 數據類型 約束,
列名稱3 數據類型 約束,
....
)

這樣就很清楚了吧,列名稱就是列的名字,緊跟着列名後面就是數據類型,然後是約束,為什麼要這麼設計?舉個例子你就清楚了,比如 cxuan 剛被生出來就被打印上了標籤

比如我們創建一個表,裏面有 5 個字段,姓名(name)、性別(sex)、年齡(age)、何時雇傭(hiredate)、薪資待遇(wage),建表語句如下

create table job(name varchar(20), sex varchar(2), age int(2), hiredate date, wage decimal(10,2));

事實證明這條建表語句還是沒問題的,建表完成后可以使用 DESC tablename 查看錶的基本信息

DESC 命令會查看錶的定義,但是輸出的信息還不夠全面,所以,如果想要查看更全的信息,還要通過查看錶的創建語句的 SQL 來得到

show create table job \G;

可以看到,除了看到表定義之外,還看到了表的 engine(存儲引擎) 為 InnoDB 存儲引擎,\G 使得記錄能夠豎著排列,如果不用 \G 的話,效果如下

刪除表

表的刪除語句有兩種,一種是 drop 語句,SQL 語句如下

drop table job

一種是 truncate 語句,SQL 語句如下

truncate table job

這兩者的區別簡單理解就是 drop 語句刪除表之後,可以通過日誌進行回復,而 truncate 刪除表之後永遠恢復不了,所以,一般不使用 truncate 進行表的刪除。‘

修改表

對於已經創建好的表,尤其是有大量數據的表,如果需要對錶做結構上的改變,可以將表刪除然後重新創建表,但是這種效率會產生一些額外的工作,數據會重新加載近來,如果此時有服務正在訪問的話,也會影響服務讀取表中數據,所以此時,我們需要表的修改語句來對已經創建好的表的定義進行修改。

修改表結構一般使用 alter table 語句,下面是常用的命令

ALTER TABLE tb MODIFY [COLUMN] column_definition [FIRST | AFTER col_name];

比如我們想要將 job 表中的 name 由 varchar(20) 改為 varchar(25),可以使用如下語句

alter table job modify name varchar(25);

也可以對錶結構進行修改,比如增加一個字段

alter table job add home varchar(30);

將新添加的表的字段進行刪除

alter table job drop column home;

可以對表中字段的名稱進行修改,比如吧 wage 改為 salary

alter table job change wage salary decimal(10,2);

修改字段的排列順序,我們前面介紹過修改語法涉及到一個順序問題,都有一個可選項 **first | after ** column_name,這個選項可以用來修改表中字段的位置,默認 ADD 是在添加為表中最後一個字段,而 CHANGE/MODIFY 不會改變字段位置。比如

alter table job add birthday after hiredate;

可以對錶名進行修改,例如將 job 表改為 worker

alter table job rename worker;

DML 語句

有的地方把 DML 語句(增刪改)和 DQL 語句(查詢)統稱為 DML 語句,有的地方分開,我們目前使用分開稱呼的方式

插入

表創建好之後,我們就可以向表裡插入數據了,插入記錄的基本語法如下

INSERT INTO tablename (field1,field2) VALUES(value1,value2);

例如,向中插入以下記錄

insert into job(name,sex,age,hiredate,birthday,salary) values("cxuan","男",24,"2020-04-27","1995-08-22",8000);

也可以不用指定要插入的字段,直接插入數據即可

insert into job values("cxuan02","男",25,"2020-06-01","1995-04-23",12000);

這裏就有一個問題,如果插入的順序不一致的話會怎麼樣呢?

對於含可空字段、非空但是含有默認值的字段、自增字段可以不用在 insert 后的字段列表出現,values 後面只需要寫對應字段名稱的 value 即可,沒有寫的字段可以自動的設置為 NULL、默認值或者自增的下一個值,這樣可以縮短要插入 SQL 語句的長度和複雜性。

比如我們設置一下 hiredate、age 可以為 null,來試一下

insert into job(name,sex,birthday,salary) values("cxuan03","男","1992-08-23",15000);

我們看一下實際插入的數據

我們可以看到有一行兩個字段显示 NULL。在 MySQL 中,insert 語句還有一個很好的特性,就是一次可以插入多條記錄

INSERT INTO tablename (field1,field2) VALUES
(value1,value2),
(value1,value2),
(value1,value2),
...;

可以看出,每條記錄之間都用逗號進行分割,這個特性可以使得 MySQL 在插入大量記錄時,節省很多的網絡開銷,大大提高插入效率。

更新記錄

對於表中已經存在的數據,可以通過 update 命令對其進行修改,語法如下

UPDATE tablename SET field1 = value1, field2 = value2 ;

例如,將 job 表中的 cxuan03 中 age 的 NULL 改為 26,SQL 語句如下

update job set age = 26 where name = 'cxuan03';

SQL 語句中出現了一個 where 條件,我們會在後面說到 where 條件,這裏簡單理解一下它的概念就是根據哪條記錄進行更新,如果不寫 where 的話,會對整個表進行更新

刪除記錄

如果記錄不再需要,可以使用 delete 命令進行刪除

DELETE FROM tablename [WHERE CONDITION]

例如,在 job 中刪除名字是 cxuan03 的記錄

delete from job where name = 'cxuan03';

在 MySQL 中,刪除語句也可以不指定 where 條件,直接使用

delete from job

這種刪除方式相當於是清楚表的操作,表中所有的記錄都會被清除。

DQL 語句

下面我們一起來認識一下 DQL 語句,數據被插入到 MySQL 中,就可以使用 SELECT 命令進行查詢,來得到我們想要的結果。

SELECT 查詢語句可以說是最複雜的語句了,這裏我們只介紹一下基本語法

一種最簡單的方式就是從某個表中查詢出所有的字段和數據,簡單粗暴,直接使用 SELECT *

SELECT * FROM tablename;

例如我們將 job 表中的所有數據查出來

select * from job;

其中 * 是查詢出所有的數據,當然,你也可以查詢出指定的數據項

select name,sex,age,hiredate,birthday,salary from job;

上面這條 SQL 語句和 select * from job 表是等價的,但是這種直接查詢指定字段的 SQL 語句效率要高。

上面我們介紹了基本的 SQL 查詢語句,但是實際的使用場景會會比簡單查詢複雜太多,一般都會使用各種 SQL 的函數和查詢條件等,下面我們就來一起認識一下。

去重

使用非常廣泛的場景之一就是 去重,去重可以使用 distinct 關鍵字來實現

為了演示效果,我們先向數據庫中插入批量數據,插入完成后的表結構如下

下面我們使用 distinct 來對 age 去重來看一下效果

你會發現只有兩個不同的值,其他和 25 重複的值被過濾掉了,所以我們使用 distinct 來進行去重

條件查詢

我們之前的所有例子都是查詢全部的記錄,如果我們只想查詢指定的記錄呢?這裏就會用到 where 條件查詢語句,條件查詢可以對指定的字段進行查詢,比如我們想查詢所有年齡為 24 的記錄,如下

select * from job where age = 24;

where 條件語句後面會跟一個判斷的運算符 =,除了 = 號比較外,還可以使用 >、<、>=、<=、!= 等比較運算符;例如

select * from job where age >= 24;

就會從 job 表中查詢出 age 年齡大於或等於 24 的記錄

除此之外,在 where 條件查詢中還可以有多個並列的查詢條件,比如我們可以查詢年齡大於等於 24,並且薪資大雨 8000 的記錄

select * from job where age >= 24 and salary > 8000;

多個條件之間還可以使用 or、and 等邏輯運算符進行多條件聯合查詢,運算符會在以後章節中詳細講解。

排序

我們會經常有這樣的需求,按照某個字段進行排序,這就用到了數據庫的排序功能,使用關鍵字 order by 來實現,語法如下

SELECT * FROM tablename [WHERE CONDITION] [ORDER BY field1 [DESC|ASC] , field2 [DESC|ASC],……fieldn [DESC|ASC]]

其中 DESC 和 ASC 就是順序排序的關鍵字,DESC 會按照字段進行降序排列,ASC 會按照字段進行升序排列,默認會使用升序排列,也就是說,你不寫 order by 具體的排序的話,默認會使用升序排列。order by 後面可以跟多個排序字段,並且每個排序字段可以有不同的排序順序。

為了演示功能,我們先把表中的 salary 工資列進行修改,修改完成后的表記錄如下

下面我們按照工資進行排序,SQL 語句如下

select * from job order by salary desc;

語句執行完成后的結果如下

這是對一個字段進行排序的結果,也可以對多個字段進行排序,但是需要注意一點

根據 order by 後面聲名的順序進行排序,如果有三個排序字段 A、B、C 的話,如果 A 字段排序字段的值一樣,則會根據第二個字段進行排序,以此類推。

如果只有一個排序字段,那麼這些字段相同的記錄將會無序排列。

限制

對於排序后的字段,或者不排序的字段,如果只希望显示一部分的話,就會使用 LIMIT 關鍵字來實現,比如我們只想取前三條記錄

select * from job limit 3;

或者我們對排序后的字段取前三條記錄

select * from job order by salary limit 3;

上面這種 limit 是從表記錄的第 0 條開始取,如果從指定記錄開始取,比如從第二條開始取,取三條記錄,SQL 如下

select * from job order by salary desc limit 2,3;

limit 一般經常和 order by 語法一起實現分頁查詢。

注意:limit 是 MySQL 擴展 SQL92 之後的語法,在其他數據庫比如 Oracle 上就不通用,我犯過一個白痴的行為就是在 Oracle 中使用 limit 查詢語句。。。

聚合

下面我們來看一下對記錄進行匯總的操作,這類操作主要有

  • 匯總函數,比如 sum 求和、count 統計數量、max 最大值、min 最小值等
  • group by,關鍵字表示對分類聚合的字段進行分組,比如按照部門統計員工的數量,那麼 group by 後面就應該跟上部門
  • with 是可選的語法,它表示對匯總之後的記錄進行再次匯總
  • having 關鍵字表示對分類后的結果再進行條件的過濾。

看起來 where 和 having 意思差不多,不過它們用法不一樣,where 是使用在統計之前,對統計前的記錄進行過濾,having 是用在統計之後,是對聚合之後的結果進行過濾。也就是說 where 永遠用在 having 之前,我們應該先對篩選的記錄進行過濾,然後再對分組的記錄進行過濾。

可以對 job 表中員工薪水進行統計,選出總共的薪水、最大薪水、最小薪水

select sum(salary) from job;

select max(salary),min(salary) from job;

比如我們要統計 job 表中人員的數量

select count(1) from job;

統計完成后的結果如下

我們可以按照 job 表中的年齡來進行對應的統計

select age,count(1) from job group by age;

既要統計各年齡段的人數,又要統計總人數

select age,count(1) from job group by age with rollup;

在此基礎上進行分組,統計數量大於 1 的記錄

select age,count(1) from job group by age with rollup having count(1) > 1;

表連接

表連接一直是筆者比較痛苦的地方,曾經因為一個表連接掛了面試,現在來認真擼一遍。

表連接一般體現在表之間的關係上。當需要同時显示多個表中的字段時,就可以用表連接來實現。

為了演示表連接的功能,我們為 job 表加一個 type 字段表示工作類型,增加一個 job_type 表表示具體的工作種類,如下所示

下面開始我們的演示

查詢出 job 表中的 type 和 job_type 表中的 type 匹配的姓名和工作類型

select job.name,job_type.name from job,job_type where job.type = job_type.type;

上面這種連接使用的是內連接,除此之外,還有外連接。那麼它們之間的區別是啥呢?

內連接:選出兩張表中互相匹配的記錄;

外連接:不僅選出匹配的記錄,也會選出不匹配的記錄;

外連接分為兩種

  • 左外連接:篩選出包含左表的記錄並且右表沒有和它匹配的記錄
  • 右外連接:篩選出包含右表的記錄甚至左表沒有和它匹配的記錄

為了演示效果我們在 job 表和 job_type 表中分別添加記錄,添加完成后的兩表如下

下面我們進行左外連接查詢:查詢出 job 表中的 type 和 job_type 表中的 type 匹配的姓名和工作類型

select job.name,job_type.name from job left join job_type on job.type = job_type.type;

查詢出來的結果如下

可以看出 cxuan06 也被查詢出來了,而 cxuan06 他沒有具體的工作類型。

使用右外連接查詢

select job.name,job_type.name from job right join job_type on job.type = job_type.type;

可以看出,job 表中並沒有 waiter 和 manager 的角色,但是也被查詢出來了。

子查詢

有一些情況,我們需要的查詢條件是另一個 SQL 語句的查詢結果,這種查詢方式就是子查詢,子查詢有一些關鍵字比如 in、not in、=、!=、exists、not exists 等,例如我們可以通過子查詢查詢出每個人的工作類型

select job.* from job where type in (select type from job_type);

如果自查詢數量唯一的話,還可以用 = 來替換 in

select * from job where type = (select type from job_type);

意思是自查詢不唯一,我們使用 limit 限制一下返回的記錄數

select * from job where type = (select type from job_type limit 1,1);

在某些情況下,子查詢可以轉換為表連接

聯合查詢

我們還經常會遇到這樣的場景,將兩個表的數據單獨查詢出來之後,將結果合併到一起進行显示,這個時候就需要 UNION 和 UNION ALL 這兩個關鍵字來實現這樣的功能,UNION 和 UNION ALL 的主要區別是 UNION ALL 是把結果集直接合併在一起,而 UNION 是將 UNION ALL 后的結果進行一次 DISTINCT 去除掉重複數據。

比如

select type from job union all select type from job_type;

它的結果如下

上述結果是查詢 job 表中的 type 字段和 job_type 表中的 type 字段,並把它們進行匯總,可以看出 UNION ALL 只是把所有的結果都列出來了

使用 UNION 的 SQL 語句如下

select type from job union select type from job_type;

可以看出 UNION 是對 UNION ALL 使用了 distinct 去重處理。

DCL 語句

DCL 語句主要是管理數據庫權限的時候使用,這類操作一般是 DBA 使用的,開發人員不會使用 DCL 語句。

關於幫助文檔的使用

我們一般使用 MySQL 遇到不會的或者有疑問的東西經常要去查閱網上資料,甚至可能需要去查 MySQL 官發文檔,這樣會耗費大量的時間和精力。

下面教你一下在 MySQL 命令行就能直接查詢資料的語句

按照層次查詢

可以使用 ? contents 來查詢所有可供查詢的分類,如下所示

? contents;

我們輸入

? Account Management

可以查詢具體關於權限管理的命令

比如我們想了解一下數據類型

? Data Types

然後我們想了解一下 VARCHAR 的基本定義,可以直接使用

? VARCHAR

可以看到有關於 VARCHAR 數據類型的詳細信息,然後在最下面還有 MySQL 的官方文檔,方便我們快速查閱。

快速查閱

在實際應用過程中,如果要快速查詢某個語法時,可以使用關鍵字進行快速查詢,比如我們使用

? show

能夠快速列出一些命令

比如我們想要查閱 database 的信息,使用

SHOW CREATE DATABASE cxuandb;

MySQL 數據類型

MySQL 提供很多種數據類型來對不同的常量、變量進行區分,MySQL 中的數據類型主要是 數值類型、日期和時間類型、字符串類型 選擇合適的數據類型進行數據的存儲非常重要,在實際開發過程中,選擇合適的數據類型也能夠提高 SQL 性能,所以有必要認識一下這些數據類型。

數值類型

MySQL 支持所有標準的 SQL 數據類型,這些數據類型包括嚴格數據類型的嚴格數值類型,這些數據類型有

  • INTEGER
  • SMALLINT
  • DECIMAL
  • NUMERIC。

近似數值數據類型 並不用嚴格按照指定的數據類型進行存儲,這些有

  • FLOAT
  • REAL
  • DOUBLE PRECISION

還有經過擴展之後的數據類型,它們是

  • TINYINT
  • MEDIUMINT
  • BIGINT
  • BIT

其中 INT 是 INTEGER 的縮寫,DEC 是 DECIMAL 的縮寫。

下面是所有數據類型的匯總

整數

在整數類型中,按照取值範圍和存儲方式的不同,分為

![image-20200613091331344](/Users/mr.l/Library/Application Support/typora-user-images/image-20200613091331344.png)

  • TINYINT ,佔用 1 字節
  • SMALLINT,佔用 2 字節
  • MEDIUMINT,佔用 3 字節
  • INT、INTEGER,佔用 4 字節
  • BIGINT,佔用 8 字節

五個數據類型,如果超出類型範圍的操作,會發生錯誤提示,所以選擇合適的數據類型非常重要。

還記得我們上面的建表語句么

我們一般會在 SQL 語句的數據類型後面加上指定長度來表示數據類型許可的範圍,例如

int(7)

表示 int 類型的數據最大長度為 7,如果填充不滿的話會自動填滿,如果不指定 int 數據類型的長度的話,默認是 int(11)

我們創建一張表來演示一下

create table test1(aId int, bId int(5));

/* 然後我們查看一下錶結構 */
desc test1;

整數類型一般配合 zerofill 來使用,顧名思義,就是用 0 進行填充,也就是数字位數不夠的空間使用 0 進行填充。

分別修改 test1 表中的兩個字段

alter table test1 modify aId int zerofill;

alter table test1 modify bId int(5) zerofill;

然後插入兩條數據,執行查詢操作

如上圖所示,使用zerofill 可以在数字前面使用 0 來進行填充,那麼如果寬度超過指定長度後會如何显示?我們來試驗一下,向 aId 和 bId 分別插入超過字符限制的数字

會發現 aId 已經超出了指定範圍,那麼我們對 aId 插入一個在其允許範圍之內的數據

會發現,aId 已經插進去了,bId 也插進去了,為什麼 bId 显示的是 int(5) 卻能夠插入 7 位長度的數值呢?

所有的整數都有一個可選屬性 UNSIGNED(無符號),如果需要在字段裏面保存非負數或者是需要較大上限值時,可以使用此選項,它的取值範圍是正常值的下限取 0 ,上限取原值的 2 倍。如果一個列為 zerofill ,會自動為該列添加 UNSIGNED 屬性。

除此之外,整數還有一個類型就是 AUTO_INCREMENT,在需要產生唯一標識符或者順序值時,可利用此屬性,這個屬性只用於整数字符。一個表中最多只有一個 AUTO_INCREMENT 屬性,一般用於自增主鍵,而且 NOT NULL,並且是 PRIMARY KEY UNIQUE 的,主鍵必須保證唯一性而且不為空。

小數

小數說的是啥?它其實有兩種類型;一種是浮點數類型,一種是定點數類型;

浮點數有兩種

  • 單精度浮點型 – float 型
  • 雙精度浮點型 – double 型

定點數只有一種 decimal。定點數在 MySQL 內部中以字符串的形式存在,比浮點數更為準確,適合用來表示精度特別高的數據。

浮點數和定點數都可以使用 (M,D) 的方式來表示,M 表示的就是 整數位 + 小數位 的数字,D 表示位於 . 後面的小數。M 也被稱為精度 ,D 被稱為標度。

下面通過示例來演示一下

首先建立一個 test2

CREATE TABLE test2 (aId float(6,2) default NULL, bId double(6,2) default NULL,cId decimal(6,2) default NULL)

然後向表中插入幾條數據

insert into test2 values(1234.12,1234.12,1234.12);

這個時候显示的數據就是

然後再向表中插入一些約束之外的數據

insert into test2 values(1234.123,1234.123,1234.123);

發現插入完成后還显示的是 1234.12,小數位第三位的值被捨去了。

現在我們把 test2 表中的精度全部去掉,再次插入

alter table test2 modify aId float;

alter table test2 modify bId double;

alter table test2 modify cId decimal;

先查詢一下,發現 cId 捨去了小數位。

然後再次插入 1.23,SQL 語句如下

insert into test2 values(1.23,1.23,1.23);

結果如下

這個時候可以驗證

  • 浮點數如果不寫精度和標度,會按照實際的精度值進行显示
  • 定點數如果不寫精度和標度,會按照 decimal(10,0) 來進行操作,如果數據超過了精度和標題,MySQL 會報錯

位類型

對於位類型,用於存放字段值,BIT(M) 可以用來存放多位二進制數,M 的範圍是 1 – 64,如果不寫的話默認為 1 位。

下面我們來掩飾一下位類型

新建一個 test3 表,表中只有一個位類型的字段

create table test3(id bit(1));

然後隨意插入一條數據

insert into test3 values(1);

發現無法查詢出對應結果。

然後我們使用 hex()bin() 函數進行查詢

發現能夠查詢出對應結果。

也就是說當數據插入 test3 時,會首先把數據轉換成為二進制數,如果位數允許,則將成功插入;如果位數小於實際定義的位數,則插入失敗。如果我們像表中插入數據 2

insert into test3 values(2);

那麼會報錯

因為 2 的二進制數表示是 10,而表中定義的是 bit(1) ,所以無法插入。

那麼我們將表字段修改一下

然後再進行插入,發現已經能夠插入了

日期時間類型

MySQL 中的日期與時間類型,主要包括:YEAR、TIME、DATE、DATETIME、TIMESTAMP,每個版本可能不同。下錶中列出了這幾種類型的屬性。

下面分別來介紹一下

YEAR

YEAR 可以使用三種方式來表示

  • 用 4 位的数字或者字符串表示,兩者效果相同,表示範圍 1901 – 2155,插入超出範圍的數據會報錯。
  • 以 2 位字符串格式表示,範圍為 ‘00’‘99’。‘00’‘69’ 表示 20002069,‘70’‘99’ 表示1970~1999。‘0’ 和 ‘00’ 都會被識別為 2000,超出範圍的數據也會被識別為 2000。
  • 以 2 位数字格式表示,範圍為 199。169 表示 2001~2069, 70~99 表示 1970~1999。但 0 值會被識別為0000,這和 2 位字符串被識別為 2000 有所不同

下面我們來演示一下 YEAR 的用法,創建一個 test4 表

create table test4(id year);

然後我們看一下 test4 的表結構

默認創建的 year 就是 4 位,下面我們向 test4 中插入數據

insert into test4 values(2020),('2020');

然後進行查詢,發現表示形式是一樣的

使用兩位字符串來表示

delete from test4;

insert into test4 values ('0'),('00'),('11'),('88'),('20'),('21');

使用兩位数字來表示

delete from test4;

insert into test4 values (0),(00),(11),(88),(20),(21);

發現只有前兩項不一樣。

TIME

TIME 所表示的範圍和我們預想的不一樣

我們把 test4 改為 TIME 類型,下面是 TIME 的示例

alter table test4 modify id TIME;

insert into test4 values ('15:11:23'),('20:13'),('2 11:11'),('3 05'),('33');

結果如下

DATE

DATE 表示的類型有很多種,下面是 DATE 的幾個示例

create table test5 (id date);

查看一下 test5 表

然後插入部分數據

insert into test5 values ('2020-06-13'),('20200613'),(20200613);

DATE 的表示一般很多種,如下所示 DATE 的所有形式

  • ‘YYYY-MM-DD’
  • ‘YYYYMMDD’
  • YYYYMMDD
  • ‘YY-MM-DD’
  • ‘YYMMDD’
  • YYMMDD

DATETIME

DATETIME 類型,包含日期和時間部分,可以使用引用字符串或者数字,年份可以是 4 位也可以是 2 位。

下面是 DATETIME 的示例

create table test6 (id datetime);

insert into test4 values ('2020-06-13 11:11:11'),(20200613111111),('20200613111111'),(20200613080808);

TIMESTAMP

TIMESTAMP 類型和 DATETIME 類型的格式相同,存儲 4 個字節(比DATETIME少),取值範圍比 DATETIME 小。

下面來說一下各個時間類型的使用場景

  • 一般表示年月日,通常用 DATE 類型;

  • 用來表示時分秒,通常用 TIME 表示;

  • 年月日時分秒 ,通常用 DATETIME 來表示;

  • 如果需要插入的是當前時間,通常使用 TIMESTAMP 來表示,TIMESTAMP 值返回后显示為 YYYY-MM-DD HH:MM:SS 格式的字符串,

  • 如果只表示年份、則應該使用 YEAR,它比 DATE 類型需要更小的空間。

每種日期類型都有一個範圍,如果超出這個範圍,在默認的 SQLMode 下,系統會提示錯誤,並進行零值存儲。

下面來解釋一下 SQLMode 是什麼

MySQL 中有一個環境變量是 sql_mode ,sql_mode 支持了 MySQL 的語法、數據校驗,我們可以通過下面這種方式來查看當前數據庫使用的 sql_mode

select @@sql_mode;

一共有下面這幾種模式

來源於 https://www.cnblogs.com/Zender/p/8270833.html

字符串類型

MySQL 提供了很多種字符串類型,下面是字符串類型的匯總

下面我們對這些數據類型做一個詳細的介紹

CHAR 和 VARCHAR 類型

CHAR 和 VARCHAR 類型很相似,導致很多同學都會忽略他們之間的差別,首先他倆都是用來保存字符串的數據類型,他倆的主要區別在於存儲方式不同。CHAR 類型的長度就是你定義多少显示多少。佔用 M 字節,比如你聲明一個 CHAR(20) 的字符串類型,那麼每個字符串佔用 20 字節,M 的取值範圍時 0 – 255。VARCHAR 是可變長的字符串,範圍是 0 – 65535,在字符串檢索的時候,CHAR 會去掉尾部的空格,而 VARCHAR 會保留這些空格。下面是演示例子

create table vctest1 (vc varchar(6),ch char(6));

insert into vctest1 values("abc  ","abc  ");

select length(vc),length(ch) from vctest1;

結果如下

可以看到 vc 的字符串類型是 varchar ,長度是 5,ch 的字符串類型是 char,長度是 3。可以得出結論,varchar 會保留最後的空格,char 會去掉最後的空格。

BINARY 和 VARBINARY 類型

BINARY 和 VARBINARY 與 CHAR 和 VARCHAR 非常類似,不同的是它們包含二進制字符串而不包含非二進制字符串。BINARY 與 VARBINARY 的最大長度和 CHAR 與 VARCHAR 是一樣的,只不過他們是定義字節長度,而 CHAR 和 VARCHAR 對應的是字符長度。

BLOB 類型

BLOB 是一個二進制大對象,可以容納可變數量的數據。有 4 種 BLOB 類型:TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB。它們區別在於可容納存儲範圍不同。

TEXT 類型

有 4 種 TEXT 類型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。對應的這 4 種 BLOB 類型,可存儲的最大長度不同,可根據實際情況選擇。

ENUM 類型

ENUM 我們在 Java 中經常會用到,它表示的是枚舉類型。它的範圍需要在創建表時显示指定,對 1 – 255 的枚舉需要 1 個字節存儲;對於 255 – 65535 的枚舉需要 2 個字節存儲。ENUM 會忽略大小寫,在存儲時都會轉換為大寫。

SET 類型

SET 類型和 ENUM 類型有兩處不同

  • 存儲方式

SET 對於每 0 – 8 個成員,分別佔用 1 個字節,最大到 64 ,佔用 8 個字節

  • Set 和 ENUM 除了存儲之外,最主要的區別在於 Set 類型一次可以選取多個成員,而 ENUM 則只能選一個。

MySQL 運算符

MySQL 中有多種運算符,下面對 MySQL 運算符進行分類

  • 算術運算符
  • 比較運算符
  • 邏輯運算符
  • 位運算符

下面那我們對各個運算符進行介紹

算術運算符

MySQL 支持的算術運算符包括加、減、乘、除和取余,這類運算符的使用頻率比較高

下面是運算符的分類

運算符 作用
+ 加法
減法
* 乘法
/, DIV 除法,返回商
%, MOD 除法,返回餘數

下面簡單描述了這些運算符的使用方法

  • + 用於獲得一個或多個值的和
  • - 用於從一個值減去另一個值
  • * 用於兩數相乘,得到兩個或多個值的乘積
  • / 用一個值除以另一個值得到商
  • % 用於一個值除以另一個值得到餘數

在除法和取余需要注意一點,如果除數是 0 ,將是非法除數,返回結果為 NULL。

比較運算符

熟悉了運算符,下面來聊一聊比較運算符,使用 SELECT 語句進行查詢時,MySQL 允許用戶對表達式的兩側的操作數進行比較,比較結果為真,返回 1, 比較結果為假,返回 0 ,比較結果不確定返回 NULL。下面是所有的比較運算符

運算符 描述
= 等於
<> 或者是 != 不等於
<=> NULL 安全的等於,也就是 NULL-safe
< 小於
<= 小於等於
> 大於
>= 大於等於
BETWEEN 在指定範圍內
IS NULL 是否為 NULL
IS NOT NULL 是否為 NULL
IN 存在於指定集合
LIKE 通配符匹配
REGEXP 或 RLIKE 正則表達式匹配

比較運算符可以用來比較数字、字符串或者表達式。数字作為浮點數進行比較,字符串以不區分大小寫的方式進行比較。

  • = 號運算符,用於比較運算符兩側的操作數是否相等,如果相等則返回 1, 如果不相等則返回 0 ,下面是具體的示例,NULL 不能用於比較,會直接返回 NULL
  • <> 號用於表示不等於,和 = 號相反,示例如下
  • <=> NULL-safe 的等於運算符,與 = 號最大的區別在於可以比較 NULL 值
  • < 號運算符,當左側操作數小於右側操作數時,返回值為 1, 否則其返回值為 0。
  • 和上面同理,只不過是滿足 <= 的時候返回 1 ,否則 > 返回 0。這裏我有個疑問,為什麼
select 'a' <= 'b';  /* 返回 1 */

/*而*/

select 'a' >= 'b'; /* 返回 0 呢*/
  • 關於 >>= 是同理

  • BETWEEN 運算符的使用格式是 a BETWEEN min AND max ,當 a 大於等於 min 並且小於等於 max 時,返回 1,否則返回 0 。操作數類型不同的時候,會轉換成相同的數據類型再進行處理。比如

  • IS NULLIS NOT NULL 表示的是是否為 NULL,ISNULL 為 true 返回 1,否則返回 0 ;IS NOT NULL 同理
  • IN 這個比較操作符判斷某個值是否在一個集合中,使用方式是 xxx in (value1,value2,value3)
  • LIKE 運算符的格式是 xxx LIKE %123%,比如如下

當 like 後面跟的是 123% 的時候, xxx 如果是 123 則返回 1,如果是 123xxx 也返回 1,如果是 12 或者 1 就返回 0 。123 是一個整體。

  • REGEX 運算符的格式是 s REGEXP str ,匹配時返回值為 1,否則返回 0 。

後面會詳細介紹 regexp 的用法。

邏輯運算符

邏輯運算符指的就是布爾運算符,布爾運算符指返回真和假。MySQL 支持四種邏輯運算符

運算符 作用
NOT 或 ! 邏輯非
AND 或者是 && 邏輯與
OR 或者是 || 邏輯或
XOR 邏輯異或

下面分別來介紹一下

  • NOT 或者是 ! 表示的是邏輯非,當操作數為 0(假) ,則返回值為 1,否則值為 0。但是有一點除外,那就是 NOT NULL 的返回值為 NULL
  • AND&& 表示的是邏輯與的邏輯,當所有操作數為非零值並且不為 NULL 時,結果為 1,但凡是有一個 0 則返回 0,操作數中有一個 null 則返回 null
  • OR|| 表示的是邏輯或,當兩個操作數均為非 NULL 值時,如有任意一個操作數為非零值,則結果為 1,否則結果為 0。
  • XOR 表示邏輯異或,當任意一個操作數為 NULL 時,返回值為 NULL。對於非 NULL 的操作數,如果兩個的邏輯真假值相異,則返回結果 1;否則返回 0。

位運算符

一聽說位運算,就知道是和二進制有關的運算符了,位運算就是將給定的操作數轉換為二進制后,對各個操作數的每一位都進行指定的邏輯運算,得到的二進制結果轉換為十進制后就說是位運算的結果,下面是所有的位運算。

運算符 作用
& 位與
| 位或
^ 位異或
位取反
>> 位右移
<< 位左移

下面分別來演示一下這些例子

  • 位與 指的就是按位與,把 & 雙方轉換為二進制再進行 & 操作

按位與是一個數值減小的操作

  • 位或 指的就是按位或,把 | 雙方轉換為二進制再進行 | 操作

位或是一個數值增大的操作

  • 位異或 指的就是對操作數的二進制位做異或操作
  • 位取反 指的就是對操作數的二進制位做 NOT 操作,這裏的操作數只能是一位,下面看一個經典的取反例子:對 1 做位取反,具體如下所示:

為什麼會有這種現象,因為在 MySQL 中,常量数字默認會以 8 個字節來显示,8 個字節就是 64 位,常量 1 的二進製表示 63 個 0,加 1 個 1 , 位取反后就是 63 個 1 加一個 0 , 轉換為二進制后就是 18446744073709551614,我們可以使用 select bin() 查看一下

  • 位右移 是對左操作數向右移動指定位數,例如 50 >> 3,就是對 50 取其二進制然後向右移三位,左邊補上 0 ,轉換結果如下
  • 位左移 與位右移相反,是對左操作數向左移動指定位數,例如 20 << 2

MySQL 常用函數

下面我們來了解一下 MySQL 函數,MySQL 函數也是我們日常開發過程中經常使用的,選用合適的函數能夠提高我們的開發效率,下面我們就來一起認識一下這些函數

字符串函數

字符串函數是最常用的一種函數了,MySQL 也是支持很多種字符串函數,下面是 MySQL 支持的字符串函數表

函數 功能
LOWER 將字符串所有字符變為小寫
UPPER 將字符串所有字符變為大寫
CONCAT 進行字符串拼接
LEFT 返回字符串最左邊的字符
RIGHT 返回字符串最右邊的字符
INSERT 字符串替換
LTRIM 去掉字符串左邊的空格
RTRIM 去掉字符串右邊的空格
REPEAT 返回重複的結果
TRIM 去掉字符串行尾和行頭的空格
SUBSTRING 返回指定的字符串
LPAD 用字符串對最左邊進行填充
RPAD 用字符串對最右邊進行填充
STRCMP 比較字符串 s1 和 s2
REPLACE 進行字符串替換

下面通過具體的示例演示一下每個函數的用法

  • LOWER(str) 和 UPPER(str) 函數:用於轉換大小寫
  • CONCAT(s1,s2 … sn) :把傳入的參數拼接成一個字符串

上面把 c xu an 拼接成為了一個字符串,另外需要注意一點,任何和 NULL 進行字符串拼接的結果都是 NULL。

  • LEFT(str,x) 和 RIGHT(str,x) 函數:分別返回字符串最左邊的 x 個字符和最右邊的 x 個字符。如果第二個參數是 NULL,那麼將不會返回任何字符串
  • INSERT(str,x,y,instr) : 將字符串 str 從指定 x 的位置開始, 取 y 個長度的字串替換為 instr。
  • LTRIM(str) 和 RTRIM(str) 分別表示去掉字符串 str 左側和右側的空格
  • REPEAT(str,x) 函數:返回 str 重複 x 次的結果
  • TRIM(str) 函數:用於去掉目標字符串的空格
  • SUBSTRING(str,x,y) 函數:返回從字符串 str 中第 x 位置起 y 個字符長度的字符串
  • LPAD(str,n,pad) 和 RPAD(str,n,pad) 函數:用字符串 pad 對 str 左邊和右邊進行填充,直到長度為 n 個字符長度
  • STRCMP(s1,s2) 用於比較字符串 s1 和 s2 的 ASCII 值大小。如果 s1 < s2,則返回 -1;如果 s1 = s2 ,返回 0 ;如果 s1 > s2 ,返回 1。
  • REPLACE(str,a,b) : 用字符串 b 替換字符串 str 種所有出現的字符串 a

數值函數

MySQL 支持數值函數,這些函數能夠處理很多數值運算。下面我們一起來學習一下 MySQL 中的數值函數,下面是所有的數值函數

函數 功能
ABS 返回絕對值
CEIL 返回大於某個值的最大整數值
MOD 返回模
ROUND 四舍五入
FLOOR 返回小於某個值的最大整數值
TRUNCATE 返回数字截斷小數的結果
RAND 返回 0 – 1 的隨機值

下面我們還是以實踐為主來聊一聊這些用法

  • ABS(x) 函數:返回 x 的絕對值
  • CEIL(x) 函數: 返回大於 x 的整數
  • MOD(x,y),對 x 和 y 進行取模操作
  • ROUND(x,y) 返回 x 四舍五入后保留 y 位小數的值;如果是整數,那麼 y 位就是 0 ;如果不指定 y ,那麼 y 默認也是 0 。
  • FLOOR(x) : 返回小於 x 的最大整數,用法與 CEIL 相反
  • TRUNCATE(x,y): 返回数字 x 截斷為 y 位小數的結果, TRUNCATE 知識截斷,並不是四舍五入。
  • RAND() :返回 0 到 1 的隨機值

日期和時間函數

日期和時間函數也是 MySQL 中非常重要的一部分,下面我們就來一起認識一下這些函數

函數 功能
NOW 返回當前的日期和時間
WEEK 返回一年中的第幾周
YEAR 返回日期的年份
HOUR 返回小時值
MINUTE 返回分鐘值
MONTHNAME 返回月份名
CURDATE 返回當前日期
CURTIME 返回當前時間
UNIX_TIMESTAMP 返回日期 UNIX 時間戳
DATE_FORMAT 返回按照字符串格式化的日期
FROM_UNIXTIME 返回 UNIX 時間戳的日期值
DATE_ADD 返回日期時間 + 上一個時間間隔
DATEDIFF 返回起始時間和結束時間之間的天數

下面結合示例來講解一下每個函數的使用

  • NOW(): 返回當前的日期和時間
  • WEEK(DATE) 和 YEAR(DATE) :前者返回的是一年中的第幾周,後者返回的是給定日期的哪一年
  • HOUR(time) 和 MINUTE(time) : 返回給定時間的小時,後者返回給定時間的分鐘
  • MONTHNAME(date) 函數:返回 date 的英文月份
  • CURDATE() 函數:返回當前日期,只包含年月日
  • CURTIME() 函數:返回當前時間,只包含時分秒
  • UNIX_TIMESTAMP(date) : 返回 UNIX 的時間戳
  • FROM_UNIXTIME(date) : 返回 UNIXTIME 時間戳的日期值,和 UNIX_TIMESTAMP 相反
  • DATE_FORMAT(date,fmt) 函數:按照字符串 fmt 對 date 進行格式化,格式化后按照指定日期格式显示

具體的日期格式可以參考這篇文章 https://blog.csdn.net/weixin_38703170/article/details/82177837

我們演示一下將當前日期显示為年月日的這種形式,使用的日期格式是 %M %D %Y

  • DATE_ADD(date, interval, expr type) 函數:返回與所給日期 date 相差 interval 時間段的日期

interval 表示間隔類型的關鍵字,expr 是表達式,這個表達式對應後面的類型,type 是間隔類型,MySQL 提供了 13 種時間間隔類型

表達式類型 描述 格式
YEAR YY
MONTH MM
DAY DD
HOUR 小時 hh
MINUTE mm
SECOND ss
YEAR_MONTH 年和月 YY-MM
DAY_HOUR 日和小時 DD hh
DAY_MINUTE 日和分鐘 DD hh : mm
DAY_SECOND 日和秒 DD hh :mm :ss
HOUR_MINUTE 小時和分 hh:mm
HOUR_SECOND 小時和秒 hh:ss
MINUTE_SECOND 分鐘和秒 mm:ss
  • DATE_DIFF(date1, date2) 用來計算兩個日期之間相差的天數

查看離 2021 – 01 – 01 還有多少天

流程函數

流程函數也是很常用的一類函數,用戶可以使用這類函數在 SQL 中實現條件選擇。這樣做能夠提高查詢效率。下錶列出了這些流程函數

函數 功能
IF(value,t f) 如果 value 是真,返回 t;否則返回 f
IFNULL(value1,value2) 如果 value1 不為 NULL,返回 value1,否則返回 value2。
CASE WHEN[value1] THEN[result1] …ELSE[default] END 如果 value1 是真,返回 result1,否則返回 default
CASE[expr] WHEN[value1] THEN [result1]… ELSE[default] END 如果 expr 等於 value1, 返回 result1, 否則返回 default

其他函數

除了我們介紹過的字符串函數、日期和時間函數、流程函數,還有一些函數並不屬於上面三類函數,它們是

函數 功能
VERSION 返回當前數據庫的版本
DATABASE 返回當前數據庫名
USER 返回當前登陸用戶名
PASSWORD 返回字符串的加密版本
MD5 返回 MD5 值
INET_ATON(IP) 返回 IP 地址的數字錶示
INET_NTOA(num) 返回数字代表的 IP 地址

下面來看一下具體的使用

  • VERSION: 返回當前數據庫版本
  • DATABASE: 返回當前的數據庫名
  • USER : 返回當前登錄用戶名
  • PASSWORD(str) : 返回字符串的加密版本,例如
  • MD5(str) 函數:返回字符串 str 的 MD5 值
  • INET_ATON(IP): 返回 IP 的網絡字節序列
  • INET_NTOA(num)函數:返回網絡字節序列代表的 IP 地址,與 INET_ATON 相對

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

秋季穿搭顯白大法

“秋季穿搭顯白大法”

小小仙女們準備好購物車

Let’s go!

很多人都覺得皮膚不白

可以靠降低衣服的亮度反襯

這個想法和

“身邊都是沒我好看的人,那我就是最美的”

有些微妙得異曲同工

可問題是

這個設想只適用於天賦冷白皮

作為被上帝嫌棄的

“黃”和“暗”

這麼穿跟自殺沒什麼區別了

畢竟會顯得你又黑又臟又沒氣色

(氣質先摁住不提,深棕是真的壓膚色啊)

真正能顯膚白的

其實是明亮的淺彩色

仔細想想

平日拍照時為你兢兢業業

打光的反光板

是淺到底的亮白色

同理

想要膚色顯白

穿搭要往明亮掛走走

挑色要建立在

明亮而不刺眼的基準上

高明度+低飽和度

輕輕淺淺乾乾凈凈的顏色

看着舒服又白皙

如果你是一個黃皮

同時又愛死了深色

那你真的很難搞

那你可以嘗試

在大面積深色的基礎上

搭配一些明亮的單品

把滿屏的暗沉色調biu的提上來

這樣才不會顯得過分沉悶

高飽和度+低明度

雖然搭配醬說要往亮了穿

但是明亮≠炸眼!!!

能駕馭扎眼高飽和熒光色的

一般只有光澤感滿分的白皮和黑皮

黃皮如果不死心地想挑戰

哦豁 那就精彩了

但是如果你是個黃皮

又愛死了高飽和的彩色

那你和前面那位一樣難搞誒

可以嘗試把明度降低

也就是在原本的高調顏色里加一點灰

冷調如此

暖調也同樣適用

加了灰度的高飽和暖色

就像在晨霧裡看太陽

有種毛茸茸的溫和感

高飽和+小面積

滿屏熒光色雖然hin顯眼

但沒一會兒就會產生視覺疲勞

如果在常溫色調上加那麼

一點點熒光

反而會有意料之外的點睛效果

鞋子包包甚至DIY的腰帶

小小一塊熒光

照亮整個畫面

遠離臉部

秋冬全身上下露最多的

就是我們的小臉蛋兒

為了讓這僅有的露膚面積

盡可能的顯白

熒光配色部位一定要

遠!離!你!的!臉!

否則一秒變黃

不騙你

正確的打開姿勢是——

上半身可勁兒的淺

下半身可勁兒的熒光

畢竟一般人看你

會從最顯眼的褲子開始

經過上衣的的亮度谷底劃到臉上

會詭異地產生

“她好白啊”

的錯覺

(不明白的小小仙女們可以百度一下視覺后像)

用材質壓

如果你是個倔強的girl

一定要全身鋪滿高飽和

可以選擇垂墜感較強的材質

緩和一下顏色帶來的視覺衝擊

白色是萬能的包容色

可以把各種材質和色彩消化的很好

但是對黃皮寶寶來說

一身“保險員式”板正的白

堪比自殺式土黑現場

黃黑皮更適合米白、熟絹白練色

這種帶點宣紙質感的色彩

氣息比正白要溫和很多

再加上白色自帶的“視覺洗滌”效果

往往能給人乾淨舒服的感覺

遇到各種

花花綠綠咋咋呼呼的彩色

也能壓得住場面

本站聲明:網站內容來源於http://www.shelive.net/,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※你應該要知道的電子煙懶人包!

※男人為什麼總愛舒壓按摩台北外送茶交流呢?

※解決小三、二奶問題,斬桃花推薦

※網友現身說法:神祕的降頭術?

※挽回老公/男友的心,和合術諮詢

※老司機曝包養內幕

穿衣不懂配色,簡直土掉渣

除了黑白灰,你們平時還會穿什麼顏色?

我知道很多小可是愛也不是非要穿黑白灰的人。

只是每天盯着柜子看半天也不知道,到底哪個配哪個才好看,拿出來一穿,請問還能再丑一點嗎???

挑到最後只能放棄,一臉輕鬆的拿出穿了兩年的黑T恤、黑褲子,滿意的看着鏡子中的自己,老娘還是穿黑色好看,那些妖艷賤貨根本配不上我

醒醒吧姐妹,根本不是穿彩色不好看,而是你不會配色啊!

等學會了色彩搭配,再犀利的顏色穿在你身上都會變得“服服帖帖”!

紅色搭配:白色、藍色、黑色

紅色作為超級大氣、熱情、顯眼的顏色,無論搭配到什麼單品上都會非常精彩。但問題也在於,紅色太過熱情了,搭配不得體就會丑的很顯眼。

所以在這種情況下,我們可以選擇白色來緩衝這種衝擊感。

或者乾脆只用紅色做裝飾,在白色的衣服上做裝飾,這樣還更適合日常穿搭。乾淨清透~

黑色也是和紅色很搭的顏色,基本屬於那中閉着眼睛穿都不會出錯的搭配。

不想顯的自己很成熟,還可以搭配撞色款的藍色,藝術氣息很濃有沒有

休閑一點的風格就可以選擇牛仔來搭配啦,這也是現在最常見的搭配方式,休閑隨意很好看。

當然,成熟的搭配也是有的,把紅色和卡其色搭配在一起真的非常有韻味,成熟但一點都不顯老。

總的來說,只要身上顏色不超過3種,以上這些配色都是可以混合搭配的,一樣很好看

橙色搭配:藍色、白色、黑色

2019最火珊瑚橙你們確定不get一件?

不過話說回來,雖然橙色很跳躍很明亮也很好看,但是真的不太好搭配,確切的說不太容易配的好看

這時候就必須拿出最完美的百搭色啦!

白色!能夠完美搭配任何顏色的百搭色,只要款式選的對,想穿出什麼風格都隨你。

但是想搭出特色當然不能只靠百搭色。

想必大家很難想象橙色搭藍色會是什麼效果吧!

其實它們搭在一起的效果真的非!常!好看!因為冷色調的藍色可以有效緩和橙色的暖色調。

其次就是萬能黑,雖不屬於冷色調,但是它同樣具備低調屬性,甚至高級感更強!

本站聲明:網站內容來源於http://www.shelive.net/,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

電子煙有爭議?真相解密

桃園外送茶莊、喝茶吃宵夜首選

※愛情診療室,感情挽回推薦

※挽回老公/男友的心,和合術諮詢

※揭開降頭祕辛?

※老司機曝包養內幕