(202307)wonderful-sql:复杂一点的查询(task3)

数据库基础:视图与子查询的应用与理解
本文介绍了数据库中的视图和子查询概念,包括它们的作用和创建方法。视图提供了一种虚拟表的形式,方便数据的检索和安全性,而子查询是嵌套在其他查询中的查询,用于过滤和计算条件。文章还提供了相关练习题,帮助读者巩固对视图和子查询的理解,并讨论了涉及NULL值的运算规则。

教程链接:Datawhale - 一个热爱学习的社区

知识学习

1 视图

视图是一张虚拟的表。《sql基础教程第2版》用一句话非常凝练的概括了视图与表的区别---“是否保存了实际的数据”。

  1. 通过定义视图可以将频繁使用的SELECT语句保存以提高效率。
  2. 通过定义视图可以使用户看到的数据更加清晰。
  3. 通过定义视图可以不对外公开数据表全部字段,增强数据的保密性。
  4. 通过定义视图可以降低数据的冗余。

创建视图的基本语法是

CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>

注意:可以在视图的基础上再创建视图;需要注意的是在一般的DBMS中定义视图时不能使用ORDER BY语句,因为视图和表一样,数据行都是没有数据的;

我们也可以创建多表视图,使用WHERE进行条件控制

CREATE VIEW view_shop_product(product_type, sale_price, shop_name)
AS
SELECT product_type, sale_price, shop_name
  FROM product,
       shop_product
 WHERE product.product_id = shop_product.product_id;

2 子查询

子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL 4.1 开始引入,在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。

与视图的关系:子查询就是将用来定义视图的 SELECT 语句直接用于 FROM 子句当中。其中AS studentSum可以看作是子查询的名称,而且由于子查询是一次性的,所以子查询不会像视图那样保存在存储介质中, 而是在 SELECT 语句执行之后就消失了。

小结

视图和子查询是数据库操作中较为基础的内容,对于一些复杂的查询需要使用子查询加一些条件语句组合才能得到正确的结果。但是无论如何对于一个SQL语句来说都不应该设计的层数非常深且特别复杂,不仅可读性差而且执行效率也难以保证,所以尽量有简洁的语句来完成需要的功能。

练习题-第一部分

3.1 创建出满足下述三个条件的视图

CREATE VIEW ViewPractice5_1 AS
SELECT product_name, sale_price, regist_date
FROM product
WHERE sale_price >= 1000
    AND regist_date = '2009-09-20';

输入 SELECT * FROM ViewPractice5_1; 检查结果

3.2 向习题一中创建的视图 ViewPractice5_1 中插入如下数据,会得到什么样的结果?为什么?

INSERT INTO ViewPractice5_1 VALUES (' 刀子 ', 300, '2009-11-02');

结果

[2023-07-26 14:18:18] [HY000][1423] Field of view 'shop.ViewPractice5_1' underlying table doesn't have a default value

 翻译为底层的表没有默认的值,推测因为这个视图底层的表有的列要求非空,而这里给不出值,就不能了。由此也可知道修改视图也会修改底层的表。

3.3 请根据如下结果编写 SELECT 语句,其中 sale_price_avg 列为全部商品的平均销售单价。

SELECT product_id, product_name, sale_price, (SELECT AVG(sale_price) FROM product) AS sale_price_avg
FROM product;

 3.4 请根据习题一中的条件编写一条 SQL 语句,创建一幅包含如下数据的视图(名称为AvgPriceByType)。

CREATE VIEW AvgPriceByType(product_id,product_name,product_type,sale_price,sale_price_avg_type)
AS
SELECT product_id,product_name,product_type,sale_price,
(SELECT avg(sale_price) FROM product AS pm WHERE pm.product_type = pn.product_type GROUP BY product_type) 
    AS sale_price_avg_type FROM product AS pn

练习题-第二部分

3.5 四则运算中含有 NULL 时(不进行特殊处理的情况下),运算结果是否必然会变为NULL ?

 是的,四则运算中含有NULL,不进行特殊处理,运算结果是必然变成NULL。

3.6 对本章中使用的 product(商品)表执行如下 2 条 SELECT 语句,能够得到什么样的结果呢

SELECT product_name, purchase_price
  FROM product
 WHERE purchase_price NOT IN (500, 2800, 5000);

返回product_name和purchase_price两列,行的数据满足其原本在product表中的purchase_price不是500,2800,500和NULL。

SELECT product_name, purchase_price
  FROM product
 WHERE purchase_price NOT IN (500, 2800, 5000, NULL);

NULL不等于任何值,无法判断即使不是500,2800,5000的那些值是否为NULL。

可以用NOT EXISTS字句来查找,因为在子查询中使用等于操作符时,NULL 值将被视为与其他 NULL 值不相等。例如:

SELECT * FROM mytable WHERE NOT EXISTS (SELECT * FROM mytable2 WHERE mytable2.col1 = mytable.col1);

3.7 编写语句将商品分为三档

SELECT SUM(CASE WHEN sale_price <= 1000 THEN 1 ELSE 0 END) AS low_price,
       SUM(CASE WHEN sale_price BETWEEN 1001 AND 3000 THEN 1 ELSE 0 END ) AS mid_price,
       SUM(CASE WHEN sale_price >= 3001 THEN 1 ELSE 0 END) AS high_price
FROM product;

结果

 

<template> <div class="main-layout"> <div class="body-layout"> <Sidebar /> <div class="crud-container"> <div class="welcome"> <!-- 公共头部 --> <HeaderBar :devices="devices" @search="handleSearch" @search-reset="handleSearchReset" /> <el-main style="padding: 20px; display: flex; flex-direction: column"> <div> <!-- 首页内容 --> <div class="add-device"> <div class="add-device-bg"> <div class="hellow-text" style="margin-top: 30px"> 你好,小智 </div> <div class="hellow-text"> 让我们度过 <div style="display: inline-block; color: #5778ff"> 美好的一天! </div> </div> <div class="hi-hint">Hello, Let&#39;s have a wonderful day!</div> <div class="add-device-btn"> <div class="left-add" @click="showAddDialog"> 添加智能体 </div> <div style=" width: 23px; height: 13px; background: #5778ff; margin-left: -10px; " /> <div class="right-add"> <i class="el-icon-right" @click="showAddDialog" style="font-size: 20px; color: #fff" /> </div> </div> </div> </div> <div class="device-list-container"> <template v-if="isLoading"> <div v-for="i in skeletonCount" :key="&#39;skeleton-&#39; + i" class="skeleton-item" > <div class="skeleton-image"></div> <div class="skeleton-content"> <div class="skeleton-line"></div> <div class="skeleton-line-short"></div> </div> </div> </template> <template v-else> <DeviceItem v-for="(item, index) in devices" :key="index" :device="item" @configure="goToRoleConfig" @deviceManage="handleDeviceManage" @delete="handleDeleteAgent" @chat-history="handleShowChatHistory" /> </template> </div> </div> <AddWisdomBodyDialog :visible.sync="addDeviceDialogVisible" @confirm="handleWisdomBodyAdded" /> </el-main> <el-footer> <version-footer /> </el-footer> <chat-history-dialog :visible.sync="showChatHistory" :agent-id="currentAgentId" :agent-name="currentAgentName" /> </div> </div> </div> </div> </template> <script> import Api from "@/apis/api"; import AddWisdomBodyDialog from "@/components/AddWisdomBodyDialog.vue"; import ChatHistoryDialog from "@/components/ChatHistoryDialog.vue"; import DeviceItem from "@/components/DeviceItem.vue"; import HeaderBar from "@/components/HeaderBar.vue"; import VersionFooter from "@/components/VersionFooter.vue"; import Sidebar from "@/components/SideBar.vue"; export default { name: "HomePage", components: { Sidebar, DeviceItem, AddWisdomBodyDialog, HeaderBar, VersionFooter, ChatHistoryDialog, }, data() { return { addDeviceDialogVisible: false, devices: [], originalDevices: [], isSearching: false, searchRegex: null, isLoading: true, skeletonCount: localStorage.getItem("skeletonCount") || 8, showChatHistory: false, currentAgentId: "", currentAgentName: "", }; }, mounted() { this.fetchAgentList(); }, methods: { showAddDialog() { this.addDeviceDialogVisible = true; }, goToRoleConfig() { // 点击配置角色后跳转到角色配置页 this.$router.push("/role-config"); }, handleWisdomBodyAdded(res) { this.fetchAgentList(); this.addDeviceDialogVisible = false; }, handleDeviceManage() { this.$router.push("/device-management"); }, handleSearch(regex) { this.isSearching = true; this.searchRegex = regex; this.applySearchFilter(); }, handleSearchReset() { this.isSearching = false; this.searchRegex = null; this.devices = [...this.originalDevices]; }, applySearchFilter() { if (!this.isSearching || !this.searchRegex) { this.devices = [...this.originalDevices]; return; } this.devices = this.originalDevices.filter((device) => { return this.searchRegex.test(device.agentName); }); }, // 搜索更新智能体列表 handleSearchResult(filteredList) { this.devices = filteredList; // 更新设备列表 }, // 获取智能体列表 fetchAgentList() { this.isLoading = true; Api.agent.getAgentList( ({ data }) => { if (data?.data) { this.originalDevices = data.data.map((item) => ({ ...item, agentId: item.id, })); // 动态设置骨架屏数量(可选) this.skeletonCount = Math.min( Math.max(this.originalDevices.length, 3), // 最少3个 10 // 最多10个 ); this.handleSearchReset(); } this.isLoading = false; }, (error) => { console.error("Failed to fetch agent list:", error); this.isLoading = false; } ); }, // 删除智能体 handleDeleteAgent(agentId) { this.$confirm("确定要删除该智能体吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(() => { Api.agent.deleteAgent(agentId, (res) => { if (res.data.code === 0) { this.$message.success({ message: "删除成功", showClose: true, }); this.fetchAgentList(); // 刷新列表 } else { this.$message.error({ message: res.data.msg || "删除失败", showClose: true, }); } }); }) .catch(() => {}); }, handleShowChatHistory({ agentId, agentName }) { this.currentAgentId = agentId; this.currentAgentName = agentName; this.showChatHistory = true; }, }, }; </script> <style scoped> .main-layout { display: flex; flex-direction: column; height: 100vh; } .body-layout { display: flex; flex: 1; min-height: 0; } .crud-container { flex: 1; background: #f5f7fa; min-width: 0; min-height: 0; overflow: auto; } .welcome { min-width: 900px; min-height: 506px; height: 100vh; display: flex; flex-direction: column; background: linear-gradient(145deg, #e6eeff, #eff0ff); background-size: cover; /* 确保背景图像覆盖整个元素 */ background-position: center; /* 从顶部中心对齐 */ -webkit-background-size: cover; /* 兼容老版本WebKit浏览器 */ -o-background-size: cover; /* 兼容老版本Opera浏览器 */ } .add-device { height: 195px; border-radius: 15px; position: relative; overflow: hidden; background: linear-gradient( 269.62deg, #e0e6fd 0%, #cce7ff 49.69%, #d3d3fe 100% ); } .add-device-bg { width: 100%; height: 100%; text-align: left; background-image: url("@/assets/home/main-top-bg.png"); overflow: hidden; background-size: cover; /* 确保背景图像覆盖整个元素 */ background-position: center; /* 从顶部中心对齐 */ -webkit-background-size: cover; /* 兼容老版本WebKit浏览器 */ -o-background-size: cover; box-sizing: border-box; /* 兼容老版本Opera浏览器 */ .hellow-text { margin-left: 75px; color: #3d4566; font-size: 33px; font-weight: 700; letter-spacing: 0; } .hi-hint { font-weight: 400; font-size: 12px; text-align: left; color: #818cae; margin-left: 75px; margin-top: 5px; } } .add-device-btn { display: flex; align-items: center; margin-left: 75px; margin-top: 15px; cursor: pointer; .left-add { width: 105px; height: 34px; border-radius: 17px; background: #5778ff; color: #fff; font-size: 14px; font-weight: 500; text-align: center; line-height: 34px; } .right-add { width: 34px; height: 34px; border-radius: 50%; background: #5778ff; margin-left: -6px; display: flex; justify-content: center; align-items: center; } } .device-list-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 30px; padding: 30px 0; } /* 在 DeviceItem.vue 的样式中 */ .device-item { margin: 0 !important; /* 避免冲突 */ width: auto !important; } .footer { font-size: 12px; font-weight: 400; margin-top: auto; padding-top: 30px; color: #979db1; text-align: center; /* 居中显示 */ } /* 骨架屏动画 */ @keyframes shimmer { 100% { transform: translateX(100%); } } .skeleton-item { background: #fff; border-radius: 8px; padding: 20px; height: 120px; position: relative; overflow: hidden; margin-bottom: 20px; } .skeleton-image { width: 80px; height: 80px; background: #f0f2f5; border-radius: 4px; float: left; position: relative; overflow: hidden; } .skeleton-content { margin-left: 100px; } .skeleton-line { height: 16px; background: #f0f2f5; border-radius: 4px; margin-bottom: 12px; width: 70%; position: relative; overflow: hidden; } .skeleton-line-short { height: 12px; background: #f0f2f5; border-radius: 4px; width: 50%; } .skeleton-item::after { content: ""; position: absolute; top: 0; left: 0; width: 50%; height: 100%; background: linear-gradient( 90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0) ); animation: shimmer 1.5s infinite; } </style> 在代码不变的情况下实现懒加载
08-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早上真好

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值