SQLite数据库从0到1

news2025/2/28 12:35:25

SQLite

SQLite基础知识

  1. SQLite数据库功能特性:ACID事务;支持数据库大小至2TB;足够小,大致13万行C代码4MB左右;存储在单一磁盘文件中的完整数据库。独立,无额外依赖。源码完全开源。支持多种编程语言,如 C/C++、PHP、Java、Python 等。

  2. ACID:在 DBMS 写入或更新数据过程中,为保证事务正确可靠,具备原子性、一致性、隔离性、持久性 4 个特性 。

  3. 常用指令

    1. databases 列出数据库的名称及所附属的文件信息
      quit 	  退出SQLite程序
      show	  显示各种属性
      open ?OPTION? ?FILE? 打开数据库
      tables ?TABLE? 查询数据库有哪些表
      limit 
      del studentdb.db 删除数据库
      
  4. SQLite数据库中的主表保存数据库表的关键数据信息,命名为sqlite_master。

    1. sqlite> .schema sqlite_master
      CREATE TABLE sqlite_master (
        type text,
        name text,
        tbl_name text,
        rootpage integer,
        sql text
      );
      
  5. 创建SQ Lite数据库及创建表

    1. sqlite> .databases
      main: "" r/w
      sqlite> .open studetndb.db
      --通过打开方式创建数据库,如果已经存在,直接打开,否则直接创建数据库
      sqlite> .databases
      main: E:\sqlite\studetndb.db r/w
      sqlite> .tables
      sqlite> create table tbl(stuno,stuname);
      sqlite> .tables
      tbl
      sqlite> insert into tbl values(101,'dell');
      sqlite> .tables
      tbl
      sqlite> select *from tbl;
      101|dell
      sqlite> insert into tbl values(102,'dxll');
      sqlite> select *from tbl;
      101|dell
      102|dxll
      

SQLite数据库:附加与分离

附加数据库

此操作:打开数据库和使用attach附加进来的数据库必须在同一目录

sqlite> .databases
main: "" r/w
sqlite> attach database 'db1.db' as 'db1test'
   ...> ;
--没有 db1test 就新建
sqlite> .databases
main: "" r/w
db1test: E:\sqlite\db1.db r/w
sqlite> attach database 'db1.db' as 'db1test2';
sqlite> .databases
main: "" r/w
db1test: E:\sqlite\db1.db r/w
db1test2: E:\sqlite\db1.db r/w

分离数据库

detach database 数据库名称

sqlite> detach database 'db1test';
sqlite> .databases
main: "" r/w
db1test2: E:\sqlite\db1.db r/w

SQLite数据类型

SQLite存储各类和数据类型

  1. 数据值:
    • NULL:表示该值为NULL值
    • INTEGER:整型值
    • REAL:浮点值
    • TEXT:文本字符串,存储使用的编码方式为UTF-8、UTF-16BE,UTF-16LE。
    • BLOB:存储Blob数据,该类型数据和输入数据完全相同
  2. 日期和时间数据类型:和布尔类型一样,SQLite也同样没有提供专门的日期时间存储类型,而是以TEXY、REAL和INTEGER类型分别表示不同的格式表示该类型。
    • TEXT (‘YYYY-MM-DD HH:MM:SS’)
    • REAL:从公元前4714年11月24日格林尼治时间的正午计算的天数。
    • INTEGER:从1970-01-01 00:00:00 UTC 算起的秒数
  3. 布尔类型:SQLite没有提供专门的布尔类型,存储为0 1整数

SQLite五种亲缘类型

最大化SQLite和其他数据库引擎之间额数据类型兼容性,SQLite提出类型亲缘性(type affinity)

  • TEXT:数值型数据在被插入之前,需要先被转换为文本格式,之后再插入到目标字段中。
  • NUMERIC:当文本数据被插入到亲缘性为 NUMERIC 的字段中时,如果转换操作不会导致数据信息丢失以及完全可逆,那么 SQLite 就会将该文本数据转换为 INTEGER 或 REAL 类型的数据,如果转换失败,SQLite 仍会以 TEXT 方式存储该数据。
  • INTEGER:对于亲缘类型为 INTEGER 的字段,其规则等同于 NUMERIC,唯一差别是在执行 CAST 表达式时。
  • REAL:其规则基本等同于 NUMERIC,唯一的差别是不会将 “30000.0” 这样的文本数据转换为 INTEGER 存储方式。
  • NONE:不做任何的转换,直接以该数据所属的数据类型进行存储。

INTEGER(假设类型字符串当中包含 INT,此字段亲缘类型为 INEGER)

  • INT、INTEGER(取值范围足够大,不用担心超限问题)
  • TINYINT:枚举类型
  • SMALLINT:用于小范围的统计数据
  • MEDIUMINT:用于比较大整数的计算
  • BIGINT:处理巨大整数才用到。

TEXT

  • VARCHAR 可变长度的字符串
  • TEXT:一个文本字符串
  • CLOB:大文本、大字段

REAL

  • REAL:浮点值,以 8 个字节指数形式存储
  • FLOAT:单精度
  • DOUBLE:双精度

NUMERIC

  • DECIMAL:小数值
  • NUMERIC:精确数字类型
  • DATE:日期类型
  • DATETIME:日期时间类型

SQLite 比较表达式:=、==、<、<=、>、>=、!=、IN、NOT IN、BETWEEN、IS、IS NOT。
SQLite 操作符: + - * / % << >> & and | 。

创建表&删除表

  1. create table 语法:

    1. create table 鼠标名称(
      字段名称1 数据类型1 约束等条件,
      字段名称2 数据类型2 约束等条件,
      字段名称3 数据类型3 约束等条件,
      ...)
      
  2. SQLite常用约束条件

    1. NOT NULL 约束:确保某列不能有 NULL 值。
      DEFAULT 约束:当某列没有指定值时,为该列提供默认值。
      UNIQUE 约束:确保某列中的所有值是不同的。
      PRIMARY Key 约束:唯一标识数据库表中的各行 / 记录。
      CHECK 约束:CHECK 约束确保某列中的所有值满足一定条件。
      
  3. Navicat:

    1. --------创建数据表
      -- 学生表
      create table student(
      sno INT primary key not null, --学号
      sname text not null,--姓名
      ssex text not null,--性别
      sdept text --所在系、学员
      );
      -- 课程表
      create table course(
      cno int PRIMARY KEY not NULL, --课程编号
      cname text not null, --课程名称
      ccredit int --课程学分
      );
      
      --学生选课表
      create table sc(
      sno int PRIMARY KEY NOT NULL, --学号
      cno int NOT NULL, --课程编号
      score int --考试分数
      );
      select *FROM sqlite_master;
      ----------删除数据表
      drop table sc;
      

insert into 语句

INSERT INTO TABLE_NAME[(cloumn1,cloumn2,cloumn3,...cloumnN)] VALUES (value1,value2,value3...,valueN);
INSERT INTO TABLE_NAME VALUES (value1, value2,value3 ..., valueN) ; --不指定列名按顺序即可
--查询输出student数据表
select *from student;

--向student数据表插入一条记录
insert into student(sno,sname,ssex,sage,sdept)
values(101,'张华','男',21,'计算机系');
insert into student(sno,sname,ssex,sage,sdept)
values(102,'张ss','女',22,'外语系');

--未指定数据表中的字段
insert into student values(103,'刘ss','女',23,'生物系');

select & update & delete

select用法

-- 查询student表中所有记录
SELECT * FROM "student"

-- 只查询student表中的:学号、姓名
SELECT Sno as '学号', Sname as '姓名' FROM Student

-- SQLite 算术运算符
select 10 + 20; 
select 10 - 20;
select 10 * 20;
select 10 / 20;
select 10 % 20;

--查询表结构信息
PRAGMA table_info(course);

-- SQLite比较运算符
SELECT * FROM student WHERE sage = 19;
SELECT * FROM student WHERE sage > 19;

-- SQLite 逻辑运算符
SELECT *FROM student WHERE sage >= 19 and sage <= 21
SELECT *FROM student WHERE sname LIKE '张%'  --模糊匹配
SELECT *FROM student WHERE sage in(19,21); --19 和 21
SELECT *FROM student WHERE sage not in(19,21); -- 非 19 和 21

SELECT *FROM student WHERE sage BETWEEN 20 and 24; -- 非 19 和 21

-- SQLite表达式
SELECT *FROM student WHERE ssex='女' and sage = 22;

update & delete

-- SQLite UPDATE 语句操作
UPDATE student 
SET sage = 32
where sno = 1004 --将学号为1004的年龄改为32


-- SQLite delete 删除语句
DELETE FROM student WHERE sno = 1004; --用主键作为条件,防止删除多个

联合查询

select student.sno,student.sage,student.ssex,student.sname,student.sdept,sc.cno,sc.score
from student,sc
WHERE(student.sno=sc.sno);
1001	21	男	张华	计算机系	101	98
1003	23	女	刘ss	物理系	105	99
1008	22	女	叶知夏	生物系	106	89
1006	19	男	陈嘉佑	生物系	104	88

distinct & orderby & groupby

  1. dinstinct 专用于消除重复记录

    1. -- 通过distinct关键字,消除重复这个字段
      select DISTINCT sname from staff;
      
  2. order by 专用于对一个或多个字段按升序或者降序排列数据

    1. -- ASC | DESC  从小到大(默认) | 从大到小 order by
      SELECT *FROM staff ORDER BY ssalary ASC;
      SELECT *FROM staff ORDER BY ssalary DESC;
      
  3. group by 子句专用于配合相同数据进行分组

    1. -- 相同数据进行分组
      select sid, sname,sage,sdepartment,spost, ssalary, sum(ssalary) from staff group by sname ORDER by sname;
      
  4. having子句专用于指定条件 来过滤将数据,having子句由group by子句创建的分组在设置条件

    1. -- 过滤数据
      select *FROM staff GROUP by sname HAVING COUNT(sname)<2  --姓名只出现一次的
      select *FROM staff GROUP by sname HAVING COUNT(sname)=2  --姓名只出现一次的
      -- 1	andy	29	开发部	工程师	15000
      

SQLite触发器

SQLite触发器是数据库的一个回调函数,在指定的数据库事件发生时,自动执行或调用。比如insert、update、delete时等触发。

select * FROM staff;

select * FROM staff_log;

--创建staff员工表对应日志表
create table staff_log(
Staff_ID INT NOT NULL,
Staff_Date TEXT NOT NULL
);

-- 创建触发器 staff数据表--->监控此表插入记录事件
CREATE TRIGGER staff_logtrigger after INSERT  -- 对 staff 执行插入操作后
ON staff
BEGIN
	insert into staff_log(Staff_ID,Staff_Date) VALUES(new.SID,datetime('now'));
END

--- test data

insert into staff values(15,'yuuny',38,'财务部','会计',16000);
-- 15	2025-02-27 06:12:49
insert into staff values(16,'say',32,'财务部','出纳',17000);
16	2025-02-27 06:14:58

-- 查询刚创建的触发器:staff_logtrigger
SELECT name from sqlite_master
where type='trigger';

-- 删除触发器
DROP TRIGGER staff_logtrigger;

SQLite索引

索引index是一种特殊查找表,数据库搜索引擎用来加速数据检索功能

CREATE INDEX 索引名称 ON 数据表名称;
  • 搜索码:表示的是记录各种字符段的一个集合,它可以是一个或者多个字符段的任意序列组合,并不是唯一的一个标识记录。
  • 数据目录项: 为索引的相关元素,在建立索引的过程中,数据目录项一般具有各种不同的选择方式
  • 记录ID:每一个/段索引在存储内容中唯一的一个标识符。
  1. 单列索引(基于表的一个列上创建索引)
  2. 唯一索引(唯一索引不允许任何重复的值插入到数据表当中:create unique index)
  3. 组合索引(基于表的两个或多个列创建索引)
  4. 隐式索引(在创建数据表的时候,由数据库服务器自动创建索引)
--查询数据表:staff
SELECT * FROM staff;

-- 创建索引
CREATE INDEX staff_ssalary_index on staff(SSalary);

-- 查找索引
SELECT * FROM sqlite_master WHERE type='index'

-- 通过索引查询数据
SELECT *FROM staff indexed by staff_ssalary_index WHERE ssalary >= 10000


SELECT *FROM staff WHERE ssalary >= 10000
--删除索引
drop index staff_ssalary_index;

优缺点

  • 优点

    • ​ 通过建立索引可以极大地提高在数据库中获取所需数据信息的速度,同时还能够提高服务器处理相关搜索请求的效率

    • ​ 索引本质上是一种数据结构,它会存储表中特定列的值以及这些值对应的行在磁盘上的物理地址(或逻辑指针)。当表中的数据发生变化时,**数据库不仅要更新表中的数据,还要更新与之相关的所有索引。**降低插入、更新和删除操作的性能

  • 避免使用

    • 数据表比较小不要使用索引
    • 索引不应该出现在频繁操作的列当中
    • 索引应该使用在由大量的NULL值列上
    • 又频繁大笔批量数据实现更新、增加、删除操作的。

SQLite 视图

视图则为一张虚拟表,它内容数据由查询决定。视图并不在数据

库中存储的数据值形式存储,记录和字段由自由视图的查询所引用的

表,并且在引用视图时动态生成。

​ 视图是存储在数据库中查询 SQL 语句:安全原因、可以使复杂

的查询易于理解和使用。可以在视图上创建一个触发器。

优势:视点集中简化操作定制数据安全性问题。

SQLite 事务

​ **数据库事务( transaction)**是访问并可能操作各种数据项的

一个数据库操作序列,这些操作要么全部执行,要么全部不执行,

是一个不可分割的工作单位。事务由事务开始与事务结束之间执

行的全部数据库操作组成。

2、事务属性

​ **原子性(Atomicity):**事务中的全部操作在数据库中是不可

分割的,要么全部完成,要么全部不执行。

​ **一致性(Consistency):**几个并行执行的事务,其执行结果

必须与按某一顺序串行执行的结果相一致。

​ **隔离性(Isolation):**事务的执行不受其他事务的干扰,事务

执行的中间结果对其他事务必须是透明的。

​ **持久性(Durability):**对于任意已提交事务,系统必须保证

该事务对数据库的改变不被丢失,即使数据库出现故障。

3、事务控制命令

  • begin transaction:事务处理
  • commit:提交事务
  • rollback:回滚事务

通过事务方式对数据库进行访问优势:

  • 能够保证数据的读一致性;
  • 在数据永久改变前,可以预览数据变化;
  • 把逻辑相关的操作分成一个组。
select * FROM staff
--事务处理
BEGIN;
DELETE FROM staff WHERE sid=2;
--事务提交 提交之后无法回滚
COMMIT;
--事务回滚
ROLLBACK;

Qt操作SQLite数据库实战

1、QSqlDatabase 类

该类专门用于处理和数据库的连接。头文件需要导入

QSqlDatabase

在工程 qmake.pro 文件中加入 SQL 数据库支持:

QT +=sql

2、Qsqlquery 类

QSqlQuery 类提供执行和操作 SQL 语句的各种方法。比如执行

DML(数据操作语言)语句,如 select/insert/delete/update 及

DLL(数据定义语言),如 create table/create view 等。

CreateSQLConnection.h

#ifndef CREATESQLCONNECTION_H
#define CREATESQLCONNECTION_H

#include <Qtsql/qsqldatabase>
#include <QSqlQuery>
#include <QMessageBox>

static bool CreateSQLConnectionFunc()
{
    QSqlDatabase sqldb=QSqlDatabase::addDatabase("QSQLITE");
    sqldb.setDatabaseName("test.db");

    if(!sqldb.open())
    {
        QMessageBox::critical(0,"错误","SQLite数据库打开失败,请重新打开",QMessageBox::Ok);
        return false;
    }
    else
    {
        QMessageBox::information(0,"正确","SQLite数据库打开成功!",QMessageBox::Ok);
    }
    QSqlQuery queryexec;

    //创建数据表
    queryexec.exec("create table emp(empid int primary key,empname varchar(20))");

    //插入记录
    queryexec.exec("insert into emp values(101,'vico')");
    queryexec.exec("insert into emp values(102,'abd')");
    queryexec.exec("insert into emp values(103,'eve')");
    queryexec.exec("insert into emp values(104,'lot')");
    queryexec.exec("insert into emp values(105,'clat')");
    return true;
}
#endif // CREATESQLCONNECTION_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "CreateSQLConnection.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<"正在连接SQLite数据库......";
    if(!CreateSQLConnectionFunc())
    {
        QMessageBox::information(0,"错误","数据库创建失败,请重新检查",QMessageBox::Ok);
    }

    QSqlQuery query;
    query.exec("select *from emp");

    qDebug()<<"职工编号"<<"职工姓名";

    while(query.next())
    {
        qDebug()<<query.value(0).toInt()<<qPrintable(query.value(1).toString());
    }
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java语法基础知识点1

目录 一、数组 1.1数组的初始化&#xff1a; 1.2数组的遍历方法&#xff1a; 1.3数组的常见使用方法&#xff1a; 二、类和对象 2.1构造方法&#xff1a; 2.2this关键字: 三、封装 3.1访问限定符&#xff1a; 3.2static关键字&#xff1a; 3.3代码块&#xff1a; 一…

【新手入门】SQL注入之盲注

一、引言 在我们的注入语句被带入数据库查询但却什么都没有返回的情况我们该怎么办? 例如应用程序返回到一个"通用的"的页面&#xff0c;或者重定向一个通用页面(可能为网站首页)。这时&#xff0c;我们之前学习的SQL注入的办法就无法使用了。这种情况我们称之为无…

python-leetcode-分割等和子集

416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; class Solution:def canPartition(self, nums: List[int]) -> bool:total sum(nums)if total % 2 ! 0:return Falsetarget total // 2dp [False] * (target 1)dp[0] Truefor num in nums:for j in range(tar…

趣讲TCP三次握手

一、TCP三次握手简介 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP连接中&#xff0c;只有两方进行通信&#xff0c;它使用校验和、确认和重传机制来保证数据的可靠传输。…

基于coze+微信小程序实现图片上传并利用大模型解析

项目截图&#xff1a; 实现代码&#xff08;直接搬去可用&#xff09; 前提&#xff1a;需要填写你的oss配置coze的api授权配置&#xff01;&#xff01;&#xff01; <template><view class"container"><!-- 高斯模糊背景 --><view class&qu…

VMware Fusion 虚拟机Mac版 安装CentOS 7 系统

介绍 CentOS是Community Enterprise Operating System的缩写&#xff0c;也叫做社区企业操作系统。是企业Linux发行版领头羊Red Hat Enterprise Linux的再编译版本&#xff08;是一个再发行版本&#xff09;&#xff0c;而且在RHEL的基础上修正了不少已知的 Bug &#xff0c;相…

java练习(44)

ps:题目来自力扣 两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 // 定义链表节点类&#xff0c;每个节…

Deepseek 开源周第一天:FlashMLA

Deepseek 隆重开启开源周!第一天我们迎来了FlashMLA。我很高兴带大家了解这项创新,揭秘 FlashMLA 为何能成为 AI 和 GPU 优化领域的变革者。 Deepseek 开源周的热门话题有哪些?

DeepSeek-OpenSourceWeek-第三天-Release of DeepGEMM

DeepGEMM:这是一款专为高效的 FP8(8 位浮点)通用矩阵乘法(GEMMs)而开发的尖端库。GEMMs 是许多 AI 工作负载(尤其是深度学习)中的基本操作。 特点: 支持稠密和 MoE GEMMs:它可以处理标准的稠密矩阵乘法以及混合专家(MoE)模型中使用的矩阵乘法。MoE 是一种神经网络架…

Bitlocker取证之PXE降级取密钥

支持到微软Surface pro系列。

.Net Core Visual Studio NuGet.Config 配置参考

Visual Studio 2022 NUGET NU1301 无法加载源 基础连接已关闭&#xff1a;无法建立SSL / TLS安全通道的信任关系&#xff1b;根据验证过程&#xff0c;远程证书无效&#xff0c;参考文章&#xff1a;https://blog.csdn.net/hefeng_aspnet/article/details/145780081 NuGet 行为…

Immich自托管服务的本地化部署与随时随地安全便捷在线访问数据

文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 小伙伴们&#xff0c;你们好呀&#xff01;今天要给大家揭秘一个超炫的技能——如何把自家电脑变成私人云相册&#xff0c;并…

Apache-iotdb 基本概念

问题背景 定义&#xff08;写得太好了&#xff01;&#xff09; root 是整个树状结构的父节点&#xff0c; CirroData-TimeS 有存储组、设备、测点等概念&#xff0c;数据在存储的时候&#xff0c;不同的存储组的数据是存储在不同的文件夹中的。上图中有 root.sgcc、root.ln两…

CryptoJS库中WordArray对象支持哪些输出格式?除了toString() 方法还有什么方法可以输出吗?WordArray对象的作用是什么?

前言&#xff1a;这里只说js用的CryptoJS库里的相关内容&#xff0c;只用js来进行代码操作和讲解。 这里网上相关的帖子很少&#xff0c;不得已问了很长时间AI 想引用CryptoJS库情况分两种&#xff0c;一种是html引用&#xff0c;另一种是在Nodejs里引用。 一、引用CryptoJS库…

springboot浅析

springboot浅析 什么是springboot&#xff1f; 实际上springboot就是一个给我们提供了快速搭建使用spring的一种方式&#xff0c;让我们省去了繁琐的xml配置。 为什么无需进行大量的xml配置&#xff0c;就是因为springboot是基于约定优于配置的思想&#xff0c;简单来说就是遵循…

【文件基础操作】小笔记

Step1: 现在项目文件夹&#xff08;我的项目叫做RunPony&#xff09;下创建一个a.txt文本文件&#xff0c;手动写入一些数字&#xff0c;保存 Step2: 现在在main.c内写一个基本的文件处理的程序 Step3: 现在已经知道如何打开关闭文件&#xff0c;下一步要搞懂如何读取txt内的…

SSL 证书是 SSL 协议实现安全通信的必要组成部分

SSL证书和SSL/TLS协议有着密切的关系&#xff0c;但它们本质上是不同的概念。下面是两者的区别和它们之间的关系的表格&#xff1a; 属性SSL/TLS 协议SSL证书英文全称SSL&#xff08;Secure Sockets Layer&#xff09;&#xff0c;TLS&#xff08;Transport Layer Security&am…

AI问答-供应链管理:排队模型M/D/5/100/m/FCFS代表的含义是什么

在供应链管理中&#xff0c;排队模型M/D/5/100/m/FCFS代表的含义如下&#xff1a; M&#xff1a; 表示顾客到达时间间隔服从负指数分布&#xff08;Markov&#xff0c;负指数分布具有无记忆性&#xff09;&#xff0c;即顾客到达是随机的&#xff0c;且到达时间间隔服从指数分…

Linux驱动学习(四)--字符设备注册

上一节讲到的字符设备注册与销毁是通过cdev_init、cdev_add、cdev_del等函数分步执行的&#xff0c;本小节用一种更简单的方式&#xff0c;来注册字符设备 register_chrdev 如果major为0&#xff0c;该函数将动态的分配一个主设备号并且返回对应的值如果major > 0&#xff…

30天开发操作系统 第24天 -- 窗口操作

一、窗口切换 1.0 前天开始我们的应用程序可以显示自己的窗口了&#xff0c;现在画面上到处都是窗口&#xff0c;我们急需能够 切换窗口顺序的功能&#xff0c;使得在需要的时候可以查 看最下面的窗口的内容。这个功能看起来不难&#xff0c;我们马上来实现它。 不过&#xf…