【CS.DB】数据库-关系型数据库-MySQL-3.4.数据的插入_查询_更新和删除

1000.04.CS.DB-Database-Relational-MySQL-3.4.数据的插入_查询_更新和删除-Created: 2024-08-08.Thursday10:11

在MySQL中,数据操作是日常数据库管理的核心部分。下面我们将详细讲解如何插入、查询、更新和删除数据,并通过经典例子帮助理解这些操作。

示例:

假设我们有一个 employees 表,结构如下:

sql 复制代码
CREATE TABLE employees (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    position VARCHAR(50),
    salary DECIMAL(10, 2),
    hire_date DATE
);

1 数据插入

数据插入是将新记录添加到表中的过程。最常用的插入方法是 INSERT 语句。

插入一条记录:

sql 复制代码
INSERT INTO employees (name, position, salary, hire_date) VALUES ('Alice', 'Engineer', 70000.00, '2024-01-15');

插入多条记录:

sql 复制代码
INSERT INTO employees (name, position, salary, hire_date) VALUES
('Bob', 'Manager', 90000.00, '2023-05-01'),
('Charlie', 'Analyst', 60000.00, '2024-02-20');

使用隐式类型转换:

sql 复制代码
INSERT INTO employees (name, position, salary, hire_date) VALUES ('David', 'Clerk', 40000, 20240101);
-- 40000 将被转换为 40000.00, 20240101 将被转换为 '2024-01-01'

mysql> select * from employees;
+----+-------+----------+----------+------------+
| id | name  | position | salary   | hire_date  |
+----+-------+----------+----------+------------+
|  1 | David | Clerk    | 40000.00 | 2024-01-01 |
+----+-------+----------+----------+------------+
1 row in set (0.01 sec)

2 数据查询

数据查询是从表中检索记录的过程。最常用的查询方法是 SELECT 语句。

示例:

查询所有记录:

sql 复制代码
SELECT * FROM employees;

查询特定字段:

sql 复制代码
SELECT name, position FROM employees;

使用条件查询:

sql 复制代码
SELECT * FROM employees WHERE position = 'Engineer';

使用排序:

sql 复制代码
SELECT * FROM employees ORDER BY salary DESC;

使用分页: ^20240808102635

sql 复制代码
-- 这将返回从第 21 条记录开始的 10 条记录。在这种查询中,`LIMIT` 指定返回的记录数量,`OFFSET` 指定跳过的记录数量。
SELECT * FROM employees LIMIT 10 OFFSET 20;

Ref. [[1000.04.CS.DB-Database-Relational-MySQL-4.6.深分页查询的优化]]

分页可以分为浅分页和深分页。上述提到的查询属于浅分页,适用于页数较少的情况。在记录数量不多的情况下,浅分页性能通常是可以接受的。但是,当页数变多时,特别是当 OFFSET 值变得很大时,浅分页的性能问题就会变得明显,因为数据库在处理深分页查询时,仍然需要扫描和跳过大量记录。

3 数据更新

数据更新是修改表中现有记录的过程。最常用的更新方法是 UPDATE 语句。

示例:

更新单条记录:

sql 复制代码
UPDATE employees SET salary = 75000.00 WHERE name = 'Alice';

更新多条记录:

sql 复制代码
UPDATE employees SET salary = salary * 1.10 WHERE position = 'Analyst';

带有条件的更新:

sql 复制代码
UPDATE employees SET position = 'Senior Engineer' WHERE name = 'Alice' AND salary < 80000;

4 数据删除

数据删除是从表中移除记录的过程。最常用的删除方法是 DELETE 语句。

示例:

删除单条记录:

sql 复制代码
DELETE FROM employees WHERE name = 'Charlie';

删除多条记录:

sql 复制代码
DELETE FROM employees WHERE position = 'Clerk';
sql 复制代码
DELETE FROM employees;
-- 或使用 TRUNCATE 清空表
TRUNCATE TABLE employees;

5 隐式类型转换 🔥

隐式类型转换是指数据库在必要时自动将一种数据类型转换为另一种数据类型。这在插入和查询操作中尤为常见。

隐式类型转换是MySQL的一项强大特性,但也需要谨慎使用,以避免数据不一致或意外错误。

示例:

字符串和整数之间的隐式转换:

sql 复制代码
INSERT INTO employees (name, position, salary, hire_date) VALUES ('Eve', 'Accountant', 50000, '20240101');
-- '20240101' 将被转换为 '2024-01-01'

查询中的隐式转换:

sql 复制代码
SELECT * FROM employees WHERE hire_date = 20240101;
-- 20240101 将被转换为 '2024-01-01'

**在某些情况下可能会引入潜在的错误或导致性能问题: **

5.1 字符串比较

你提到的错误示例:

sql 复制代码
mysql> select * from employees where name = David;
ERROR 1054 (42S22): Unknown column 'David' in 'where clause'

这表明 MySQL 试图将 David 解释为一个列名,而不是一个字符串值。在 SQL 中,字符串值需要用单引号 ' 括起来:

sql 复制代码
mysql> select * from employees where name = 'David';

正确的查询使用了单引号,并且成功返回了结果。

5.2 隐式类型转换的潜在问题

隐式类型转换虽然方便,但可能会引入一些 bug,以下是一些例子和说明:

5.2.1 数值和字符串比较 🔥

考虑下面的表和数据:

sql 复制代码
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    price VARCHAR(10)
);

INSERT INTO products (id, name, price) VALUES
(1, 'Product A', '10'),
(2, 'Product B', '20'),
(3, 'Product C', '30');

假设你想要比较价格,并执行以下查询:

sql 复制代码
mysql> SELECT * FROM products WHERE price = 20;
+----+-----------+-------+
| id | name      | price |
+----+-----------+-------+
|  2 | Product B | 20    |
+----+-----------+-------+
1 row in set, 1 warning (0.01 sec)

mysql>  SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1292
Message: Truncated incorrect DOUBLE value: 'twenty'
1 row in set (0.00 sec)

因为 price 列是字符串类型,MySQL 会将 20 转换为字符串 '20',这个查询会返回正确的结果。但是,如果 price 列包含非数值字符串,查询会产生不可预期的结果:

sql 复制代码
INSERT INTO products (id, name, price) VALUES (4, 'Product D', 'twenty');

再执行查询:

sql 复制代码
mysql> SELECT * FROM products WHERE price = 20;
+----+-----------+-------+
| id | name      | price |
+----+-----------+-------+
|  2 | Product B | 20    |
+----+-----------+-------+
1 row in set, 1 warning (0.00 sec)

-- MySQL 尝试将 `price` 列的值与数值 `20` 进行比较。这时,MySQL 进行隐式类型转换,将 `price` 列的值转换为数值。对于包含非数值字符串(如 'twenty')的行,MySQL 会发出警告并将这些值视为 `0`,从而导致不准确的结果。
mysql> SELECT * FROM products WHERE price < 20;
+----+-----------+--------+
| id | name      | price  |
+----+-----------+--------+
|  1 | Product A | 10     |
|  4 | Product D | twenty |
+----+-----------+--------+
2 rows in set, 1 warning (0.01 sec)

这时,MySQL 会进行隐式转换,可能会导致无法预期的结果或错误。这种情况下,显式转换能更明确地表达意图:

sql 复制代码
mysql> SELECT * FROM products WHERE CAST(price AS DECIMAL(10, 2)) = 20.00;
+----+-----------+-------+
| id | name      | price |
+----+-----------+-------+
|  2 | Product B | 20    |
+----+-----------+-------+
1 row in set, 2 warnings (0.00 sec)

mysql>  SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1366
Message: Incorrect DECIMAL value: '0' for column '' at row -1
*************************** 2. row ***************************
  Level: Warning
   Code: 1292
Message: Truncated incorrect DECIMAL value: 'twenty'
2 rows in set (0.01 sec)

总结:

  • 在设计数据库时,尽量选择合适的数据类型。例如,如果 price 应该始终是数值类型,应该将其定义为 DECIMALFLOAT 类型:CREATE TABLE products (id INT PRIMARY KEY,name VARCHAR(50),price DECIMAL(10, 2));
  • 在插入数据时,进行数据验证以确保数据类型和格式正确。例如,在应用程序层进行数据验证,确保插入到 price 列的数据是有效的数值。
  • 在查询时,显式地转换数据类型并处理异常。例如,使用 TRY_CAST 函数(在支持的数据库中)来处理可能的转换错误:SELECT * FROM products WHERE TRY_CAST(price AS DECIMAL(10, 2)) = 20.00;
5.2.2 字符串和日期比较

考虑下面的表和数据:

sql 复制代码
CREATE TABLE events (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    event_date VARCHAR(10)
);

INSERT INTO events (id, name, event_date) VALUES
(1, 'Event A', '2024-01-01'),
(2, 'Event B', '2024-02-01');

假设你想要比较日期,并执行以下查询:

sql 复制代码
SELECT * FROM events WHERE event_date = '2024-01-01';

这个查询会正确返回结果。但是如果 event_date 列包含非日期字符串:

sql 复制代码
INSERT INTO events (id, name, event_date) VALUES (3, 'Event C', 'January 1, 2024');

-- 再执行查询:
SELECT * FROM events WHERE event_date = '2024-01-01';

MySQL 会进行隐式转换,可能会导致意外结果或错误。显式转换可以避免这种问题:

sql 复制代码
SELECT * FROM events WHERE STR_TO_DATE(event_date, '%Y-%m-%d') = '2024-01-01';
5.2.3 字符串与时间戳比较

考虑一个包含时间戳的表:

sql 复制代码
CREATE TABLE logs (
    id INT PRIMARY KEY,
    event_time TIMESTAMP
);

INSERT INTO logs (id, event_time) VALUES
(1, '2024-01-01 10:00:00'),
(2, '2024-01-01 12:00:00');

假设你要查找特定时间之后的所有记录:

sql 复制代码
mysql> SELECT * FROM logs WHERE event_time > '2024-01-01 11:00:00';
+----+---------------------+
| id | event_time          |
+----+---------------------+
|  2 | 2024-01-01 12:00:00 |
+----+---------------------+
1 row in set (0.02 sec)

这条查询可以正常工作,因为字符串 '2024-01-01 11:00:00' 可以隐式转换为时间戳。但是,如果输入格式不一致,可能会引发问题:

sql 复制代码
mysql> SELECT * FROM logs WHERE event_time > '01-01-2024 11:00:00';
+----+---------------------+
| id | event_time          |
+----+---------------------+
|  1 | 2024-01-01 10:00:00 |
|  2 | 2024-01-01 12:00:00 |
+----+---------------------+
2 rows in set, 1 warning (0.01 sec)

-- 虽然查询没有抛出错误,但返回的结果和警告表明,MySQL 在处理日期时间比较时进行了隐式转换。我们可以进一步探讨这个隐式转换的行为及其可能引发的问题。

隐式转换行为: MySQL 在处理不符合标准格式的日期时间字符串时,会尝试将其转换为合适的格式。如果转换失败,MySQL 可能会使用默认值 '0000-00-00 00:00:00' 进行比较。这就是为什么查询没有抛出错误但仍返回了结果,并且产生了一个警告。

sql 复制代码
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1292
Message: Incorrect datetime value: '01-01-2024 11:00:00' for column 'event_time' at row 1
1 row in set (0.00 sec)

为了避免隐式转换问题并确保查询结果的准确性,建议使用标准的日期时间格式或显式转换函数。

使用标准日期时间格式 : 确保使用符合 MySQL 标准的日期时间格式 'YYYY-MM-DD HH:MM:SS'

sql 复制代码
SELECT * FROM logs WHERE event_time > '2024-01-01 11:00:00';

使用 STR_TO_DATE 函数 : 如果输入的日期时间格式不标准,可以使用 STR_TO_DATE 函数显式地转换:

sql 复制代码
mysql> SELECT * FROM logs WHERE event_time > STR_TO_DATE('01-01-2024 11:00:00', '%d-%m-%Y %H:%i:%s');
+----+---------------------+
| id | event_time          |
+----+---------------------+
|  2 | 2024-01-01 12:00:00 |
+----+---------------------+
1 row in set (0.00 sec)
5.2.4 如何验证非数值字符串(如 'twenty')在 MySQL 中被转换为 0 ?
sql 复制代码
-- 首先,创建一个名为 `products` 的表,并插入一些数据,包括非数值字符串。
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    price VARCHAR(10)
);

INSERT INTO products (id, name, price) VALUES
(1, 'Product A', '10'),
(2, 'Product B', '20'),
(3, 'Product C', '30'),
(4, 'Product D', 'twenty');

-- 执行查询,将 `price` 列中的值转换为 `DECIMAL` 类型,并观察结果。
mysql> SELECT id, name, price, CAST(price AS DECIMAL(10, 2)) AS price_as_decimal FROM products;
+----+-----------+--------+------------------+
| id | name      | price  | price_as_decimal |
+----+-----------+--------+------------------+
|  1 | Product A | 10     |            10.00 |
|  2 | Product B | 20     |            20.00 |
|  3 | Product C | 30     |            30.00 |
|  4 | Product D | twenty |             0.00 |
+----+-----------+--------+------------------+
4 rows in set, 2 warnings (0.01 sec)

-- 为了更加明确地验证转换过程中是否产生了警告信息,可以使用 `SHOW WARNINGS` 命令查看详细的警告信息。
mysql> SHOW WARNINGS;
+---------+------+------------------------------------------------------+
| Level   | Code | Message                                              |
+---------+------+------------------------------------------------------+
| Warning | 1366 | Incorrect DECIMAL value: '0' for column '' at row -1 |
| Warning | 1292 | Truncated incorrect DECIMAL value: 'twenty'          |
+---------+------+------------------------------------------------------+
2 rows in set (0.01 sec)

在这个查询结果中,我们可以看到 price 列的原始值和转换后的 price_as_decimal 列的值。对于 Product Dprice 列中的非数值字符串 'twenty' 被转换为了 0.00

  • Warning 1366 : 这个警告信息说明在将某个值转换为 DECIMAL 类型时,出现了不正确的 DECIMAL'0'。这通常是因为在转换过程中,MySQL 遇到了无法转换的字符串,并将其截断为 0
  • Warning 1292 : 这个警告信息明确指出 'twenty' 这个值被截断为不正确的 DECIMAL 值。这是由于 'twenty' 不是一个有效的数值字符串。

5.3 避免隐式转换的优势

  1. 明确性:显式转换可以使查询更明确和易读,减少误解和错误。
  2. 性能:显式转换可以减少不必要的计算和转换,提高查询性能。
  3. 一致性:通过显式转换,可以确保数据类型的一致性,避免数据类型混淆带来的问题。

References

相关推荐
云和数据.ChenGuang1 小时前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys1 小时前
sql server 查询对象的修改时间
运维·数据库·sqlserver
Hacker_LaoYi1 小时前
SQL注入的那些面试题总结
数据库·sql
建投数据2 小时前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
你的微笑,乱了夏天4 小时前
linux centos 7 安装 mongodb7
数据库·mongodb
工业甲酰苯胺4 小时前
分布式系统架构:服务容错
数据库·架构
菜鸡中的奋斗鸡→挣扎鸡5 小时前
滑动窗口 + 算法复习
数据结构·算法