一次sql请求,返回分页数据和总条数

日常搬砖,总少不了需要获取分页数据和总行数。
一直以来的实践是编码两次sql请求,分别拉分页数据和totalCount。
最近我在思考:
常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新,显而易见的优势:
① 能显著减低“客户端和服务器之间的网络往返次数”,提高吞吐量
② 简化客户端代码逻辑
1. mysql 默认单sql请求单语句
mysql客户端选项client_multi_statements默认为false:会禁止多条 SQL 语句的执行,这意味着在单个sql请求中只有第一条 SQL 语句会被执行,后续的 SQL 语句将被忽略。
这是一种提高数据库操作安全性的方法,可以有效防止 SQL 注入攻击和意外执行多条语句带来的风险。
MySQL客户端支持修改这样的设定 :client_multi_statements=true。

劣势:存在sql注入的风险, 错误处理比较复杂。
(1) go-sql-driver开启多语句支持: multiStatements=true
(2)
SELECT *  FROM `dict_plugin`  limit  20 ,10;
SELECT count(*) as  totalCount  from `dict_plugin`;将会形成2个数据集,golang的实践如下:
results, err = p.Query(querystring)for results.Next() {err = results.Scan(&...)}if !results.NextResultSet() {log.ErrorF(ctx, "expected more result sets: %v", results.Err())}for results.Next() {err = results.Scan(&totalCount)}既然提到了开启client_multi_statements 有sql注入的风险,我们就展开聊一聊。
2. sql注入
我们先看下sql注入的原理:
有这样的业务sql:
var input_name string
query: = "select  * from user where user_name='" + input_name+"'"
sql.Query(query)如果从界面输入的input_name="janus';delete from user;  --",
会形成恶意sql:select * from user where user_name='janus';delete from user;  --' 。
这个时候,客户端的client_multi_statements默认值为false就能于水火之间挽救数据库:执行第一个sql之后,后面的恶意sql都不会执行。
由此可知,client_multi_statements=false,确实可以显著降低sql注入的风险,但是还是没有办法避免单sql注入, 比如从界面密码框注入' OR '1'='1 会绕过登录认证。
query:= "select * from user where user='" + input_name +"' and  pwd='" +input_pwd +"'" select * from user where user='xxx' and pwd='' OR '1'='1'  -- 会绕过认证逻辑。3. 参数化查询防止sql注入
参数化查询可以防止sql注入风险[1]
// Correct format for executing an SQL statement with parameters.var queryStr = "SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?"
var args string = "55 union select * from `dict_plugin_Test`"rows, err := db.Query(queryStr, args)sql查询内部会利用提供的参数1创建预编译语句, 在运行时,实际是执行带参的预编译后的语句。
在服务器收到的查询日志如下:
2024-08-13T08:07:18.922818Z   26 Connect root@localhost on tcinfra_janus_sharing using TCP/IP
2024-08-13T08:07:18.924525Z   26 Prepare SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?
2024-08-13T08:07:18.924671Z   26 Execute SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = '55 union select * from `dict_plugin_Test`'
2024-08-13T08:07:18.925273Z   26 Close stmt判断mysql数据库开启了查询日志:show variables like '%general_log%';
打开sql查询日志的开关:set global general_log = on; 。
注意:参数占位符根据DBSM和驱动而有所不同,例如,Postgres 的pq驱动程序接受占位符形式是 $1而不是?。
3.1 预编译语句
数据库预编译后, SQL语义结构和数据分离,这样即使输入包含恶意代码,它也只会被当作数据处理,不会影响已经被解析固定的SQL语义结构。
预编译语句包含两次 sql交互:
① 预编译阶段(Prepare Phase):
- 客户端向服务器发送一个包含 SQL 语句(带有参数占位符)的请求。 
- sql服务器对SQL 语句进行语法和语义检查,然后对其进行预编译,并为其分配一个标识符(Statement ID)。 
- 服务器返回一个确认响应,表示预编译语句已经成功准备好。 
② 执行阶段(Execute Phase):
- 客户端发送执行请求,包含预编译语句的标识符和实际参数值。 
- 服务器将参数值绑定到预编译语句的占位符上,然后执行该语句。 
- 服务器返回执行结果(如结果集或影响的行数)。 
图示如下:
客户端                          服务器|                               ||----预编译语句(Prepare)------>||                               ||<-------确认响应(OK)----------||                               ||---执行语句(Execute) + 参数---->||                               ||<----------查询结果-------------|我们了解到预编译语句,将SQL语义和数据分离,通过两次sql交互(在预编译阶段固定了sql语义结构), 有效防止了SQL注入攻击, 另一方面,预编译语句在重复执行某一sql语句时确实有加快查询结果的效果。
golang的预编译的写法与常规的sql查询类似:
stmt, err := p.Prepare("SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?")
var args string = "55 union select * from `dict_plugin_Test`"
results, err := stmt.Query(args)
if err != nil {fmt.Printf("query fail: %v", err)return err
}
defer stmt.Close()for results.Next() {err = results.Scan(.....)......
}btw, C#  其实也支持预编译语句版本的sqlCommand:SqlCommand.Prepare()
总结
本文通过我们最初开始数据库编程时的一个实践, 提出在【一次sql请求中执行多次sql查询】的猜想;
了解到client_multi_statements= false 确实能避免一部分sql注入风险;
之后落地到sql注入的原理, 给出了参数化查询(预编译语句)能防止sql注入的核心机制。
参考资料
[1]
参数化查询可以防止sql注入风险: https://go.dev/doc/database/sql-injection

自古以来,同步/异步都是八股文第一章
async/await 贴脸输出,这次你总该明白了
流量调度、微服务可寻址性和注册中心
Go语言正/反向代理的姿势
两将军问题和TCP三次握手
"家长进校园"之《计算机和人工智能》

点“赞” 戳“在看”
戳“在看”
相关文章:
 
一次sql请求,返回分页数据和总条数
日常搬砖,总少不了需要获取分页数据和总行数。 一直以来的实践是编码两次sql请求,分别拉分页数据和totalCount。 最近我在思考: 常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新,显而易见的优势: ① 能…...
2.5 pyautogui 实现微信自动回复
第四节:实战微信自动回复 课程目标 学习如何通过pyautogui完成微信自动回复 课程内容 编码实现 import pyautogui as pg import time from pyautogui import ImageNotFoundException import pyperclip from cnocr import CnOcr import random ocr CnOcr() msg…...
 
观存储历史,论数据未来
数据存储 这几天我反复观看了腾讯云社区的《中国数据库前世今生》纪录片,每次的感受都大相径庭。以下是我在这段时间里对纪录片的两个不同感想,希望感兴趣的小伙伴们也能去观看一番。 一个是关于国产数据库的发展趋势的探讨:https://blog.c…...
linux:对目录的操作
一、对目录操作 1,打开目标目录 2.读取目录,, 3.关闭目录 目录 当文件看,只不过操作函数和操作文件函数不一样。 *1.opendir DIR *opendir(const char *name); 功能:打开一个目录获得一个目录流指针 参数:name:目录名 返回值…...
 
详解Redis 高可用的方式 Redis Cluster
Redis 高可用方式 Redis 提供了多种高可用性方案,主要包括以下几种方式: 主从复制(Replication) 主从复制是最基本的高可用性方案,通过将数据从一个主节点复制到多个从节点来实现数据的冗余和读写分离。主节点负责所…...
$clog2(1)=0
项目场景: 写ip 时, 使用参数化的方式实现2w1r 时,出现计算读返回index 时,减下溢! 问题描述 verilog中会使用parameter 参数化,例如使用dpth 和$clog2(dpth)addr 。 常见的写法没有什么问题。 module …...
 
开发学习日记1
用这个系列博客记录下学习开发的一些小收获 git的使用: 说来惭愧,学到了大二,git的使用还是一团糟,记录一下如何使用git进行团队合作开发 当要加入其他人的项目时首先你要创建自己的分支(克隆一下其他分支ÿ…...
 
孙宇晨领航波场TRON:引领数字资产迈向崭新纪元
 在风起云涌的数字资产领域,孙宇晨这个名字始终与创新、突破和引领紧密相连。作为波场TRON的创始人,他不仅是一位远见卓识的领导者,更是推动数字资产迈向新纪元的坚实力量。 自波场TRON诞生以来,孙宇晨便以其敏锐的洞察力…...
python运维(twenty-four day)
一、python基础 1、环境python2、python3 [rootpython ~]# yum list installed | grep python #检查是否有python包 [rootpython ~]# yum list installed | grep epel #检查是否有epel包 [rootpython ~]# yum -y install epel-release [rootpython ~]# yum -y instal…...
Eureka原理实践
1. 简介 1.1. 概述 Eureka是Netflix开源的一个服务注册与发现框架,它在微服务架构中扮演着至关重要的角色。 Eureka由两个核心组件组成: Eureka Server(服务注册中心):负责存储、管理和提供服务实例信息,如服务名、IP地址、端口号等。Eureka Server通常采用集群部署以保…...
Ant-Design-Vue快速上手指南+排坑
1. 简介 1.1. 概述 Ant-Design-Vue是由阿里巴巴开源的一个基于Vue.js框架的企业级UI设计语言。它旨在帮助开发者构建设计优雅、体验流畅的企业级应用。Ant-Design-Vue提供了一系列高质量的Vue组件,包括表单、表格、布局、导航、图标等,可以帮助开发者快速搭建应用程序界面。…...
 
mysql5.7安装
1.创建一个software文件 2.先下载mysql的repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 3安装源包 rpm -ivh mysql-community-release-el7-5.noarch.rpm 可能会报错 改成命令 rpm -ivh mysql-community-release-el7-5.noarch.rpm --nodeps…...
 
UE开发中的设计模式(三) —— 对象池模式
在FPS游戏中,射击会生成子弹,在命中敌人后子弹会被销毁,那么会导致子弹对象频繁地创建和销毁,会造成运行效率降低且会产生内存碎片问题,而对象池模式可以很好地解决这个问题。 文章目录 问题提出概述问题解决总结 问题…...
Mocha测试框架:JavaScript自动化测试的瑞士军刀
在JavaScript开发中,自动化测试是确保代码质量和可靠性的关键环节。Mocha是一个广泛使用的JavaScript测试框架,它支持多种断言库,允许开发者编写简洁、灵活的测试用例。Mocha特别适用于Node.js环境,但也可以在浏览器中运行。本文将…...
flask实现Streaming内容传输
当传输大量内存,以至于超出内存大小,一般http服务器会报500错误,这时可以使用Streaming流的方式来传输内容,类似ChatGPT和视频流那样的输出方式,flask里要用到生成器和直接响应。 from flask import stream_with_cont…...
 
seata的使用(SpringBoot项目整合seata)
文章目录 1、解压 seata-server-1.7.1.zip2、启动 双击 seata-server.bat3、启动 seata 控制台用户界面4、所有分布式事务相关数据库要有undo-log5、项目引入seata依赖6、项目添加seata配置7、代码实现: 1、解压 seata-server-1.7.1.zip 2、启动 双击 seata-server.…...
 
docker容器和宿主机网络不通
防火墙未开启,检查网络配置无异常 解决: [rootlocalhost ~]# vim /etc/sysctl.confnet.bridge.beidge-nf-call-iptables 1 net.bridge.beidge-nf-call-ip6tables 1[rootlocalhost ~]# sysctl -p [rootlocalhost ~]# systemctl restart docker 如果网…...
编程学习之旅:高效记录与整理笔记的艺术
引言:知识的海洋与导航的灯塔 在编程的浩瀚星空中,每一位学习者都像是勇敢的航海家,驾驶着知识的帆船,在无尽的信息海洋中探索未知的领域。然而,这片海洋既充满了机遇,也潜藏着挑战。信息的过载、知识的碎…...
 
dev c++中,在C++11模式下编译带M_PI宏的文件报错的解决办法
一、问题描述 当使用C11的模式,编译引用了math库中的M_PI的源文件时,报M_PI未声明的错误。 二、问题原因 因为M_PI是GNU扩展的宏,它不属于C11的标准,而-stdc11,表示以C11的标准进行编译,因此会产生以上问…...
【ubutnu24.04】k8s部署2:摸索修复问题
1.30.0 一直init失败有人说版本兼容问题重新安装了最新的1.31.0 版本kubeadm init 仍旧失败。安装依赖项 sudo apt-get install -y apt-transport-https ca-certificates curl gpgroot@PerfSvr:/home/zhangbin/perfwork/k8sadmin# sudo apt-get install -y apt-transport-https…...
 
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
 
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
 
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
 
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
 
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
 
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
 
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
