软件测试/测试开发丨Pytest学习笔记

Pytest

格式要求
  • 文件: 以 test_ 开头或以 _test 结尾
  • 类: 以 Test 开头
  • 方法/函数: 以 _test 开头
  • 测试类中不可以添加构造函数, 若添加构造函数将导致Pytest无法识别类下的测试方法
断言

与Unittest不同, 在Pytest中我们需要使用python自带的 assert 关键字进行断言

  • assert <表达式>
  • assert <表达式>, <"描述信息">
def test_demo1(x):
    assert x == 5

def test_demo2(x):
    assert x == 5, f"x当前的值为{x}"
前置与后置
  • 全局模块级: setup_module/teardown_module
  • 类级: setup_class/teardown_class
  • 函数级: setup_function/teardown_function
  • 方法级: setup_method/teardown_method

我们可以通过一段代码来展示各个级别的作用域(对于执行结果做了一些并不改动结构的排版,以便观看)

def setup_module():
    print("setup_module")

def teardown_module():
    print("teardown_module")

​def setup_function():
    print("setup_function")

​def teardown_function():
    print("teardown_function")

​def test_function1():
    print("测试函数1")

​def test_function2():
    print("测试函数2")

​
class TestDemo1:
    def setup_class(self):
        print("setup_class")

​    def teardown_class(self):
        print("setup_class")

    def setup_method(self):
        print("setup")

​    def teardwon_method(self):
        print("teardwon")

    def test_fun1(self):
        print("测试方法1")

    def test_fun2(self):
        print("测试方法2")

​class TestDemo2:
    def setup_class(self):
        print("setup_class")

    def teardown_class(self):
        print("setup_class")

    def setup_method(self):
        print("setup")

    def teardown_method(self):
        print("teardwon")

    def test_fun3(self):
        print("测试方法3")

​

# ============================= test session starts ==============================

# collecting ... collected 5 items
# test_sample.py::test_function1
# setup_module
# setup_function
# PASSED [ 20%]测试函数1
# teardown_function
# test_sample.py::test_function2
# setup_function
# PASSED [ 40%]测试函数2
# teardown_function
# test_sample.py::TestDemo1::test_fun1
# test_sample.py::TestDemo1::test_fun2
# test_sample.py::TestDemo2::test_fun3
# setup_class1
# setup_method1
# PASSED [ 60%]测试方法1
# teardown_method1
# setup_method1
# PASSED [ 80%]测试方法2
# teardown_method1
# teardown_class1
# Process finished with exit code 0
# setup_class2
# setup_method2
# PASSED [100%]测试方法3
# teardown_method2
# teardown_class2
# teardown_module
# ============================== 5 passed in 0.20s ===============================

通过上面简单的框架,我们可以轻易的发现。

  • 在整个文件的执行过程中全局模块级作用于整个文件且仅执行一次, setup_module 在文件被执行时首先执行, teardown_module在文件内容全部执行完之后执行。
  • 类级仅作用在他的所属类当中且仅伴随类执行一次, setup_class 在类被执行前首先执行, teardown_class 在类执行完毕之后执行。
  • 我们称每一个不被任何类包含的function为函数, 函数级作用在每一个类外函数前后, setup_function 在每一个类外函数被执行前首先执行, teardown_function 在每一个类外函数执行结束之后执行。
  • 我们称每一个包含在类内的function为方法, 方法级作用在每一个类内方法前后, setup_method 在每一个类内方法被执行前首先执行, teardown_method 在每一个类内方法执行结束之后执行。
参数化

Pytest通过 pytest.mark.paramterize 装饰器帮助我们实现参数化

  • 单参数
import pytest

expect_key = ["one", "two", "three"]
keys = ["one", "three", "four"]

@pytest.mark.parametrize("key", keys)
def test_search(key):
    assert key in expect_key

上述代码实现了对元素是否包含在列表中的测试, expect_key是一个存放期望数据的列表, 我们把待验证的数据都存放在列表 key 中并把列表传递给 pytest.mark.parametrize。注意传入 pytest.mark.parametrize 的第一个参数是我们测试函数/方法的参数名,并且注意是字符串类型, 传入的 pytest.mark.parametrize 第二个参数是一个列表即存放我们待验证数据的列表。执行测试函数/方法时 pytest.mark.parametrize 会自行把待验证数据列表中每一个元素传入测试函数/方法中执行。

  • 多参数
import pytest

@pytest.mark.parametrize("username, password", [["username", "password"], ["username2", "password2"]])
def test_login(username, password):
    assert username == "username" and password == "password"

上述代码模拟了一个用户名和密码的验证, 原理与单参数相同。但要注意传入 pytest.mark.parametrize 的第一个参数需要将测试函数/方法的所有参数放到同一个字符串中再传入, 第二个是参数是以列表嵌套的形式传入所有的待验证数据。

  • 为用例创建别名

当我们在编译器中执行上述多参数所示的代码之后, 编译前会如何显示结果呢?

view_results

是的, 编译器会把每一次传入的数据作为case名, 看起来并不直观。当我们需要一个更直观的case名的时候我们可以做如下的操作进行优化。

import pytest

@pytest.mark.parametrize("username, password", [["username", "password"], ["username2", "password2"]], ids=["correct", "wrong"])
def test_login(username,password):
    assert username == "username" and password == "password"

我们为 pytest.mark.parametrize 加入了第三个参数,这会帮助我们给case命名

rename_case

需要注意的是 ids 列表的长度需要与数据列表的长度相等, 多或少都会报错。意味着一旦命名就必须每一个case都拥有名字。

  • 笛卡尔积形式参数化

我们在多参数过程中所有的参数都是被固定组合传入的, 那如果假设有两个序列, 我们希望两个序列中的元素分别组合进行测试我们要怎么做呢? 我们可以一起来通过代码看看如何实现参数的组合。

import pytest

list_a = [1, 2, 3]
list_b = [4, 5, 6]
​
@pytest.mark.parametrize("b", list_b, ids=["data_1", "data_2", "data_3"])
@pytest.mark.parametrize("a", list_a, ids=["data_4", "data_5", "data_6"])
def test_sum(a, b):
    assert a + b >= 7

上述代码是在判断两个列表中各个元素之和是否大于等于7, 我们利用 pytest.mark.parametrize 的叠加做到了所有组合。

如此叠加 pytest.mark.parametrize 装饰器其实更像是内嵌循环, 在执行过程中优先执行离测试函数/方法最近的装饰器, 并携带数据去执行外侧装饰器, 当外侧数据全部执行完毕再返回内侧装饰器并重复上述步骤直至内外侧数据全部组合完毕。

针对上述代码来说程序会先将 list_a 中的 1 赋值给 a , 带着 a=1 去执行外侧的装饰器, 在外侧会将 list_b 中的 4 赋值给b, 此时 a=1, b=4 执行测试函数内部逻辑, 执行之后继续将 list_b 的下一个数据赋值给 b 并执行测试函数内部逻辑, 此时 a=1, b=5, 以此类推直到list_b所有值都已经赋值给过 b 时程序会回到内侧装饰器, 将 list_a 的下一个数据赋值给 a ,此流程直到各个参数均为对应数据组的最后一个数据被测试函数执行为止。

标记测试用例

当我们希望将符合某一特征的一部分用例打包执行时, 就可以考虑为这些用例标记标签。

@pytest.mark.valid
@pytest.mark.parametrize("x, y, expected", [[99, 99, 198], [-99, -99, -198]])
def test_valid_data(self, x, y, expected):
    assert x + y == expected

def test_demo():
    print("demo")

上面的代码中我们实现了两个测试函数其中之一验证两数字之和, 并通过 pytest.mark.valid 的方式将该用例打上 valid 的标签。另外一个case仅为了验证标签效果。此时我们只需要通过命令行 pytest -m "<标签>" 执行测试即可。

pytest08

执行后发现我们并没有执行没有标记 valid 标签的用例, 并且可以发现输出的结果中是有警告的, 虽然并不影响结果但这样的显示并不友好。不必为此担心, 我们可以通过设置 pytest.ini 文件来让这个警告消失, 对于此文件的使用我们在本文靠后部分有详细使用方法。

跳过测试

当我们的case由于一些特殊的情况不希望被执行时我们可以选择利用pytest提供的一些装饰器跳过这些case。

  • 利用 pytest.mark.skip 直接粗暴的跳过整个case
import pytest

@pytest.mark.skip(reason="This case has been skipped")
def test_skip():
    print("down")

当然我们并不是在任何情况都会跳过case, 此时我们可以利用 pytest.mark.skipif 进行一些判断

import pytest
import sys

@pytest.mark.skipif(sys.platform == "darwin", reason="The execution system is Mac, we will skip this case")
def test_skip():
    print("down")

上述代码中的 sys.platform == "darwin" 是在判断是不是mac系统, 如果当前执行的执行代码的系统是mac那么 sys.platform == "darwin" 为 True 该case会被跳过, 反之case正常执行。

上述两种跳过方式都会直接跳过整个case, 其实我们有更灵活的方式进行跳过

  • 利用 pytest.skip() 实现在代码块中跳过
import sys

def test_skip():
    print("start")
    # some processes
    if sys.platform == "darwin":
        pytest.skip(reason="The execution system is Mac, we will skip the rest of this case")
    print("end")
常用命令行参数
  • --lf : 只重新运行故障
  • --ff : 先运行故障再运行其余测试
  • -x : 用例一旦失败(fail/error)就停止运行
  • --maxfail=<num> : 允许的最大失败数, 当失败数达到num时停止执行
  • -m : 执行指定标签的用例
  • -k : 执行包含某个关键字的用例
  • -v : 打印详细日志
  • -s : 打印代码中的输出
  • --collect-only : 只收集用例,不运行
  • --help : 帮助文档
fixture的用法

假设我们有5个case在执行前都需要登录操作, 那么我们可以借助 setup 来实现。但假如我们5个case中有3个需要登录剩下的并不需要登录我们应该怎么办呢?pytest为我们提供了解决办法

import pytest

@pytest.fixture()
def login(self):
    print("登录完成")

class TestDemo:
    def test_case1(self, login):
        print("case1")

    def test_case2(self, login):
        print("case2")

在上面的代码块中, 我们为login方法增加了一个 pytest.fixture 的装饰器, 当我们需要使用login方法作为某个case的前置条件时只需要将login的函数名当做参数传入case即可

同样fixture也会有作用域, 我们只需要为 pytest.fixture 设置 scope即可

  • 函数级(默认方式): 每一个函数或者方法多会调用
  • 类级(scope=“class”): 每一个测试类只运行一次
  • 模块级(scope=“module”): 每一个.py文件只调用一次
  • 包级(scope=“package”): 每一个python包只调用一次(暂不支持)
  • 绘话级(scope=“session”): 每次会话只需要执行一次, 会话内所有方法及类, 模块都共享这个方法
import pytest

@pytest.fixture(scope="class")
def login():
    print("登录完成")

class TestDemo:
    def test_case1(self, login):
        print("case1")

def test_case2(self, login):
    print("case2")

pytest.fixture 其实也可以做到teardwon的功能, 但这需要 yield 的辅助

import pytest

@pytest.fixture(scope="class")
def login():
    print("开始登录")
    token = "this is a token"
    yield token
    print("登录已完成")
​
class TestDemo:
    def test_case1(self, login):
        print(login)

到了此处需要渐渐开始考虑第一个问题是被 pytest.fixture 装饰的方法如何被更好的共用呢?

我们可以设想一个场景, 我们有多个待测试模块, 执行这些模块下的case前都需要进行登录的操作, 我们是一定不会为每一个模块都写一遍登录方法的。我们会选择新增一个公共模块并将登录方法写入公共模块, 在需要时导入调用即可。是的这是一个可行的方法, 但有没有更简洁的方法呢? Pytest的框架中允许我们添加一个名为 conftext.py 的文件, 被写在这个文件中的方法可以免去导入的过程直接在各个模块下的case中调用

# ---- yaml中数据 -----
- 99
- 99
- 198

# ----- conftext.py -----

import pytest
import yaml

@pytest.fixture()
def get_data_yaml():
    print("开始测试")
    with open("data/data_add.yaml", "r") as f:
        yield yaml.safe_load(f)
    print("测试结束")

# ----- 待测函数 -----

class Calculator:
def add(self, a, b):
    if a > 99 or a < -99 or b > 99 or b < -99:
        print("请输入范围为【-99, 99】的整数或浮点数")
        return "参数大小超出范围"

    return a + b

# ----- 测试用例 -----
calculator = Calculator()

class TestAdd:
    def test_valid_data(self, get_data_yaml):
        assert calculator.add(int(get_data_yaml[0]), int(get_data_yaml[1])) == int(get_data_yaml[2])
pytest.ini

pytest.ini是pytest的配置文件, 可以修改pytest的默认行为

  • 修改用例的命名规则

    pytest01

  • 修改默认写入的命令行参数

    pytest02

  • 指定执行路径

    pytest03

  • 忽视某些目录

    pytest04

  • 配置日志格式

    pytest05

当我们在 pytest.ini 相关内容之后在测试函数/方法之中使用 logging 既可以在指定路径得到日志

@pytest.mark.parametrize("x, y, expected", [[99, 99, 198], [-99, -99, -198]])
def test_valid_data(self, x, y, expected):
    logging.info(f"有效等价类{x}, {y}")
    assert x + y == expected

通过pytest执行上述测试代码之后终端显示结果:

pytest06

生成 ./log/test.log 日志内容:

pytest07

  • 添加标签, 防止运行过程报警告错误

是否还记得为用例添加标签的时候我们在终端中看到了警告? 此时我们就可以来结果这个警告了, 我们只需要在 pytest.ini 文件中添加 markers = <标签名> 即可

pytest09

可以注意到图片中的例子, 当我们有多个标签需要添加时, 需要保证每一个标签各占一行。

Pytest插件

推荐可能会用到的插件:

  • 分布式插件: pytest-xdist

在实际使用中我们可能希望Pytest可以为我们实现一些专属于当前项目的功能, 那么此时我们可以去自定义的开发Pytest插件实现我们的需求。Pytest为我们提供了很多有顺序但为实现具体功能的hook函数, 这些hook函数被穿插在每一次执行用例的过程中。因此我们可以通过实现这些hook函数的具体功能来进一步开发我们需要的插件。

那么我们就有必要来了解一下这些hook函数, 他们被定义在Pytest源码中的 hookspec.py 中

root
└── pytest_cmdline_main
├── pytest_plugin_registered
├── pytest_configure
│ └── pytest_plugin_registered
├── pytest_sessionstart
│ ├── pytest_plugin_registered
│ └── pytest_report_header
├── pytest_collection
│ ├── pytest_collectstart
│ ├── pytest_make_collect_report
│ │ ├── pytest_collect_file
│ │ │ └── pytest_pycollect_makemodule
│ │ └── pytest_pycollect_makeitem
│ │ └── pytest_generate_tests
│ │ └── pytest_make_parametrize_id
│ ├── pytest_collectreport
│ ├── pytest_itemcollected
│ ├── pytest_collection_modifyitems
│ └── pytest_collection_finish
│ └── pytest_report_collectionfinish
├── pytest_runtestloop
│ └── pytest_runtest_protocol
│ ├── pytest_runtest_logstart
│ ├── pytest_runtest_setup
│ │ └── pytest_fixture_setup
│ ├── pytest_runtest_makereport
│ ├── pytest_runtest_logreport
│ │ └── pytest_report_teststatus
│ ├── pytest_runtest_call
│ │ └── pytest_pyfunc_call
│ ├── pytest_runtest_teardown
│ │ └── pytest_fixture_post_finalizer
│ └── pytest_runtest_logfinish
├── pytest_sessionfinish
│ └── pytest_terminal_summary
└── pytest_unconfigure

当我们具体实现某些hook函数时, 我们只需要将这些hook函数的具体代码实现在项目根目录中的 conftest.py 中即可

# 以下代码实现了通过命令行决定解析数据文件的类型

import pytest
import yaml
import xlrd
import csv
import json

def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> None:
    mygroup = parser.getgroup(name="wangyun")
    mygroup.addoption("--parse_data", default="yaml", help="Parsing different types of data files")

​@pytest.fixture(scope="session")
def parse_data(request):
    myenv = request.config.getoption("--parse_data", default="yaml")
    if myenv == "yaml":
        with open("data/data_add.yaml", "r") as f:
        return yaml.safe_load(f)

    elif myenv == "excel":
        table = xlrd.open_workbook("data/data_add.xls").sheet_by_name(sheet_name='Sheet1')
        data = [table.row_values(rowx=i, start_colx=0, end_colx=None) for i in range(table.nrows)]
        return data

    elif myenv == "csv":
        with open("data/data_add.csv", "r") as f:
        raw = csv.reader(f)
        data = [line for line in raw]
        return data

    if myenv == "json":
        with open("data/data_add.json", "r") as f:
        raw = json.loads(f.read())
        return list(raw.values())

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/274108.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CGAL中三角形曲面网格近似

1、介绍 此软件包实现了变分形状近似&#xff08;VSA&#xff09;方法&#xff0c;通过更简单的表面三角形网格来近似输入表面网格。该算法的输入必须是&#xff1a; 三角形分割&#xff1b;组合2流形 输出是一个三角形汤&#xff0c;可以构建成多边形曲面网格。 给定一个输入曲…

【GNSS】LAMBDA 模糊度搜索 MATLAB 工具箱使用笔记

文章目录 Part.I IntroductionChap.I 传送门Chap.II 工具箱下载 Part.II LAMBDA 3.0 工具箱Chap.I 文件结构Chap.II 简单使用 Part.III Ps-LAMBDA 1.0 工具箱Chap.I 文件结构Chap.II 简单使用 Part.IV 待解决的问题Reference Part.I Introduction 最近进行模糊度搜索方面的研究…

TensorFlow的实战(详细代码)

1 TensorFlow基础 1.1 TensorFlow概要 TensorFlow使用数据流式图规划计算流程&#xff0c;它可以将计算映射到不同的硬件和操作系统平台。 1.2 TensorFlow编程模型简介 TensorFlow中的计算可表示为一个有向图(计算图)&#xff0c;其中每个运算操作为一个节点&#xff0c;每个…

黑马头条--day11-kafkaStream热点文章实时计算

目录 一.定时计算与实时计算 二. 实时流式计算 1.概念 2. 应用场景 3.技术方案选型 三. Kafka Stream 1 概述 2.Kafka Streams的关键概念 3. KStream 4. Kafka Stream入门案例编写 5.SpringBoot集成Kafka Stream 四.app端热点文章计算 功能实现 用户行为&#xff…

数据库(Database)基础知识

什么是数据库 数据库是按照数据结构来组织、存储和管理数据的仓库&#xff0c;用户可以通过数据库管理系统对存储的数据进行增删改查操作。 数据库实际上是一个文件集合&#xff0c;本质就是一个文件系统&#xff0c;以文件的方式&#xff0c;将数据保存在电脑上。 什么是数据…

Postman常见问题及解决方法

1、网络连接问题 如果Postman无法发送请求或接收响应&#xff0c;可以尝试以下操作&#xff1a; 检查网络连接是否正常&#xff0c;包括检查网络设置、代理设置等。 确认请求的URL是否正确&#xff0c;并检查是否使用了正确的HTTP方法&#xff08;例如GET、POST、PUT等&#…

深度强化学习DQN训练避障

目录 一.前言 二.代码 2.1完整代码 2.2运行环境 2.3动作空间 2.4奖励函数 2.5状态输入 2.6实验结果 一.前言 深度Q网络&#xff08;DQN&#xff09;是深度强化学习领域的一项革命性技术&#xff0c;它成功地将深度学习的强大感知能力与强化学习的决策能力相结合。在过…

BloombergGPT—金融领域大模型

文章目录 背景BloombergGPT数据集金融领域数据集通用数据集分词 模型模型结构模型相关参数训练配置训练过程 模型评估评估任务分布模型对比金融领域评估通用领域评估 背景 GPT-3的发布证明了训练非常大的自回归语言模型&#xff08;LLM&#xff09;的强大优势。GPT-3有1750亿个…

Java并发编程(一)

1.什么是线程和进程&#xff0c;区别是什么? 进程&#xff1a;进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程。 线程&#xff1a;线程与进程相似&#xff0…

亿欧智库详解2023人力资源数字化,红海云解决方案受关注

近日&#xff0c;亿欧智库发布《2023中国人力资源数字化企业需求分析》报告&#xff0c;基于调研结果对开展人力资源数字化转型的企业进行画像分析&#xff0c;揭示了不同企业下人力资源数字化转型需求的差异性&#xff0c;同时为企业人力资源数字化转型路径、方法及平台工具选…

springboot带微信端小程序智慧校园电子班牌系统源码

随着时代进步&#xff0c;数字信息化不断发展&#xff0c;很多学校都开始了数字化的转变。智慧校园电子班牌系统源码是电子班牌集合信息化技术、物联网、智能化&#xff0c;电子班牌以云平台、云服务器为基础&#xff0c;融合了班级文化展示、课程管理、物联控制、教务管理、考…

如何配置TLSv1.2版本的ssl

1、tomcat配置TLSv1.2版本的ssl 如下图所示&#xff0c;打开tomcat\conf\server.xml文件&#xff0c;进行如下配置&#xff1a; 注意&#xff1a;需要将申请的tomcat版本的ssl认证文件&#xff0c;如server.jks存放到tomcat\conf\ssl_file\目录下。 <Connector port"1…

【Vue篇】基础篇—Vue指令,Vue生命周期

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f354;Vue概述&#x1f384;快速入门&#x1f33a;Vue指令⭐v-…

LTD257次升级 | 商品库存能提醒 • 商品运费批量改 • 小程序官网发视频 • 网页地址可设中文

1、 商城新增库存提醒&#xff0c;支持批量改运费&#xff1b; 2、 极速官微支持发布视频&#xff1b; 3、 官微中心登录新增公众号验证码验证&#xff1b; 4、 编辑器页面地址支持设置为中文&#xff1b; 5、 其他已知问题修复与优化&#xff1b; 01 商城 1) 新增商品库存提醒…

SpringMVC:SSM(Spring+SpringMVC+MyBatis)代码整理

文章目录 SpringMVC - 07SSM 框架代码整理一、准备工作1. 分析需求、准备数据库2. 新建一个项目&#xff0c;导入依赖&#xff1a;pom.xml3. 用 IDEA 连接数据库 二、MyBatis 层1. 外部配置文件&#xff1a;db.properties2. MyBatis 核心配置文件&#xff1a;mybatis-config.xm…

fpga xvc 调试实现,支持多端口同时调试多颗FPGA芯片

xilinx 推荐的实现结构方式如下&#xff1a; 通过一个ZYNQ运行xvc服务器&#xff0c;然后通过zynq去配置其他的FPGA&#xff0c;具体参考设计可以参考手册xapp1251&#xff0c;由于XVC运行的协议是标准的TCP协议&#xff0c;这种方式需要ZYNQ运行TCP协议&#xff0c;也就需要运…

单片机外设矩阵键盘之行列扫描识别原理与示例

单片机外设矩阵键盘之行列扫描识别原理与示例 1.概述 这篇文章介绍单片机通过行列扫描的方式识别矩阵键盘的按键&#xff0c;通过程序执行相应的操作。 2.行列扫描识别原理 2.1.独立按键识别原理 为什么需要矩阵按键 独立按键操作简单&#xff0c;当数量较多时候会占用单片机…

win10: 搭建本地pip镜像源

前言&#xff1a; windows下和linux下都可以搭建本地pip镜像源。操作流程上一样&#xff0c;但是细节上存在一些差异。建议在linux上搭建本地镜像&#xff0c;流程简单很多。在windows系统上&#xff0c;会在多个地方存在问题&#xff08;比如不识别.symlink文件&#xff0c;一…

【MySQL】索引特性

文章目录 一、索引的概念二、MySQL与磁盘三、索引的理解观察主键索引现象推导主键索引结构的构建索引结构可以采用的数据结构聚簇索引 VS 非聚簇索引 四、索引操作创建主键索引创建唯一索引创建普通索引创建全文索引查询索引删除索引索引创建原则 一、索引的概念 数据库表中存…

PostgreSQL | FunctionProcedure | 函数与存储过程的区别

文章目录 PostgreSQL | Function&Procedure | 函数与存储过程的区别1. 简述书面说法大白话讲 2. 函数&#xff08;Function&#xff09;2.1 定义2.2 用途2.3 执行2.4 事务处理2.5 说点例子1. 当参数都是IN类时2. 参数中出现OUT、INOUT参数时 3. 存储过程&#xff08;Proced…