【MySQL】

6月2号学习记录

1. git介绍

2. git命令总结

3.git遇到的问题

fatal: not a git repository (or any of the parent directories): 

解决方法:在命令行输入git init 回车就好了。

4.  python-存储数据

<1>一种简单的方法是使用模块json(javascript object notation

javascript 对象表示法)来存储数据。

模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用JSON在Python程序之间分享数据。

更重要的是,JSON数据格式并非Python专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。

<2> 使用 json.dump() 和 json.load()

json.dump()接受两个实参,要存储的数据和用于存储数据的文件对象

实例:如何用json.dump()来存储数字列表:

import json  #首先导入模块json,在传建一个数字列表,

numbers = [2, 6, 7, 9, 11, 13]

filename1 = 'numbers.json' #把数字列表存储到json中,在创建一个JSON格式的文件

with open(filename1, 'w') as file_obj:
    json.dump(numbers, file_obj) #这个程序没有输出,打开文件就可以看到

在编写一个json.load()程序,,将这个列表读取到内存中。

import json

filename1 = 'numbers.json' #我们要确保读取的是前面那个文件,

with open(filename1) as file_obj: #直接打开那个文件
    numbers = json.load(file_obj) #json.load()加载存储在numbers.json中的信息,并将其存储在变量中
print(numbers)

注:这是一种在程序之间共享数据的简单方式。

# 10.4.2保存和读取用户生成的数据
# 例子:用户首次运行程序时被提示输入自己的名字,这样再次运行程序时就记住他了。
import json

username = input('你的名字叫什么?') #提示输入用户名,并将其存储在一个变量中。

filename2 = 'username.json'

with open(filename2, 'w') as file_obj:
    json.dump(username, file_obj) #调用方法,将用户名和文件对象传递给它,从而将用户名存储到文件中
    print('你回来的时候我们会记住你的,' + username + '。') #打印一条消息,存储他输入的信息。

with open(filename2) as file_obj:
    json.load(file_obj)  # 将存储在username.json文件中的数据读取出来,读到变量username中,
    print('欢迎回来,' + username + '。')

将两个代码合并在一起。

# 如果以前存储了用户名,就加载它, 
# 否则,就提示用户输入用户名并加载它。
import json
filename2 = 'username.json'
try:
    with open(filename2) as file_obj:
        username = json.load(file_obj)
except FileNotFoundError:
    username = input('你的名字叫什么?')
    with open(filename2, 'w') as file_obj:
        json.dump(username, file_obj)
        print('你回来的时候我们会记住你的,' + username + '。')
else:
    print('欢迎回来,' + username + '。')

5. mysql -第八章用通配符进行过滤

select prod_id, prod_name from django_apps_product where prod_name like "jet%";

select prod_id, prod_name from django_apps_product where prod_name like "%anvi%";

 29      | .5 ton anvil |
| 30      | 1 ton anvil  |
| 31      | 2 ton anvil 
select prod_id, prod_name from django_apps_product where prod_name like "_ ton anvil";

+---------+-------------+
| prod_id | prod_name   |
+---------+-------------+
| 30      | 1 ton anvil |
| 31      | 2 ton anvil |
+---------+-------------+

select prod_name from django_apps_product where prod_name regexp "1000" order by prod_name;

+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
+--------------+
 

select prod_name from django_apps_product where prod_name regexp ".000" order by prod_name;

+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
select prod_name from django_apps_product where prod_name like "1000" order by prod_name;

+-----------+
| prod_name |
+-----------+
+-----------+

 select prod_name from django_apps_product where prod_name regexp "1000" order by prod_name;

+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
+--------------+
select prod_name from django_apps_product where prod_name regexp "1000|2000" order by prod_name;

select prod_name from django_apps_product where prod_name regexp "[123] Ton" order by prod_name;

+-------------+
| prod_name   |
+-------------+
| 1 ton anvil |
| 2 ton anvil |
+-------------+

select prod_name from django_apps_product where prod_name regexp "1|2|3 Ton" order by prod_name;

+--------------+
| prod_name    |
+--------------+
| 1 ton anvil  |
| 2 ton anvil  |
| JetPack 1000 |
| JetPack 2000 |
| TNT(1 stick) |
+--------------+
select prod_name from django_apps_product where prod_name regexp "[^123] Ton" order by prod_name;

select prod_name from django_apps_product where prod_name regexp "[1-5] Ton" order by prod_name;

insert into django_apps_vendor(vend_name) values ("ACME"),("Anvils R Us"),("Furball Inc."),("Jet Set"),("Jouets Et Ours"),("LT Supplies");

6月3号学习记录

1.Git命令

2. Python-重构函数
# 10.4.3重构 将代码划分为一系列完成具体工作的函数,这样的过程被称为重构。
# 重构让代码更清晰、更易于理解、更容易扩展。
# 可将大部分逻辑放到一个或多个函数中。
def greet_user():
    """问候用户,并指出起名字"""
    filename2 = 'username.json'
    try:
        with open(filename2) as file_obj:
            username = json.load(file_obj)
    except FileNotFoundError:
        username = input('你的名字叫什么?')
        with open(filename2, 'w') as file_obj:
            json.dump(username, file_obj)
            print('你回来的时候我们会记住你的,' + username + '。')
    else:
        print('欢迎回来,' + username + '。')


greet_user()


# 下面来重构函数greet_user()
def get_stored_username():  # 新增函数
    """如果存储了用户名,就获取它"""
    filename2 = 'username.json'
    try:
        with open(filename2) as file_obj:
            username = json.load(file_obj)
    except FileNotFoundError:
        return None
    else:
        return username


def get_new_username():
    """提示用户输入用户名"""
    username = input('你的名字叫什么?')
    filename2 = 'username.json'
    with open(filename2, 'w') as file_obj:
        json.dump(username, file_obj)
    return username


def greet_user():
    """问候用户,并指出起名字"""
    username = get_stored_username()
    if username:
        print('欢迎回来,' + username + '。')
    else:
        username = get_new_username()
        print('你回来的时候我们会记住你的,' + username + '。')


greet_user()
# 注释: 每个函数都执行单一而清晰的任务,我们调用了greet_user(),打印一条消息,要么欢迎老用户回来,要么问候新用户。
# 首先调用get_stored_username(),在调用get_new_username()

3. mysql 

比如:“ . ”, '[ ]', ' | ', ' - ',等还有其他的一些字符,怎么才能匹配到这些字符?

例子:select vend_name from django_apps_vendor where vend_name regexp "." order by 
| vend_name      |
+----------------+
| ACME           |
| Anvils R Us    |
| Furball Inc.   |
| Jet Set        |
| Jouets Et Ours |
| LT Supplies    |
+----------------+

这样的输出并不是我们想要的输出,所以我们也要在匹配特殊字符前使用\\。

\\-:代表匹配-, \\.:代表匹配.

select vend_name from django_apps_vendor where vend_name regexp "\\." order by vend_name;
+--------------+
| vend_name    |
+--------------+
| Furball Inc. |
+--------------+

这个才是我们想要的输出。这种处理就是所谓的转义。正则表达式中具有特殊含义的所用字符都必须以这种方式转义。包括:. | [ ]以及迄今为止使用过的其他特殊字符。

\\t \\r \\n \\f \\v :代表的意思不一样。

Mysql要求两个反斜杠:\\ Mysql自己解释一个,regexp自己解释另一个。

匹配字符类:

数字,字母,和数字字母。

[:alnum:]:表示任意字母和数字(a-z A-Z 0-9)。

[:alpha:] :任意字母

[:digit:] :数字

[:xdigit:]: 十六进制数字(a-f A-F 0-9)

[:lower:]

[:upper:]

[:space:] :任意空白字符(\\t \\r \\n \\f \\v)

[:cntrl:] ASCII控制字符(0-31和127)

[:blank:] :空格和制表

匹配多个实例:

* ,+, ?,{n},{n,},{n,m}

0个或多个匹配,*

1个或多个匹配 +

0个或1个匹配 ?

指定数目的匹配{n}

不少于指定数目的匹配{n,}

匹配数目的范围{n,m ,m不超过255}

select prod_name from django_apps_product where prod_name regexp "\\([0-9] sticks?\\)" order by prod_name;
+---------------+
| prod_name     |
+---------------+
| TNT(1 stick)  |
| TNT(5 sticks) |
+---------------+
\\([0-9] sticks?\\) :\\(匹配),[0-9],sticks? 匹配stick和sticks,?匹配0个或一个,使s变成可选,

\\)匹配)

my_firstDB> select prod_name from django_apps_product where prod_name regexp "[[:digit:]]{4}" order by prod_name;
+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
"[[:digit:]]{4}" :匹配任意数字为4位

select prod_name from django_apps_product where prod_name regexp "[0-9][0-9][0-9][0-9]" order by prod_name;
+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
定位符:

^ $ [[:>:]] [[:<:]]

文本开始 文本结束, 词的开始,词的结尾

select prod_name from django_apps_product where prod_name regexp "^[0-9\\.]" order by prod_name;
+--------------+
| prod_name    |
+--------------+
| .5 ton anvil |
| 1 ton anvil  |
| 2 ton anvil  |
+--------------+

^ :的两个含义:[^123]表示否定

^[0-9\\.]:表示文本的开始。

like和regexp:利用定位符(^和$)like可以达到和regexp一样的作用。

简单的regexp测试

select "hello" regexp "[0-9]";
+------------------------+
| "hello" regexp "[0-9]" |
+------------------------+
| 0                      |
+------------------------+

0代表不匹配,1代表匹配。很显然hello中没用数字。

6月4号学习记录

第十章-创建字段

1.MySQL

拼接字段-将值联结到一起构成单个值。

把两个列拼接起来,可使用Concat()函数来拼接两个列。

select CONCAT(vend_name, '(', vend_country, ')') from django_apps_vendor order by vend_name;
+-------------------------------------------+
| CONCAT(vend_name, '(', vend_country, ')') |
+-------------------------------------------+
| ACME(USA)                                 |
| Anvils R Us(USA)                          |
| Furball Inc.(USA)                         |
| Jet Set(England)                          |
| Jouets Et Ours(France)                    |
| Lt Supplies(USA)                          |
+-------------------------------------------+
 

select CONCAT(RTrim(vend_name), '(', RTrim(vend_country), ')') from django_apps_vendor order by vend_name;

RTrim():去掉右边多余的空格。

LTrim:去掉左边多余的空格。

Trim():去掉两边多余的空格。

使用别名:别名是一个字段或值的替换名,别名用as 关键字。

select CONCAT(RTrim(vend_name), '(', RTrim(vend_country), ')') as vend_title from django_apps_vendor order by vend_name;
+------------------------+
| vend_title             |
+------------------------+
| ACME(USA)              |
| Anvils R Us(USA)       |
| Furball Inc.(USA)      |
| Jet Set(England)       |
| Jouets Et Ours(France) |
| Lt Supplies(USA)       |
+------------------------+

2.django-第九章完成整个博客系统

9.1 增加搜索和作者过滤

基于之前写好的class-based view,很容易就可以完成这一需求。

9.1.1 增加搜索功能

这里可以根据title和desc搜索,这样的话需要怎么做呢?

其实实现很简单,依然需要继承IndexView。只需要控制数据源就好了,在IndexView中,控制数据源的部分由get_queryset方法实现。

在blog/views.py中新增代码:

from django.db.models import Q  # 这一句放到文件的第一行
class SearchView(IndexView):
    def get_context_data(self):
        context = super().get_context_data()
        context.update({
            'keyword': self.request.GET.get('keyword', '')
        })
        return context

    def get_queryset(self):
        queryset = super().get_queryset()
        keyword = self.request.GET.get('keyword')
        if not keyword:
            return queryset
        return queryset.filter(Q(title_icontains=keyword) | Q(desc_icontains=
                                                              keyword))

接着配置url.py ,在url中引入SearchView,然后将

url(r'^search/$', SearchView.as_view(), name='search'),增加到urlpatterns配置中。

接下来,需要做的就是修改搜索部分的模块了。在目录bootstrap/templates/blog/base.html中增加一下内容,将nav中form部分的代码修改为:

<form class="form-inline" action="/search/" method="GET">
    <input class="form-control" type="search" name="keyword" placeholder="Search"
           aria-label="Search" value="{{ keyword }}">
    <button class="btn btn-outline-success" type="submit">搜索</button>
</form>

form的作用是提交数据到服务端,action用于指定提交数据到那个URL上,既可以是相对路径,也可以是绝对路径。method指定以那种方法发送数据,是get还是post。

在提交数据时,input标签的内容会被发送到服务器,submit用来完成数据提交的按钮,他可以是input标签或者是button标签。

9.1.2 增加作者页面。

在blog/views.py中增加代码即可。

class AuthorView(IndexView):
    def get_queryset(self):
        queryset = super().get_queryset()
        author_id = self.kwargs.get('owner_id')
        return queryset.filter(owner_id=author_id)

相对搜索来说,只需控制数据源,如果需要调整展示的逻辑,可以重写get_context_data来完成。

接着修改urls.py,引入新增加的AuthorView,然后再加入到urlpatterns配置中。

url(r'^author/(?P<owner_id>\d+)/$', AuthorView.as_view(), name='author'),

总结:

基本上就完成了搜索界面和作者界面。相对来说比较简单易懂,当然还有一些需要在模版中渲染为作者链接的部分。

6月5号学习记录

1. mysql 十一章使用数据处理函数

11.1 函数:

例子:RTrim(),Trim(), LTrim(),

11.2使用函数

处理字符串的文本函数(supper(),lower())。

数值函数

日期和时间函数

系统函数

11.2.1 文本处理函数

代码如下:select vend_name, upper(vend_name) as vend_name_upcase from django_apps_vendor order by vend_name;

upper()将文本转换为大写

常用的文本处理函数:RTrim(),Trim(), LTrim(),Upper(),Lower(),SubString(), Length(), Left(), Right(), Locate().

Soundex()函数:

select cust_name, cust_contact from django_apps_customer where cust_contact = "Y.Lie";

把cust_contact= "Y.Lee"输入为cust_contact = "Y.Lie"结果没有数据返回

select cust_name, cust_contact from django_apps_customer where Soundex(cust_contact) = Soundex("Y.Lie");

使用这个函数进行搜索,它匹配所用发音类似于Y.Lie的联系名。

11.2.2 日期和时间处理函数https://wiki.jikexueyuan.com/project/mysql/useful-functions/time-functions.html

日期必须为格式yyyy-mm-dd

6月6号学习记录

1.MySQL-执行算术运算

select prod_id, quantity, item_price from orderitems where order_num = 20005;

select prod_id, quantity, item_price, quantity*item_price as expanded_price from orderitems where order_num = 20005;

quantity*item_price总的价格。

算术运算符的优先级,括号,加,减,乘,除。

如何去测试计算:

例子:select (3*2) ;

select RTrim('abc');  

select now();   

函数:upper()转换为大写

select vend_name, upper(vend_name) as vend_name_upcase from vendors order by vend_name;   

soundex()根据发音比较而不是字母比较。

select cust_name, cust_contact from customers where cust_contact = "Y.Lie";

 select cust_name, cust_contact from customers where soundex(cust_contact) = soundex("Y.Lie");

日期的简单查询:yyyy-mm-dd基本格式

select order_num, cust_id from orders where order_date = '2005-09-01';

select order_num, cust_id from orders where date(order_date) = '2005-09-01';

select cust_id, order_num from orders where date(order_date) between '2005-09-01' and  '2005-09-30';日期范围

select cust_id, order_num from orders where year(order_date) = 2005 and month(order_date) = 9;

这个不需要记住每个月有多少天,或者不需要关心闰年2月的方法。

数值函数;:

select mod(6,3)   select sqrt(8);  select exp(8); select abs(-1);select cos(1);select rand();select pi()

2.mysql-汇总数据

聚集函数:运行在行组上,计算和返回单个值的函数。

确定表中的行数, 获得表中行组的数据, 找出表列的最大值,最小值和平均值。

mysql给出5个聚集函数。

AVG() 平均值 count()行数 max()最大值 min()最小值 sum()值之和

 实例:select avg(prod_price) as avg_price from products;

count():

select count(*) as num_cust from customers;所用的行,忽略null

select count(cust_email) as num_cust from customers; 特定的行。不忽略null

max():

select max(prod_price) as max_price from products;

mix():

select min(prod_price) as min_price from products;

sum(): 返回指定列值的和。

select sum(quantity) as items_ordered from orderitems where order_num = 20005;求20005这个订单号,数量的和。

select sum(quantity*item_price) as items_ordered from orderitems where order_num = 20005;

求20005物品价钱的和,

select avg(distinct prod_price) as avg_price from products where vend_id = 1003;

组合聚集函数使用:select count(*) as num_items, min(prod_price) as price_min, max(prod_price) as price_max, avg(prod_price) as price_avg from products;

6月7号学习记录

一. MySQL

1.分组数据

select count(*) as num_prods from products where vend_id = 1003;

返回供应商1003提供的产品数目。

2.创建分组:group by 子句

select vend_id, count(*) as num_prods from products group by vend_id;

group by按vend_id 排序并分组数据。

group by 子句的一些规定:

<1> group by子句必须出现在where子句之后,order by子句之前。

<2> group by子句可以包含任意数目的列。

<3>在建立分组时,指定的所有列都一起计算。如果在group by子句中嵌套了分组,数据将在最后的分组上进行汇总。

<4> group by子句中列出的每个列必须是检索列或有效的表达式(但不能是聚集函数)。

如果在select中使用表达式,则必须在group by子句中指定相同的表达式。不能使用别名。

<5>除聚集函数语句外,select语句中的每个列都必须在group by子句中给出。

<6>如果分组列中具有null值,则null将作为一个分组返回。有多行null值,他们将分为一组。

3. with rollup 和 coalesce

select count(*) as num_prods from products group by vend_id with rollup;

把总的行数在进行汇总, 返回null

把返回null的设置一个取代名称,语法:coalesce(a,b,c),

如果a==null则选择b,b==null则选择c

select coalesce(vend_id, '总和:'), count(*) as num_prods from products group by vend_id with rollup;

4. 过滤分组having

select cust_id, count(*) as orders from orders group by cust_id having count(*) >= 2;

先用group by子句进行分组,然后再用having子句进行过滤分组。

select vend_id, count(*) as num_prods from products where prod_price >= 10 group by vend_id; 先进行分组,找出所有符合条件的

select vend_id, count(*) as num_prods from products where prod_price >= 10 group by vend_id having count(*) >= 2;在过滤

5.分组和排序group by 和order by 结合使用

select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum(quantity*item_price) >= 50 order by ordertotal;
第一步先进行分组:

select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num;

第二步进行分组过滤

select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum(quantity*item_price) >= 50;

最后一步:进行排序

select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum(quantity*item_price) >= 50 order by ordertotal;

6.select字句的循序

select   from  where  gruop  by  having  order by limit

select 要返回列或表达式, 

from 从中检索数据的表, 仅在从表选择数据时使用

where 行级过滤

group by 分组说明, 仅在按组计算聚集时使用

having 组级过滤

order by 输出排序顺序

limit 要检索的行数

Django3.xx——xadmin 报错处理总结

错误1.AttributeError: module ‘django.db.models‘ has no attribute ‘FieldDoesNotExist‘

解决方法:

from django.core import exceptions

 问题2:IndexError: list index out of range

解决方法:

我的解决方法:(用><分组)

将:
input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs).replace('/><input', '/>\n<input').split('\n') if ht != '']
改为:
input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs).split('><') if ht != '']
input_html[0] = input_html[0] + ">"
input_html[1] = "<" + input_html[1]

6月8号学习记录

1.MySQL——子查询

select 语句是SQL的查询。select语句的简单查询,从单个数据库表中检索数据的单条语句。

查询(query):任何SQL语句都是查询。但此术语一般指select语句。

SQL语句还允许创建子查询,既嵌套在其他查询中的查询。

<1> 利用子查询进行过滤

例子:假如需要列出订购物品TNT2的所有客户,应该怎么检索?

步骤:第一步:检索包含物品TNT2的所有订单的编号。

select order_num from orderitems where prod_id = "TNT2"; 返回结果为(20005, 20007)

第二步:检所具有前一步骤列出的订单编号的所有客户的ID。

select cust_id from orders where order_num in (select order_num from orderitems where prod_id = "TNT2"); 或者select cust_id from orders where order_num in (20005, 20007);返回结果为(10001, 10004)

第三部:检索前一步骤返回的所有客户ID的客户信息。

select cust_name, cust_contact from customers where cust_id in (select cust_id from orders where order_num in (select order_num from orderitems where prod_id = "TNT2"));

或者select cust_name, cust_contact from customers where cust_id in (10001, 10004);

上述的每一个步骤都可以单独作为一个查询来执行。

可以把一条select语句返回的结果用于另一条select语句的where子句。

在select语句中,子查询总是从内向外处理。

注意:in操作符作用是传递给外部查询的where子句。子查询一般与in操作符结合使用。但也可以用于测试,等于(=),不等于(!=,<>)等。

<2> 作为计算字段使用子查询,另一种子查询使用方式

select cust_name, cust_state, (select count(*) from orders where orders.cust_id = customers.cust_id) as orders from customers order by cust_name;

这里涉及到相关子查询:设计外部查询的子查询。任何时候只要列名可能有多义性,就必须使用这种语法,表名和列名由一个句点分割。

where orders.cust_id = customers.cust_id

2.python测试

11.1 测试函数


11.1.1 单元测试和测试用例

Python标准库中的模块unittest提供了代码测试工具

单元测试用于核实函数的某个方面没有问题;
测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。
对于大型项目,要实现全覆盖可能很难。

11.1.2 可通过的测试

要为函数编写测试用例,可先导入模块unittest以及要测试的函数,在创建一个继承unittest.TestCase的类。,并编写一系列方法
对函数行为的不同方面进行测试。

11.1.3 不能通过的测试

11.1.4 测试未通过怎么办

11.1.5 添加新测试
11,2 测试类

前面是针对函数的测试,那么现在就是针对类的测试。
对类进行测试可以保证所做的改进没有破坏其原有的行为。

11.2.1 各种断言方法
unittest.TestCase类中提供了很多断言方法。断言方法检查你认为应该满足的条件是否确实满足。
如果该条件满足,你对程序行为的假设就得到了确认,你就可以确信其中没有错误。
反之,Python将发生异常。
unittest 中的断言方法
1. assertEquals(a,b) 核实 a==b
2. assertNotEquals(a,b) 核实 a != b
3. assertTrue(x) 核实 x=True
4.assertFalse(x) 核实 x=False
5. assertIn(item, list) 核实item 在 list中。
6. assertNotIn(item, list) 核实 item 不再 list中

6月9号学习进度

1.Django-rest-fromwork遇到的问题

Django REST Framework (DRF): TypeError: register() got an unexpected keyword argument 'base_name'j

将base_name改为basename就好了

2.How to fix " AttributeError at /api/doc 'AutoSchema' object has no attribute 'get_link' " error in Django

在Settings.py下配置:

REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' }

就好了。

第十六章——创建高级联结

1.使用表别名

select

concat(vend_name, '(',vend_country, ')')

as vend_title

from vendors

order by vend_name;

返回的数据:列别名

+------------------------+
| vend_title             |
+------------------------+
| ACME(USA)              |
| Anvils R Us(USA)       |
| Furball Inc.(USA)      |
| Jet Set(England)       |
| Jouets Et Ours(France) |
| LT Supplies(USA)       |
+------------------------+

别名除了用于列名和计算字段外,SQL还允许给表名起别名。

这样做的理由是:缩短SQL语句; 允许在单条select语句中多次使用相同的表。

 代码如下:

select cust_name, cust_contact

from customers as c, orders as o, orderitems as oi

where c.cust_id = o.cust_id

and o.order_num = oi.order_num

and prod_id = "TNT2";

表别名:

返回的数据:+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+

可以看到,from子句中三个标全都具有别名。

表别名不仅能用于where子句,还可以用于select的列表,order by子句以及语句的其他部分。

注意:表别名只在查询执行中使用。与列别名不一样,表别名不返回客户机。

2. 使用不同类型的联结

其他的联结:自联结、自然联结、外部联结

自联结:(了解)

select p1.prod_id, p1.prod_name

from products as p1, products as p2

where p1.vend_id = p2.vend_id

and p2.prod_id = "DTNTR";

6月10号学习记录

1.flask遇到的问题

flask.cli.NoAppException: Could not import "hello"

在学习flask的时候,运行python -m flask run命令,启动程序报上面的问题

在要运行的py文件下运行,比如此处的hello.py还有上一层目录test,需要切换到hello.py的目录下,运行python -m flask run

$ export FLASK_APP = hello.py
$ export FLASK_ENV = development
$ flask run

16.2.MySQL——使用不同类型的联结

16.2.1自联结

select vend_id from products where prod_id = "DTNTR";

select prod_id, prod_name from products where vend_id = 1003 order by prod_name;

这是通过子查询得到的。

找你使用自联结来查找?

select p1.prod_id, p1.prod_name

from products as p1, products as p2

where p1.vend_id = p2.vend_id

and p2.prod_id = 'DTNTR';

此查询中需要的两个表实际上是相同的表,因此products表在from子句中出现了两次。这里用表别名,p1为第一次出现的,p2为第二次出现的。按照第二个表中的prod_id过滤数据。

注意:自联结通常作为外部语句用来替代从相同的表中检索数据时使用的子查询语句。

16.2.2自然联结:排除多次出现,使每个列返回一次。

自然联结是这样一种联结,其中你只能选择那些唯一的列。

这一般是通过对表使用通配符(select *),对所有其他表的列使用明确的子集来完成的。

select c.*, o.order_num, o.order_date, oi.prod_id, oi.quantity, oi.item_price

from customers as c, orders as o, orderitems as oi

where c.cust_id  = o.cust_id

and o.order_num = oi.order_num

and prod_id = 'FB';

从这个例子中,通配符只对第一个表使用,所有其他列明确列出,所以没有重复的列被检索出来。

外部联结:联结包含那些在相关表中没有关联行的行。

需求:每个客户下了多少订单进行计数,包括哪些没有下订单的客户。

列出所有产品以及订购数量,包括没有订购的产品。

计算平均销售规模,包括哪些至今没有下订单的客户。

代码例子:内部连接

select customers.cust_id, orders.order_num

from customers inner join orders

on customers.cust_id = orders.cust_id;

在使用outer join语法时,必须使用right或left关键字指定包括其所有行的表

(right指出的是outer join右边的表,而left指出的是outer join左边的表。)

16.2.3外部联结:

select customers.cust_id, orders.order_num

from customers left outer join orders #左外部联结

on customers.cust_id = orders.cust_id;

select customers.cust_id, orders.order_num

from customers right outer join orders #右外部联结

on customers.cust_id = orders.cust_id;

16.3使用带聚集函数的联结

select customers.cust_name, customers.cust_id, count(orders.order_num) as num_ord

from customers inner join orders

on customers.cust_id = orders.cust_id

group by customers.cust_id;

select customers.cust_name, customers.cust_id, count(orders.order_num) as num_ord

from customers left outer join orders

on customers.cust_id = orders.cust_id

group by customers.cust_id;

16.4 使用联结和联结条件

1. 注意所使用的联结类型。一般我们使用内部联结,但使用外部联结也是有效的。

2.保证使用正确的联结条件。,否则将返回不正确的数据。

3.应该总是提供联结条件,否则会得出笛卡儿积。

4.在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。

16.5小结

6月11号学习记录

1.MySQL第十七章——组合查询

17.1 组合查询

定义:允许执行多个查询(多条select语句),并将结果作为单个查询结果集返回。这些组合查询通常称为并(union)或复合查询。

有两种基本情况,其中需要使用组合查询。

1.在单个查询中从不同的表返回类似结构的数据。

2.对单个表执行多个查询,按单个查询返回数据。(这一章只介绍了这一个单个表的组合查询,但是union组合查询可以应用不同的表。)

17.2 创建组合查询

可用union操作符来组合数条SQL查询。利用union,可给出多条select语句,将他们的结果组合成单个结果集。

17.2.1使用union

使用很简单,只需要给出每条select语句,在各条语句之间放上关键字union.

代码示例:select vend_id, prod_id, prod_price from products where prod_price <= 5;

select vend_id, prod_id, prod_price from products where vend_id in (1001, 1002);

1.使用union将这两条语句联系在一起

select vend_id, prod_id, prod_price from products where prod_price <= 5

union

select vend_id, prod_id, prod_price from products where vend_id in (1001, 1002);

解释:这两条语句用union关键字分隔。

union只是MySQL执行两条select语句,并把输出组合成单个查询结果集。

2.使用where怎么写?

select vend_id, prod_id, prod_price from products where prod_price <= 5

or vend_id in (1001, 1002);

我们可以比较一下union和where,很显然where子句更加简单,但是对于更加复杂的过滤条件来说,或者从多个表(而不是单个表)中检索数据的情形,使用union可能会使处理更加简单。

17.2.2 union 规则。

注意几点:第一:union必须是两条或者两条以上的select语句组成,语句之间用关键字union分隔。

第二:union中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)

第三:列数据类型必须兼容。

17.2.3 包含或取消重复的行

select vend_id, prod_id, prod_price from products where prod_price <= 5;返回4行

select vend_id, prod_id, prod_price from products where vend_id in (1001, 1002);返回5行

但使用union却返回8行,说明有一行是重复的行。所以union会将重复的行自动取消。

其实你也可以取消重复,使用union all

select vend_id, prod_id, prod_price from products where prod_price <= 5

union all

select vend_id, prod_id, prod_price from products where vend_id in (1001, 1002);

说明:使用union all MySQL不取消重复的行。

union all 为union一种形式,他完成where子句完成不了的工作。如果确实需要每个条件的的匹配行全部出现(包括重复行),则必须使用union all而不是union。

17.2.4 对组合查询结果排序

select语句输出用order by子句排序。在用union组合查询时,只能使用一条order by子句,他必须出现在最后一条select 语句之后。

select vend_id, prod_id, prod_price from products

where prod_price <= 5

union

select vend_id, prod_id, prod_price from products where vend_id in (1001, 1002)

order by prod_price;

2.djnago——调试和优化

12.1.1调试手段

一般情况下,有这么几种调试方法:print、logging(日志记录)和pdb(ipdb)。

1.print这种方法最简单、最直观的方法,新手上来就会用。(注意print只能用于开发阶段,上线之后代码里应该不允许存在print的调试代码。)

需要补充的是,如果需要打印json或者dict格式的数据,可以使用pprint模块的pprint函数。

import json

from pprint import pprint

pprint(json.loads(data))

2.logging模块(如果我们想要在线上收集一些数据的话,可以使用logging模块)

注意:print和logging的差别在于logging可以选择输出到文件上中还是输出到控制台上)

另外最重要的是logging可以始终保留在代码中,通过调整log的级别来决定是否打印到文件或者控制台上甚至是Sentry(异常收集系统)上。

3. pdb(ipdb和bpdb)

这个调试非常重要,一定要掌握。这个调试可以跟踪程序的执行流程,观察问题的所在。

pdb提供了交互式环境,我们可以在代码中引入

import pdb

pdb.set_trace()

来让程序执行到这一行后进入pdb交互模式,进而可以像在Python shell中执行命令那样,获得到上下文所有变量的值或者更改变量的值。

关于pdb具体的使用可查看官方文档来学习。

ipdb常用的命令

h(help):帮助命令
s(step into):进入函数内部
n(next):执行下一行
b(break): b line_number打断点
cl(clear): 清除断点
c(continue): 一直执行到断点
r(return): 从当前函数返回
j(jump): j line_number,跳过代码片段,直接执行指定行号所在的代码
l(list): 列出上下文代码
a(argument): 列出传入函数所有的参数值
p/pp: print 和 pretty print打印出变量值
r(restart): 重启调试器
q(quit): 推出调试,清除所有信息

MySQL:第十八章——全文本搜索

18.1 理解全文本搜索

并非所有引擎都支持全文本搜索:MySQL支持几种基本的数据库引擎。最常使用的引擎为myisam和innodb.

现在基本上使用InnoDB。
 

在前面的第八章和第九章使用like和regexp来进行文本的搜索,虽然这些搜索机制比较有用,但是存在几个重要的限制。

第一个是性能方面:使用like和regexp来进行文本的搜索来进行搜索时,通常要匹配表中所有的行,但是有的数据是不需要匹配的,所以搜索起来比较耗时间。

第二个是明确控制:使用like和regexp来进行文本的搜索很难明确地控制匹配什么和不匹配什么。

第三个是智能化的结果:使用like和regexp来进行文本的搜索它们都不能提供一种智能化的选择结果的方法。

这些限制我们都可以用全文本搜索来解决

18.2 使用全文本搜索

为了进行全文本搜索,必须索引被搜索的列。而且要随着数据的改变不断地重新索引。

在索引之后,select可与match()和against()以实际执行搜索。

18.2.1启用全文本搜索支持

一般在创建表时启用全文本搜索。

fulltext全文本搜索,定义这个之后,MySQL会自动维护该索引。在增加,更新或删除行时,索引随之自动更新。

注意:应该首先导入所有数据,然后在修改表,定义fulltext。

6月12号学习记录

18.2.2 进行全文本搜索

在索引之后,使用两个函数match()和against()执行全文本搜索,

其中match()指定被搜索的列,against()指定要使用的搜索表达式。

代码示例:

select note_text

from productnotes

where match(note_text) against("rabbit");

解释说明:

此select语句检索单个列note_text。由于where子句,一个全文本搜索被执行。

match(note_text)指示MySQL针对指定的列进行搜索,against("rabbit")指定词rabbit作为搜索文本。

注意:搜索不区分大小写;

使用完整的match()说明:传递给match()的值必须与fulltext()定义中的相同。如果指定多个列,则必须列出它们(而且次序相同)。

使用like也可以但是返回的结果不会按照排好的顺序返回。

select note_text

from productnotes

where note_text

like "%rabbit%";

全文本搜索的一个重要的部分就是对结果排序。具有较高等级的行先返回。全文本搜索不需要加入order by子句。

下面的代码是演示排序的等级:

select note_text, match(note_text) against("rabbit")

as aaa

from productnotes;

这里在select而不是where子句中使用match()和against()。这使所有的行都被返回(因为没有where子句)。创建一个计算列别名为aaa,这个列包含全文本搜索出的等级值。

等级是怎么划分的?

等级是由行中词的数目唯一词的数目整个索引中词的总数、以及包含该词的行的数目计算出来的。

不包含改词的行等级为0,包含该词的行每行都有一个等级值,文本中词靠前的行的等级值比词靠后的等级值高。

18.2.3 使用查询扩展

查询扩展用来设法放宽所返回的全文本搜索范围的结果。

代码示例:

select note_text

from productnotes

where match(note_text) against("anvils" with query expansion);

with query expansion查询扩张

查询扩展极大地增加了返回的行数,但这样做也增加了你实际上并不想要的行的数目。

行越多越好:表中的行越多(这些行中的文本就越多),使用查询扩展返回的结果越好。

18.2.4 布尔文本搜索

MySQL支持全文本搜索的另一种形式,叫做布尔方式(boolean mode)。

利用这只查询方式可以提供如下细节:

要匹配的词

要排斥的词(如果某行包含这个词,则不返回改行,及时它包含其他指定的词也是这样)

排列提示(指定某些词比其他词更重要,更重要的词等级更高)

表达式分组

另外一些内容;

注意:即使没有fulltext索引也可以使用(但是性能会随着数据量的增加而降低)

代码实例:

select note_text

from productnotes

where match(note_text) against("heavy" in boolean mode);

检索包含heavy所有的行。

select note_text

from productnotes

where match(note_text) against("heavy -rope*" in boolean mode);

解释说明:

-rope* 明确的指示mysql排除包含rope*(任何以rope开头的词,包括ropes)的行。

- * 是两个布尔操作符,-排除一个词, * 是截断操作符(相当于一个用于结尾的通配符)。

重点知识:,

布尔操作符:+ 包含,词必须存在

- 排除与上面的相反

> 包含,而且增加等级值   

< 包含,减少等级值

()把词组成子表达式

~ 取消一个词的排序值

* 词尾的通配符

" " 定义一个短语

代码实例怎么使用这些布尔操作符:

1.select note_text

from productnotes

where match(note_text) against("+rabbit +bait" in boolean mode); 包含rabbit和bait的词的行。

2.select note_text

from productnotes

where match(note_text) against("rabbit bait" in boolean mode); 包含这两个词中的至少一个词的行。

3.select note_text

from productnotes

where match(note_text) against('"rabbit bait"' in boolean mode); 匹配搜索短语rabbit bait而不是两个词。

4.select note_text

from productnotes

where match(note_text) against(">rabbit <bait" in boolean mode); 增加前者的等级,降低后者的等级。

5.select note_text

from productnotes

where match(note_text) against("+safe +(<combination)" in boolean mode);包含safe 和combination,降低后者的等级。

18.2.5 全文本搜索的使用说明

1. 仅在MyISAM数据库引擎只支持全文本搜索。

2.忽略词中的单引号。

3.表中的行数少于三行,则全文本搜索不返回结果。

4.许多词出现的频率很高,搜索他们没有用处(返回的结果太多)。

5.在搜索全文本数据时,短词被忽略且从索引中排除。短词定义为具有三个或三个以下字符的词。

6.MySQL带有一个内建的非用词列表,这些词在索引全文本数据时总是被忽略。

7.不具有词分隔符的语言不能恰当地返回全文本搜索结果。

6月13号学习记录

MySQL——第十九章插入数据

19.1数据插入(insert)

19.2插入完整的行

INSERT INTO

customers——表名

(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) ——列名
 VALUES

(null, 'Pep E.LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', 'USA', null, null);——列值

insert语句一般不会产生输出。有些列是null值,比如cust_id,cust_contact, cust_email

cust_id是这个表的主键,所以即使是null值它也会自动赋予一个可用的值。

其他的列如果没有值就可以为null值。

一般不要使用没有明确给出列的列表的insert语句。

不管使用哪种那种insert语法,都必须给出values的正确数目。

列可以定义为允许的null值。

在表定义中给出默认值。

如果数据的检索是最重要的,可以在insert和into之间添加关键字low_priority(优先)。指示MySQL减低insert语句的优先级。

19.3插入多个行

使用多条select语句,一次提交他们,每条语句一个分号结束。

19.4插入检索出的数据

19.5小结

第二十章——更新和删除数据(update和delete)

20.1 更新数据

我为了更新(修改)表中的数据,可使用update语句。可采用两种方式使用update.

更新表中的特定行;更新表中的所有行。

不要省略where子句。在使用update使一定要注意,因为稍不注意,就会更新表中所有的行。

update语句由三部分组成:分别是:要更新的表,列名和它们的新值,确定要更新行的过滤数据。

update

customers 要更新的表名

set  用来将新值赋给被更新的列。

cust_email = 'elmer@fudd.com' 更新的列名和新值

where

cust_id = 10005;  要更新新行的过滤数据。

update语句总是要以更新的表的名字开始。

update语句以where子句结束,告诉MySQL更新哪一行。如果没有where子句,那么将会根据这一个电子邮件地址更新这个表中的所有的行。

更新多个列:

update

customers

set

cust_name = 'The Fudds',cust_email = 'elmer@fudd.com'  更新这两个列

where

cust_id = 10005;

在update语句中使用子查询。和之前的子查询差不多。

ignore关键字:就是在更新多个行的时候,出现了一个错误,那怎么办呢?

即使发生错误,也继续进行更新,可使用ignore关键字

update     ignore    customers   

为了删除某个列的值,可将这个列设置为null,

update customers set cust_email = null where cust_id = 10005;   

20.2 删除数据

为了从一个表中删除数据,可使用delete语句。

两种方式:从表中删除特定的行,从表中删除所有的行。

不要省略where子句,同update

delete from customers where cust_id = 10006;

删除表的内容而不是表

更快的删除:如果想要从表中删除所有的行,不要使用delete,可使用truncate table语句。它比delete速度更快。

20.3 更新和删除的指导原则

注意点:除非确实打算更新和删除每一行,否则绝对不要使用delete和update。

保证每个表中都有主键,尽可能像where子句那样使用它。

在使用这两个之前,先使用select进行测试,保证过滤的是正确的记录。

使用强制实施引用完整性的数据库,这样MySQL将不允许删除具有与其他表相关联的数据的行。

小心使用这两个

6月14号——学习记录

1.MySQL——创建和操作表

21.1——创建表create table

一般有两种创建表的方法

使用具有交互式创建和管理工具

 表也可以直接用MySQL语句操纵

21.1.1表创建基础

新表的名字,在关键字create table之后给出

表列的名字和定义,用逗号分隔

create table 表明

( 

    列名    数据类型  not null auto_increment,

....

primary key(主键列)

) engine=innodb;

21.1.2——使用null值(就是没有值或却值)

如果这个列是空值可以不写,反之如果这个列不是空值,那么必须写出这个列的值

如果这个列不是空值,但是你插入值的时候插入的是空值,则会返回错误。

null值是默认的,如果不指定not null,那么就为null值。

注意:不要把null值和空字符串混淆;

null只是没有值,他不是空字符串。空字符串也是一个值。

21.1.3——主键再介绍

主键值必须是唯一的。表中的每个行必须具有唯一的主键值。

primary key(主键值1,主键值2)

多个主键值需要用逗号分隔开。

主键中只能使用不允许null值的列,允许为null值的列不能作为唯一标识。

21.1.4 ——使用auto_increment

告诉MySQL本列每当增加一行时自动增量。自动对该列增量,给该列赋予下一个可用的值。

每个表只有一个该列,而且它必须被索引(使它成为主键)

select last_insert_id():此语句返回最后一个auto_increment值。

21.1.5——指定默认值

比如举个例子(只列出一个列值说明):quantity int  not null default 1,

在未给出数量的情况下使用数量1.

注意:不允许使用函数作为默认值,它只支持常量。

使用默认值而不是空值。

21.1.6——引擎类型

mysql有一个具体管理和处理数据的内部引擎。

如果你不写engine=innodb,则使用MySQL默认的myisam。

但是不是所用的语句都默认使用它。这就是为什么engine=语句非常重要的原因。

myisam和innodb的区别

第一:innodb支持事务处理,不支持全文本搜索。

第二:myisam支持全文本搜索,不支持事务处理。

这两种使用分场景使用:

比如:你想让数据库表支持事务处理,就用第一个innodb

相反让数据库表支持全文本搜索就有第二个。

一般如果不需要可靠的事务处理就使用myisam就行了。

外键不能跨引擎。这也是一个缺陷。即使用一个引擎的表不能引用具有使用不同引擎的表的外键。

21.2——更新表alter table

数据少的时候可以稍微更新,但是数据量大的时候不建议更新表(因为浪费时间)。

增加一个列:

代码:alter table vendors add vend_phone char(20);

删除一个列:alter table vendors drop column vend_phone;

alter table的一种常见的用途是定义外键。

重点:

alter table orderitems

add constraint fk_orderitems_orders

foreign key (order_num)

references orders (order_num);
 

21.3——删除表drop table

drop table 要删除的表名;

21.4——重命名表

rename table语句

rename table customers2 to customers;重命名一个表。要是多个表要用逗号分隔。

rename table backup_customer to customers, backup_vendors to vendors;

21.5——小结

6月15号学习记录

第二十二章——使用视图

22.1——视图

视图是虚拟的表。与包含的数据不一样,视图只包含使用时动态检索数据的查询。

看一个例子:之前的例子

select cust_name, cust_contact

from customers, orders, orderitems

where customers.cust_id = orders.cust_id

and orders.order_num = orderitems.order_num

and prod_id = "TNT2"; 

现在,假如可以把整个查询包装成一个名为productcustomers的虚拟表,则可以如下轻松地检索出相同的数据。

select cust_name, cust_contact

from productcustomers

where prod_id = "TNT2"; 

这就是视图的作用。productcustomers是一个视图,作为视图,它不应该包含表中应该有的任何列或数据,它只包含的是一个SQL查询(与上面用以正确联结表的相同的查询)。

22.1.1 为什么使用视图

第一:重用SQL语句。

第二:简化复杂的SQL操作。

第三:使用表的组成部分而不是整个表。

第四:保护数据(可以给用户授予表的特定部分的访问权限而不是整个表的访问权限)。

第五:更改数据格式和表示。

在视图创建之后,可以用与表基本相同的方式利用它们。可以对视图进行select操作,过滤和排序数据,将视图联结到其他视图或表。甚至能添加和更新数据。

视图仅仅是用来查看存储在别处的数据的一种设施。视图本身不包含数据,因此它们返回的数据是从其他表中检索出来的。在添加或更改这些表中的数据时,试图将返回改变过的数据。

创建多个视图或者嵌套多个视图,性能会下降,所以在使用大量视图前,应该进行测试。

22.1.2——视图的规则和限制

第一:视图和表一样必须唯一命名。

第二:视图的创建数目没用限制。

第三:为了创建视图,必须具有足够的访问权限。

第四:视图可以嵌套。

第五:order by 可以用在视图中。

第六:视图不能索引

第七:视图可以和表一起使用。

22.2 使用视图

第一:视图用create view语句来创建。

第二:使用show create view viewname;来查看创建的视图的语句。

第三:用drop删除视图,其语法为drop view viewname;

第四:更新视图时,可以先用drop再用create,也可以直接用create or replace view。

如果更新的视图不存在,则第二条语句会创建一个视图,如果更新的语句存在,则第二条更新语句会替换原有视图。

22.2.1——利用视图简化复杂的联结

视图的最常用的应用之一是隐藏复杂的SQL,这通常会涉及联结。

create view productcustomers as 创建视图语句

select cust_name, cust_contact, prod_id

from customers, orders, orderitems

where customers.cust_id = orders.cust_id

and orders.order_num = orderitems.order_num; 

利用创建好的视图检索数据

select cust_name, cust_contact

from productcustomers

where prod_id ="TNT2";

通过where子句从视图中检索特定的语句。

本章重点掌握:可以看出来,视图极大地简化了复杂的SQL语句的使用。利用视图,可一次性编写基础的SQL,然后哦根据需要多次使用。——创建可重用的视图。

22.2.2 用视图重新格式化检索出的数据。

视图的另一种常见的用途是重新格式化检索出的数据。

 create view vendorlocation as

select concat(vend_name, '(',vend_country, ')')

as vend_title

from vendors

order by vend_name;

select * from vendorlocation;
加入经常需要这个格式的结果,不必再每次需要时执行联结,创建一个视图每次需要它时使用它就行了。

22.2.3——用视图过滤不想要的数据。

视图对于应用普通的where子句也很有用。比如过滤没有电子邮箱的地址的客户。

create view customeremaillist as

select cust_id, cust_name, cust_email

from customers

where cust_email is not null;

where子句过滤了cust_email列中具有null值的那些行,使他们不被检索出来。

select * from customeremaillist;

22.2.4——使用视图与计算字段

视图对于简化字段的使用特别有用。

create view orderitemsexpand as

select order_num, prod_id, quantity, item_price,

quantity*item_price as expanded_price

from orderitems;

 select * from

orderitemsexpand

where order_num = 20005;

总结:可以看到视图的创建非常容易,而且很好使用。正确使用,视图可极大地简化复杂的数据处理。

22.2.5——更新视图

现在为止所有的视图都是和select语句使用的。

通常视图是可更新的(即,可以对它们使用insert,update和delete)。

更新一个视图将更新其基表(视图本身是没有数据的),如果你对视图增加或删除行,实际上是对其基表增加或删除行。

但是并非所用的视图都是可更新的。

以下是不能进行视图的更新:

分组(使用group by和having),联结,子查询,并,聚集函数(Min()、Count()、Sum()等);distinct;导出(计算)列。

换句话说,其实大部分例子中都是不能进行视图更新的。这听上去好像是一个严重的限制,但实际上不是,因为视图主要用于数据检索(这才是视图的重点)

一般应该将视图由于检索(select语句),而不用于更新(insert,update,delete)。

6月16号学习记录

第二十三章——使用存储过程

23.1——存储过程

存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。

可将其视为批文件,虽然他们的作用不仅限于批处理。

23.2 为什么要使用存储过程

理由:第一:简化复杂的操作;

第二:保证了数据的完整性;

第三:简化对变动的管理(安全性);

第四:提高性能——使用存储过程比使用单独地SQL语句要快。

总结:好处:简单、安全、高性能

缺陷:第一:存储过程的编写比基本SQL语句复杂,编写存储过程需要更高的技能,更丰富的经验。

第二:你可能没有创建存储过程的安全访问权限。可以使用但是不能创建。

23.3——使用存储过程

使用存储过程需要知道如何执行(运行)它们。

6月17号学习过程

23.3.1——执行存储过程

mysql称存储过程的执行为调用,因此MySQL执行存储过程的语句为call。

call接受存储过程的名字以及需要传递给它的任意参数。

代码例子:

call productpricing(@pricelow, @pricehight, @priceaverage);

其中名为productpricing的存储过程,计算并返回产品的最低、最高和平均价格。

23.3.2——创建存储过程

编写存储过程并不是微不足道的事情。

例子:一个返回产品平均价格的存储过程。

create procedure productpricing() #创建一个存储过程名为productpricing记住后面的括号不要忘了,如果存储过程接收参数,它们将在()中列举出来。此存储过程没有参数但是()仍然需要。

begin #开始

select avg(prod_price) 

as priceaverage

from products;

end;#结束

这条语句没有返回的结果。

解释:存储过程名称为 ——productpricing

begin和end语句用来限定存储过程体,过程体本身仅是一个简单的select语句。使用了一个AVG()函数。

这条语句并未调用存储过程,所以没有返回的结果。只是为了以后使用而创建它。

下面是对——MySQL命令行客户机的分隔符

delimiter // 告诉命令行实用程序使用//作为新的语句结束分隔符。

create procedure productpricing()

begin

select avg(prod_price) 

as priceaverage

from products;

end //

delimiter; 最后为了恢复原来的语句分隔符使用delimiter;

那么怎么使用刚才创建的存储过程名为productpricing呢?

call productpricing(); #执行刚才创建的存储过程并显示返回的结果。

因为存储过程实际上是一种函数,所以存储过程名后需要有()符号,即使不传递参数也需要。

23.3.3——删除存储过程

存储过程在创建之后,被保存在服务器上以供使用,直到被删除。

drop procedure productpricing; 注意存储过程后面没有()

23.3.4——使用参数

productpricing只是一个简单的存储过程,它简单地显示select语句的结果。一般,存储过程并不显示结果,而是把结果返回给你指定的变量。

变量:用来临时存储数据。

以下是productpricing的修改版本(如果不先删除此存储过程,则不能再次创建它);

create procedure productpricing

(out pl decimal(8,2),

out ph decimal(8,2),

out pa decimal(8,2))

begin

select min(prod_price) into pl from products;

select max(prod_price) into ph from products;

select avg(prod_price) into pa from products;

end;

解释代码:此存储过程接受3个参数,pl ph pa代表产品最低价格,最高价格,平均价格

每个参数必须具有指定的类型。

关键字out指出相应的参数用来从存储过程传出一个值(返回给调用者)。

mysql支持in(传递给存储过程),out(此存储过程传出),和inout(对存储过程传入和传出)类型的参数。

存储过程的代码位于begin和end语句内,它们是一系列select语句。用来检索值,然后保存到相应的变量(通过指定into关键字)。

存储过程的参数允许的数据类型与表中使用的数据类型相同。

由于存储过程中有三个变量,所以在调用时,必须指定三个参数。

call productpricing(

@pricelow,

@pricehight,

@priceaverage

);

call语句要给出三个参数,它们是存储过程将保存结果的三个变量的名字。

注意:所有MySQL变量都必须以@开始。

在调用时,这条语句并不显示任何数据。它返回以后可以显示(或在其他处理中使用)的变量。

想要显示数据则执行以下语句:

select @pricelow,@pricehight, @priceaverage;

再看一个例子——这次使用in和out参数。

ordertotal接受订单号并返回该订单的合计:

create procedure ordertotal

(in onumber int,

out ototal decimal(8,2))

begin

select sum(item_price*quantity) from orderitems

where order_num = onumber into ototal;

end;
 

call ordertotal(20005, @total);得到20005的订单合计

call ordertotal(20009, @total);得到20009的订单合计

select @total;

6月24号学习记录

23.3.5 ——建立智能存储过程(难点)

目前为止见到的所用的存储过程基本上都是封装的MySQL的简单select语句查询。

虽然他们都是有效的存储过程例子,但他们所能完成的工作你直接用这些被封装的语句就能完成。

(如果说它们还能带来更多的东西,那就是使事情更复杂。)

只有在存储过程内包含业务逻辑规则和智能处理时,它们的威力才真正显示出来。

例子:需求——需要获得与以前一样的订单合计,但是需要对合计增加营业税,不过只针对某些顾客(或许是你所在州中那些顾客)。那么,你需要做下面几件事情。

1. 获得合计

2.把营业税有条件地添加到合计

3.返回合计(带或不带税)

代码实例:(前提是你要先删除之前的ordertotal)

                                 -- Name: ordertotal  --代表注释在前面放置,后面就是注释的内容,
                           ->  -- Parameters: onumber = order number 在存储过程增加时,这样做特别重要。
                           ->  --            taxable = 0 if not taxable, 1 if taxable
                           ->  --              ototal = order total variable
                           ->  create procedure ordertotal(
                           ->  in onumber int,
                           ->  in taxable boolean, 添加了另外一个参数他是一个布尔值(如果要增加税则为真,否则为假。)
                           ->  out ototal decimal(8,2)
                           ->  ) 
                           ->  begin
                           ->  -- Declare variable for total 在存储过程体中,用declare语句定义了两个局部变量。declare要求指定变量名和数据类型。
                           -> declare total decimal(8,2);
                           -> -- Declare tax percentage
                           -> declare taxrate int default 6;他也支持可选的默认值——taxrate的默认值被设置为6%
                           -> -- Get the order total
                           -> select sum(item_price*quantity)
                           -> from orderitems
                           -> where order_num = onumber
                           -> into total; select语句以及改变,其结果存储到total局部变量而不是ototal
                           -> -- Is this taxable?
                           -> if taxable then if语句检查taxable是否为真,如果为真,则用另一select语句增加营业税到局部变量total。
                           -> -- Yes, so add taxable to the total
                           -> select total+(total/100*taxrate) into total;
                           -> end if;
                           -> -- And finally, save to out variable 最后,用另一select语句将total(它增加或许不增加营业税)保存到ototal。
                           -> select total into ototal;
                           -> end;

call ordertotal(20005,0, @total); 调用存储过程,这里是三个参数。

select @total; 查询存储过程的数据

call ordertotal(20005,1, @total);

select @total;

comment 关键字,不是必需的,但如果给出,将在 show procedure status的结果中显示。

总结:这显然是一个更高级,功能更强的存储过程。

这个例子中还给出了MySQL中的if语句的基本用法,if语句还支持elseif和else子句(前者还使用then子句,后者不使用)。

23.3.6——检查存储过程

为显示用来创建一个存储过程的create语句,使用show create procedure语句。

show create procedure ordertotal;

为了获得包括何时、由谁创建等详细信息的存储过程列表,使用

show procedure status 列出所有存储过程。

为限制其输出,可使用like制定一个过滤模式,例如:

show procedure status like 'ordertotal';

6月25号学习记录

第二十四章——使用游标

24.1——游标(cursor)

游标是一个存储在MySQL服务器上的数据库查询,它不是一条select语句,而是被该语句检索出来的结果集。

在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。

有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因。

游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或作出更改。

注意:MySQL游标只能用于存储过程(和函数)。

24.2 ——使用游标

使用游标涉及几个明确的步骤。

1.在能够使用游标前,必须声明(定义它)

这个过程实际上没有检索数据,只是定义要使用的select语句。

2.一旦声明后,必须打开游标以供使用。

这个过程用前面定义的select语句把数据实际检索出来。

3.对于填有数据的游标,根据需要取出(检索)各行。

4.在结束游标使用时,必须关闭游标。

在声明游标后,可根据需要频繁地打开和关闭游标。

在游标打开后,可根据需要频繁地执行取操作。

24.2.1——创建游标

游标用declare语句创建。

declare命名游标,并定义相应的select语句。

根据需要带where和其他子句。

例子——下面的语句定义了名为ordernumbers的游标,

使用了可以检索所有订单的select语句。

create procedure processorders()
                           -> begin
                           ->     declare ordernumbers cursor
                           ->     for
                           ->     select order_num from orders;
                           ->     end;
解释一下:这个存储过程并没有做很多事情,declare语句用来定义和命名游标,这里为ordernumbers。存储过程处理完成后,游标就消失了(因为它局限于存储过程)。

24.2.2——打开和关闭游标

打开游标:open ordernumbers;

关闭游标:close ordernumbers;

close 释放游标使用的所有内部内存和资源,因此在每个游标不再需要时都应该关闭。

在一个游标关闭后,如果没有重新打开,则不能使用它。

但是,使用声明过的游标不需要再次声明,用open语句打开就可以了。

隐含关闭:如果你不明确关闭游标,MySQL将会在到达end语句时自动关闭它。

修改版本:

create procedure processorders()
                           -> begin
                           -> -- Declare the cursor
                           -> declare ordernumbers cursor
                           -> for
                           -> select order_num from orders;
                           -> -- Open the cursor
                           -> open ordernumbers;
                           -> -- Close the cursor
                           -> close ordernumbers;
                           -> end;
这个存储过程声明、打开和关闭一个游标。

但对检索出的数据什么也没做。

24.2.3——使用游标数据

在一个游标被打开后,可以使用fetch语句分别访问它的每一行。

fetch指定检索什么数据(所需的列),检索出来的数据存储在什么地方。

他还向前移动游标中的内部行指针,使下一条fetch语句检索下一行(不重复读取同一行)。

代码如下:

create procedure processorders()
                           -> begin
                           -> -- Declare local variables
                           -> declare done boolean default 0; (default 0假,不结束)定义变量done。
                           -> declare o int;
                           -> -- Declare the cursor
                           -> declare ordernumbers cursor
                           -> for
                           -> select order_num from orders;
                           -> -- Declare continue handler  //continue handler,他是在条件出现时被执行的代码。这里,他指出当sqlstate '02000' 出现时,set done=1.    sqlstate '02000'是一个未找到的条件,当repeat由于没有更多的行供循环而不能继续时,出现这个条件。
                           -> declare continue handler for sqlstate '02000' set done=1; 结束时被设置为真done=1
                           -> -- open the cursor
                           -> open ordernumbers;
                           -> -- loop throgh all rows
                           -> repeat
                           -> -- get order number
                           -> fetch ordernumbers into o;    使用fetch检索当前order_num到变量o中。但是这个fetch是在repeat内,因此它反复执行直到done为真。( until done end repeat;
                           -> -- end of loop
                           -> until done end repeat;
                           -> -- Close the cursor
                           -> close ordernumbers;
                           -> end;

注意:declare定义的次序:

declare定义局部变量必须在任意游标或句柄之前定义。

句柄必须在游标之后定义。

如果调用这个存储过程,它将定义几个变量和一个continue handler, 定义并打开一个游标,重复读取所有的行,然后关闭游标。

通常repeat语句的语法使它更适合于对游标进行循环。

7月1号学习进度

为了把这些内容组织起来,下面给出我们的游标存储过程样例的更进一步修改,这次对取出的数据进行某种实际的操作:

create procedure processorders()
                       begin
                         -- Declare local variable
                          declare done boolean default 0;
                           declare o int;
                            declare t decimal(8,2); //我们增加了另一个名为t的变量(存储每个订单的合计)
                           -- Declare the cursor
                          declare ordernumbers cursor
                            for
                           select order_num from orders;
                            -- Declare continue handler
                            declare continue handler for sqlstate '02000' set done=1;
                            -- Create a table to store the results
                            create table if not exists ordertotals //首先这个ordertotals这个新表你要先创建好。这个表将保存存储过程生成的结果。
                               (order_num int, total decimal(8,2));
                            -- Open the cursor
                             open ordernumbers;

                             -- Loop through all rows
                           repeat
                           -- Get order number
                            fetch ordernumbers into o; //fetch像以前一样取每个order_num,然后用call执行另一个存储过程来计算每个订单的带税的合计(结果存储到t)。
                            -- Get the total for this order
                          call ordertotal(o, 1, t);
                           -- Insert order and total into ordertotals
                          insert into ordertotals(order_num, total) //最后,用insert保存每个订单的订单号和合计。
                         values(o, t);
                            -- End of loop
                             until done end repeat;
                         -- Close the cursor
                            close ordernumbers;
                           end;

此存储过程不返回数据,但它能够创建和填充另一个表,用一条简单的select语句查看该表。

select * from ordertotals;

这样我们就得到了存储过程、游标、逐行处理以及存储过程调用

其他存储过程的一个完整的工作样例。

第二十五章——触发器

25.1——触发器

如果你想要某条语句(或某些语句)在事件发生时自动执行,怎么办呢?

例如:

每当增加一个顾客到某个数据库表时,都检查其电话号码格式是否正确,州的缩写是否为大写。

每当订购一个产品时,都从库存数量中减去订购的数量。

无论何时删除一行,都在某个存档表中保留一个副本。

这些例子的共同之处是它们都需要在某个表发生更改时自动处理——这确切地说就是触发器。

触发器是MySQL响应以下任意语句而自动执行的一条MySQL语句。(或位于begin和end语句之间的一组语句):

delete

insert

update

其它MySQL语句不支持触发器。

25.2——创建触发器

在创建触发器时,需要给出4条信息:

1.唯一的触发器名;

2.触发器关联的表;

3.触发器应该响应的活动(delete、insert或update);

4.触发器何时执行(处理之前或之后)。

注意:保持每个数据库的触发器名唯一。

触发器用 create trigger语句创建。
 

学习MySQL出现问题Not allowed to return a result set from a trigger

情况描述:

按书本代码输入

CREATE TRIGGER newproduct AFTER INSERT ON products

FOR EACH ROW SELECT 'Product added'//

出现错误Not allowed to return a result set from a trigger

查找原因是:MYSQL5以后,不允许触发器返回任何结果,因此使用into @变量名,将结果赋值到变量中,用select调用即可

修改为

CREATE TRIGGER newproduct AFTER INSERT ON products

FOR EACH ROW SELECT 'Product added' INTO @asd//  # 变量名用@引导    # DELIMITER //  将语句结束符号;修改为//

成功!

看看变量里有啥?

SELECT @asd//

啥也没,还没插入操作,所以显示没有值!

在products表插入一行数据:     友情提示,插入数据时注意每列的数据类型,且保证主键列的数据不重复

再运行SELECT @asd//

现在有了!

 create trigger——用来创建名为newproduct的新触发器。

after insert——触发器可在一个操作发生之前或或之后执行。

所以此触发器将在insert语句成功执行后执行。

这个触发器还指定for each row,因此代码对每个插入行执行。

在这个例子中,文本Product added将对每个插入的行显示一次。

为了测试这个触发器,使用insert语句添加一行或多行到products中,你将看到对每个成功的插入,显示Product added消息。

注意:触发器仅支持表。只有表才支持触发器,视图不支持(临时表也不支持)。

触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。

因此,每个表最多支持6个触发器(每条insert、update和delete的之前和之后)。

单一触发器不能与多个事件或多个表关联,所以,如果你需要一个对insert和update操作执行的触发器,则应该定义两个触发器。

注意:触发器失败——如果before触发器失败,则MySQL将不执行请求的操作。

此外,如果before触发器或语句本身失败,MySQL将不执行after触发器(如果有的话)。

25.3——删除触发器

drop trigger newproduct;

触发器不能更新或覆盖,为了修改一个触发器,必须先删除它,然后再重新创建。

25.4——使用触发器

所支持的每种触发器类型以及它们的差别。

7月2号——学习记录

25.4.1——insert触发器

insert触发器在insert语句执行之前或之后执行。

需要知道以下几点:

1.在insert触发器代码内,可引用一个名为new的虚拟表,访问被插入的行。

2.在before insert触发器中,new中的值也可以被更新(允许更改被插入的值)。

3.对于auto_increment列,new在insert执行之前包含0,在insert执行之后包含新的自动生成值。

例子:

auto_increment列具有MySQL自动赋予的值。

create trigger neworder after insert on orders
                           -> for each row select new.order_num into @on;

代码说明:此代码创建一个名为neworder的触发器。

按照after insert on orders执行。

在插入一个新订单到orders表时,MySQL生成一个新订单号并保存到order_num中。

触发器从new.order_num取得这个值并返回它。

此触发器必须按照after insert执行,因为在before insert语句执行之前,

新的order_num还没有生成。

对于orders的每次插入使用这个触发器将总是返回新的订单号。

为了测试这个触发器,试着插入一下新行,

insert into orders(order_date, cust_id)
                           -> values(now(), 10001);

select * from orders;
+-----------+---------------------+---------+
| order_num | order_date          | cust_id |
+-----------+---------------------+---------+
| 20010     | 2022-07-02 14:48:02 | 10001   |
+-----------+---------------------+---------+
 

orders包含3个列。order_date和cust_id必须给出,

order_num由MySQL自动生成,而现在order_num还自动被返回。

注意:before或after,通常,将before用于数据验证和净化(目的是保证插入表中的数据确实是需要的数据)。也适用于update触发器。

25.4.2——delete触发器

delete触发器在delete语句执行之前或之后执行。

需要知道以下两点:

1.在delete触发器代码内,你可以引用一个名为old的虚拟表,访问被删除的行。

2.old中的值全都是只读的,不能更新。

例子:演示使用old保存将要被删除的行到一个存档表中:

create trigger deleteorder before delete on orders
                           -> for each row 
                           -> begin
                           -> insert into archive_orders(order_num, order_date, cust_id)
                           -> values(old.order_num, old.order_date, old.cust_id);
                           -> end;
在任意订单被删除前将执行此触发器。它使用一条insert语句将old中的值(要被删除的订单)保存到一个名为archive_orders的存档表中(为实际使用这个例子,你需要用与orders相同的列创建一个名为archive_orders的表)。

使用before delete触发器的优点(相对于after delete触发器来说)为:如果由于某种原因,订单不能存档,delete本身将被放弃。

多语句触发器:触发器deleteorder使用begin和end语句标记触发器体。

使用begin end块的好处是触发器能容纳多条SQL语句。(在begin end块中一条挨着一条)。

25.4.3——update触发器

update触发器在update语句执行之前或之后执行。

需要知道以下几点:

1.在update触发器代码中,你可以引用一个名为old的虚拟表访问以前(update语句前)的值,引用一个名为new的虚拟表访问新更新的值。

2. 在before update触发器中,new中的值可能也被更新(允许更改将要用于update语句中的值);

3.old中的值全都是只读的,不能更新。

例子:保证州名缩写总是大写(不管update语句中给出的是大写还是小写):

create trigger updatevendor before update on vendors
                           -> for each row set new.vend_state = upper(new.vend_state);

任何数据净化都需要在update语句之前进行,就像这个例子一样。每次更新一个行时,

new.vend_state中的值(将用来更新表行的值)都用upper(new.vend_state)替换。

25.4.4——关于触发器的进一步介绍

再介绍一些使用触发器时需要记住的重点。

1.创建触发器可能需要特殊的安全访问权限,但是触发器的执行是自动的。如果insert、delete、update语句能够执行,则相关的触发器也能执行。

2.应该用触发器来保证数据的一致性(大小写、格式等)。

3.触发器的一种非常有意义的使用是创建审计跟踪。

使用触发器把更改记录到另一个表非常容易。

4.遗憾的是,MySQL触发器不支持call语句。

这表示不能从触发器内调用存储过程。所需的存储过程代码需要复制到触发器内。

7月3号学习记录

第二十六章——管理事务处理

什么是事务处理?以及如何利用commit和rollback语句来管理事务处理

26.1——事务处理

并非所有引擎都支持事务处理。

MySQL中最常用的数据引擎是——MyISAM 和innodb。

前者不支持明确的事务处理,而后者支持。

这就是为什么本书中使用的样例表被创建来使用innodb而不是更经常使用的MyISAM的原因。

如果你的应用中需要事务处理功能,则一定要使用正确的引擎类型。

事务处理(transaction processing):可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。

7月4号——学习记录

例子:给系统添加订单的过程。

1.检查数据库中是否存在响应的客户(从customers表查询),如果不存在,添加客户。

2.检索客户的ID。

3.添加一行到orders表,把它与客户ID关联。

4.检索orders表中赋予的新订单ID。

5.对于订购的的每个物品在orderitems表中添加一行,通过检索出来的ID把它与orders表关联(以及通过产品ID与product表关联)。

假如现在由于某种数据库故障阻止了这个过程的完成。数据库中的数据会出现什么情况?

如果故障发生在添加客户之后,orders表添加之前,不会有什么问题。

某些客户没有订单是完全OK的。再重新执行此过程,所插入的客户记录将被检索和使用。

可以有效地从出故障的地方开始执行此过程。

但是,如果故障发生在orders行添加之后,orderitems行添加之前,怎么办?

数据库中有个一个空订单。如果系统在添加orderitems行之中出现故障。结果是数据库中存在不完整的订单,而且你还不知道。

这个问题就需要事务处理来解决了。

事务处理是一种机制,用来管理必须成批执行的MySQL操作,以保证数据库不包含不完整的操作结果。

利用事务处理,可以保证一组操作不会中途停止,它们或者作为整体执行,或者完全不执行。

如果没有错误发生,整组语句提交给(写到)数据库表。

如果发生错误,则进行回退,已恢复数据库到某个已知且安全的状态。

例子:

1.检查数据库中是否存在响应的客户,如果不存在,添加客户。

2.提交客户的信息。

3.检索客户的ID。

4.添加一行到orders表。

5.如果在添加行到orders表时出现故障,回退。

6.检索orders表中赋予的新订单ID。

7.对于订购的每项物品,添加新行到orderitems表。

8.如果在添加新行到orderitems时出现故障,回退所有的添加的orderitems行和orders行。

9.提交订单信息。

有关事务的几个术语:

事务(transaction):指一组SQL语句。

回退(rollback):指撤销指定SQL语句的过程。

提交(commit):指将未存储的SQL语句结果写入数据库表。

保留点(savepoint):指事务处理中设置的临时占位符,你可以对它发布回退(与回退整个事务处理不同)。

26.2——控制事务处理

管理事务处理的关键在于将SQL语句分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退。

MySQL使用下面的语句来标识事务的开始:

start transaction;

26.2.1——使用rollback

MySQL的rollback命令用来回退(撤销)MySQL语句,

例子:

select * from ordertotals;//首先执行一条select以显示该表不为空。
                         start transaction; //然后开始一个事务处理
                          delete from ordertotals;//用一条delete语句删除表中的所有行
                            select * from ordertotals;//另一条select语句验证表确实为空
                         rollback;//这时用一条rollback语句回退start transaction之后的所有语句。
                         select * from ordertotals; //最后一条select语句显示该表不为空

显然,rollback只能在一个事务处理内使用(在执行一条select transaction命令之后)。

哪些语句可以回退

事务处理用来管理insert、update、delete语句。

其他的语句就不用管了。

26.2.2——使用commit

一般的MySQL语句都是直接针对数据库表执行和编写的。这就是所谓的隐含提交,

即提交(写或保存)操作是自动进行的。

但是在事务处理块中,提交不会隐含地进行。为进行明确的提交,使用commit语句。 

在这个例子中,从系统中完全删除订单20010.

涉及到两个表,所以使用事务处理块来保证订单不被部分删除。

如果第一条delete起作用,但第二条失败,则delete不会提交(实际上,它是被自动撤销的)。

注意:当commit或rollback语句执行后,事务会自动关闭(将来的更改会隐含提交)。

26.2.3——使用保留点

简单的rollback和commit语句就可以写入或撤销整个事务处理。

但是,只是对简单的事务处理才能这样做,更复杂的事务处理可能需要部分提交或回退。

例如前面描述的添加订单的过程为一个事务处理。

如果发生错误,只需要返回到添加orders行之前即可,不需要回退到customers表(如果存在的话)。

为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符。

这样,如果需要回退,可以回退到某个占位符。

这些占位符称为保留点。为了创占位符,可如下使用

savepoint delete1;

上面的错误解决方法: 

您必须使用 start transaction; 而不是 begin 来启动存储过程中的事务。

此外,您可能需要将 savepoint 语句移到 declare之后(取决于您放置 start transaction 的位置)

 rollback to point1;——回退到保留点

注意:保留点越多越好:可以在MySQL代码只设置任意多的保留点,越多越好。

保留点越多,你就越能按自己的意愿进行回退。

释放保留点:保留点在事务处理完成(执行一条rollback或commit)后自动释放。

26.2.4——更改默认的提交行为

任何时候你执行一条MySQL语句,该语句实际上都是针对表执行的,而且所做的的更改立即生效。

为指示MySQL不自动提交更改,需要使用以下语句:

set autocommit = 0;

autocommit 标志决定是否自动提交更改,不管有没有commit语句。设置autocommit = 0(假)指示MySQL不自动提交更改(直到autocommit被设置为真为止)。

autocommit标志是针对每个连接而不是服务器的。

7月6号——学习记录

第二十七章——全球化和本地化

MySQL处理不同字符集和语言的基础知识

27.1——字符集和校对顺序

数据库表被用来存储和检索数据。不同的语言和字符集需要以不同的方式存储和检索。

MySQL需要适应不同的字符集,适应不同的排序和检索数据的方法。

术语:

字符集:字母和符号的集合。

编码:某个字符集成员的内部表示。

校对:规定字符如何比较的指令。

27.2——使用字符集和校对顺序

MySQL支持众多的字符集。为查看所支持的字符集完整列表,使用以下语句:(图片我只截取了一部分作为参考)

show character set; 

这条语句显示所有可用的字符集以及每个字符集的描述和默认校对。

为了查看所支持的校对的完整列表,使用以下语句:(图片我只截取了一部分作为参考)

show collation;

 这条语句显示所有可用的校对,以及它们适用的字符集。可以看到有的字符集具有不止一种校对。

例如:latin1对不同的语言有几种校对,而且许多校对出现两次,一次区分大小写(由_cs表示),一次不区分大小写(由_ci表示)。

通常系统管理在安装时定义一个默认的字符集和校对。此外,也可 以在创建数据库时,指定默认的字符集和校对。为了确定所用的字符集 和校对,可以使用以下语句:

show variables like 'character%';
show variables like 'collation%';

为了给表指定字符集和校对,可使用带子句的create table:

代码:

 create table mytable
                           -> (
                           -> columnn1 int,
                           -> columnn2 varchar(10)
                           -> ) default character set hebrew
                           -> collate hebrew_general_ci
                           -> engine=innodb;
此语句创建一个包含两列的表,并且指定一个字符集和一个校 对顺序。

这个例子中指定了character set和collate两者。

一般,MySQL如下确定使用什么样的字符集和校对。

1.如果指定character set和collate两者,则使用这些值。
2.如果只指定character set,则使用此字符集及其默认的校对(如show character set的结果中所示)。
3.如果既不指定character set,也不指定collate,则使用数据库默认。

create table mytable
                           -> (
                           ->  columnn1 int,
                           ->  columnn2 varchar(10),
                           ->  columnn3 varchar(10) character set latin1 collate
                           -> latin1_general_ci
                           -> ) default character set hebrew
                           -> collate hebrew_general_ci;

这里对整个表以及一个特定的列指定了character set和 collate。

select * from customers
                           -> order by lastname, firstname 
                           -> collate latin1_general_cs;

此select使用collate指定一个备用的校对顺序(在这个例子 中,为区分大小写的校对)。这显然将会影响到结果排序的次序。

注意:

1.临时区分大小写 :上面的select语句演示了在通常不区分大 小写的表上进行区分大小写搜索的一种技术。当然,反过来 也是可以的。

2.select的其他collate子句 除了这里看到的在order by子句中使用以外,collate还可以用于group by、having、聚集函数、别名等。

3.字符串可以在字符集之间进行转换,使用Convert()函数(了解)

7月7号——学习记录

第二十八章——安全管理

数据库服务器通常包含关键的数据,确保这些数据的安全和完整性需要利用访问控制。

MySQL的访问控制和用户管理。

28.1——访问控制

MySQL服务器的安全基础是:用户应该对他们需要的数据具有适当的访问权,既不能多也不能少。用户不能对过多的数据具有过多的访问权。

考虑以下内容:

1.多数用户只需要对表进行读和写,但少数用户甚至需要能创建和删除表。

2.某些用户需要读表,但可能不需要更新表;

3.你可能想允许用户添加数据,但不允许他们数据。

4.某些用户(管理员)可能需要处理用户账号的权限,但多数用户不需要。

5.你可能想让用户通过存储过程访问数据,但不允许他们直接访问数据。

6.你可能想根据用户登录的地点限制对某些功能的访问。

这些例子需要给用户提供他们所需的访问权,且仅提供他们所需的访问权,这就是所谓的访问控制。管理访问控制需要创建和管理用户账号。

使用MySQL Administrator —— MySQL Administrato提供了一个图形用户界面,可用来管理用户及账号权限。 MySQL Administrator在内部利用本章介绍的语句,使你能交互地,方便地管理访问控制。

注意以下几点:

1.在现实世界 的日常工作中,决不能使用root。应该创建一系列的账号,有的用于管 理,有的供用户使用,有的供开发人员使用,等等。

2.防止无意的错误

3.不要使用root,不应 该在日常的MySQL操作中使用root。

28.2——管理用户

MySQL用户账号和信息存储在名为mysql的MySQL数据库中。一般 不需要直接访问mysql数据库和表(你稍后会明白这一点),但有时需要直接访问。

需要直接访问它的时机之一是在需要获得所有用户账号列表,为此,可使用以下代码:

分析:mysql数据库有一个名为user的表,它包含所有用户账号。

user表有一个名为user的列,它存储用户登录名。新安装的服务器 可能只有一个用户(如这里所示),过去建立的服务器可能具有很多用户

用多个客户机进行试验:

试验对用户账号和权限进行更改的 最好办法是打开多个数据库客户机(如mysql命令行实用程序的 多个副本),一个作为管理登录,其他作为被测试的用户登录。

28.2.1——创建用户账号

为了创建一个新用户账号,使用create user语句。

create user ben identified by 'p@$$w0rd';

在创建用户账号时不一定需要口令,不过这个例子用 identified by 'p@$$w0rd' 给出一个口令。

如果你再次列出用户账号,将会在输出中看到新账号。

select user from `user`;

+------------------+
| user             |
+------------------+
| ben              |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
+------------------+

指定散列口令: 为了作为散列值指定口 令,使用identified by password。

重命名一个用户账号,使用

rename user ben to bforta;

select user from `user`;
+------------------+
| user             |
+------------------+
| bforta           |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
+------------------+
28.2.2——删除用户账号

为了删除一个用户账号(以及相关的权限),使用drop user语句。

drop user bforta;

28.2.3——设置访问权限

在创建用户账号后,必须接着分配访问权限。

新创建的用户账号没有访问权限。

他们能登录MySQL,但不能看到数据,不能执行任何数据库操作。

为看到赋予用户账号的权限,使用show grants for,

show grants for bforta;
+------------------------------------+
| Grants for bforta@%                |
+------------------------------------+
| GRANT USAGE ON *.* TO `bforta`@`%` |

输出结果显示用户bforta有一个权限USAGE ON *.*。

usage表示根本没有权限,表示在任意数据库和任意表上对任何东西没有权限。

注意:用户定义为user@host, MySQL的权限用用户名和主机名结合定义。

如果不指定主机名,则默认的主机名%(授予用户访问权限而不管主机名)。

为设置权限,使用grant语句。grant要求你至少给出以下信息:

1.要授予的权限;

2.被授予访问权限的数据库或表;

3.用户名;

以下例子给出grant的用法:

grant select on crashcource.* to bforta;

分析:grant允许用户在crashcource.*(crashcource 数据库的所有表)上使用select。

通过只授予select访问权限,用户bforta对crashcource数据库中的所有数据具有只读访问权限。

 show grants for bforta;

+-------------------------------------------------+
| Grants for bforta@%                             |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `bforta`@`%`              |
| GRANT SELECT ON `crashcource`.* TO `bforta`@`%` |
+-------------------------------------------------+

grant的反操作为revoke撤销,用它来撤销特定的权限。

revoke select on crashcource.* from bforta;

这条revoke语句取消刚赋予用户bforta的select访问权限。被撤销的访问权限必须存在,否则会出错。

grant和revoke可在几个层次上控制访问权限;

整个服务器:使用grant all 和 revoke all

整个数据库:使用on database.*

特定的表,使用ondatabase.table;

特定的列;

特定的存储过程;

简化多次授权——可通过列出各权限并用逗号分隔,将多条 grant语句串在一起,如下所示:

grant select, insert on crashcource.* to bforta;

28.2.4——更改口令

set password语句。

新口令必须加密:

set password for bforta = password('n3w p@$$w0rd');

set password更新用户口令。新口令必须传递到password()函数进行加密。

set password还可以用来设置你自己的口令。

set password = password('n3w p@$$w0rd');

在不指定用户名时,set password更新当前登录用户的口令。

7月8号——学习记录

第二十九章——数据库维护

29.1——备份数据

MySQL的数据也必须经常备份。MySQL数据库是基于磁盘的文件,普通的备份系统和例程就能备份MySQL的数据。但是,普通的文件副本备份不一定总是有效。

1.使用命令行实用程序mysqldump转储所有数据库内容到某个外部 文件。

在进行常规备份前这个实用程序应该正常运行,以便能正确地备份转储文件。

2.可用命令行实用程序mysqlhotcopy从一个数据库复制所有数据 (并非所有数据库引擎都支持这个实用程序)。

3.可以使用MySQL的backup tableselect into outfile转储所 有数据到某个外部文件。

这两条语句都接受将要创建的系统文件名,此系统文件必须不存在,否则会出错。数据可以用restore table来复原。

首先刷新未写数据——为了保证所有数据被写到磁盘(包括索引数据),可能需要在进行备份前使用flush tables语句。

29.2——进行数据库维护

MySQL提供了一系列的语句,可以(应该)用来保证数据库正确和 正常运行。 

analyze table,用来检查表键是否正确。

 analyze table orders;
+-------------------+---------+----------+----------+
| Table             | Op      | Msg_type | Msg_text |
+-------------------+---------+----------+----------+
| my_firstdb.orders | analyze | status   | OK       |
+-------------------+---------+----------+----------+

check table用来针对许多问题对表进行检查。在MyISAM表上还对索引进行检查。check table支持一系列的用于MyISAM表的方式。

changed检查自最后一次检查以来改动过的表.

extended执行最 彻底的检查,

fast只检查未正常关闭的表,

medium检查所有被删 除的链接并进行键检验,

quick只进行快速扫描。如下所示,check table发现和修复问题:
check table orders, orderitems;
+-----------------------+-------+----------+----------+
| Table                 | Op    | Msg_type | Msg_text |
+-----------------------+-------+----------+----------+
| my_firstdb.orders     | check | status   | OK       |
| my_firstdb.orderitems | check | status   | OK       |
+-----------------------+-------+----------+----------+

如果MyISAM表访问产生不正确和不一致的结果,可能需要用 repair table来修复相应的表。

这条语句不应该经常使用,如果需要经常使用,可能会有更大的问题要解决。

如果从一个表中删除大量数据,应该使用optimize(优化) table来收回

29.3——诊断启动问题

服务器启动问题通常在对MySQL配置或服务器本身进行更改时出现。MySQL在这个问题发生时报告错误,但由于多数MySQL服务器是作为系统进程或服务自动启动的,这些消息可能看不到。

在排除系统启动问题时,首先应该尽量用手动启动服务器。MySQL 服务器自身通过在命令行上执行mysqld启动。下面是几个重要的mysqld 命令行选项:

--help显示帮助——一个选项列表;
 --safe-mode装载减去某些最佳配置的服务器;
--verbose显示全文本消息(为获得更详细的帮助消息与--help联合使用);
--version显示版本信息然后退出。

几个另外的命令行选项(与日志文件的使用有关)在下一节列出。

29.4 查看日志文件

MySQL维护管理员依赖的一系列日志文件。主要的日志文件有以下几种。

错误日志。它包含启动和关闭问题以及任意关键错误的细节。此 日志通常名为hostname.err,位于data目录中。

此日志名可用 --log-error命令行选项更改。

查询日志。它记录所有MySQL活动,在诊断问题时非常有用。此日志文件可能会很快地变得非常大,因此不应该长期使用它。此日志通常名为hostname.log,位于data目录中。

此名字可以用 --log命令行选项更改

二进制日志。它记录更新过数据(或者可能更新过数据)的所有语句。此日志通常名为hostname-bin,位于data目录内。

此名字 可以用--log-bin命令行选项更改。注意,这个日志文件是MySQL5中添加的,以前的MySQL版本中使用的是更新日志

缓慢查询日志。顾名思义,此日志记录执行缓慢的任何查询。这 个日志在确定数据库何处需要优化很有用。此日志通常名为 hostname-slow.log,位于data目录中。

此名字可以用--log-slow-queries命令行选项更改。 在使用日志时,可用FLUSH LOGS语句来刷新和重新开始所有日志文件。

第三十章——改善性能

30.1——改善性能

数据库管理员把他们生命中的相当一部份时间花在了调整、试验以 改善DBMS性能之上。在诊断应用的滞缓现象和性能问题时,性能不良的数据库(以及数据库查询)通常是最常见的祸因

可以看出,下面的内容并不能完全决定MySQL的性能。我们只是 想回顾一下前面各章的重点,提供进行性能优化探讨和分析的一个出 发点。

首先,MySQL(与所有DBMS一样)具有特定的硬件建议。在学 习和研究MySQL时,使用任何旧的计算机作为服务器都可以。但 对用于生产的服务器来说,应该坚持遵循这些硬件建议。

 一般来说,关键的生产DBMS应该运行在自己的专用服务器上。  MySQL是用一系列的默认设置预先配置的,从这些设置开始通常 是很好的。但过一段时间后你可能需要调整内存分配、缓冲区大 小等。(为查看当前设置,可使用SHOW VARIABLES;和SHOWSTATUS;。)
 MySQL一个多用户多线程的DBMS,换言之,它经常同时执行多

个任务。如果这些任务中的某一个执行缓慢,则所有请求都会执 行缓慢。如果你遇到显著的性能不良,可使用SHOW PROCESSLIST 显示所有活动进程(以及它们的线程ID和执行时间)。你还可以用

KILL命令终结某个特定的进程(使用这个命令需要作为管理员登

录)。
 总是有不止一种方法编写同一条SELECT语句。应该试验联结、并、

子查询等,找出最佳的方法。
 使用EXPLAIN语句让MySQL解释它将如何执行一条SELECT语句。  一般来说,存储过程执行得比一条一条地执行其中的各条MySQL

语句快。
 应该总是使用正确的数据类型。
 决不要检索比需求还要多的数据。换言之,不要用SELECT *(除

非你真正需要每个列)。
 有的操作(包括INSERT)支持一个可选的DELAYED关键字,如果

使用它,将把控制立即返回给调用程序,并且一旦有可能就实际

执行该操作。
 在导入数据时,应该关闭自动提交。你可能还想删除索引(包括

FULLTEXT索引),然后在导入完成后再重建它们。
 必须索引数据库表以改善数据检索的性能。确定索引什么不是一 件微不足道的任务,需要分析使用的SELECT语句以找出重复的 WHERE和ORDER BY子句。如果一个简单的WHERE子句返回结果所花 的时间太长,则可以断定其中使用的列(或几个列)就是需要索

引的对象。
 你的SELECT语句中有一系列复杂的OR条件吗?通过使用多条

SELECT语句和连接它们的UNION语句,你能看到极大的性能改

进。
 索引改善数据检索的性能,但损害数据插入、删除和更新的性能。

如果你有一些表,它们收集数据且不经常被搜索,则在有必要之

前不要索引它们。(索引可根据需要添加和删除。)
 LIKE很慢。一般来说,最好是使用FULLTEXT而不是LIKE。
 数据库是不断变化的实体。一组优化良好的表一会儿后可能就面

目全非了。由于表的使用和内容的更改,理想的优化和配置也会

改变。
 最重要的规则就是,每条规则在某些条件下都会被打破。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值