位运算在数据库中的运用实践-以MySQL和PG为例

目录

前言

一、两种不同的数据库设计

1、状态字段存储JSON

2、使用位运算

二、数据库中的位运算实践

1、MySQL中的位运算实践

2、PostgreSQL中位运算实践

三、总结


前言

        最近在解决某用户的一个业务需求时,遇到一个很有意思的场景。首先先跟大家分享一下需求背景。用户主要是面向一线的企业工厂,在他们实际生产过程中,为了保障安全生产。由于在车间中,所有的设备和机器都是全天运行,因此特别容易出现运行故障。因此,安全管理部门呢就结合生产时间,将组织专门的人员在上班时间内实现对运行设备的一个巡视,每个班组上班时间为8小时,同时要求每4个小时就要对设备进行一次巡查。根据上班时间分为早班、中班、晚班,每个班由1到多个人员组成。要求在他们上班后的一个小时内和快下班的一个小时内实现对目标设备的巡视,系统需要记录每次的检查记录,比如早班第一次和早班第二次等等。同时呢,在生产高峰期,由于订单的增加,有的机器运行更加频繁,因此要求加大巡视力度,巡视次数增加至四次,即两个小时巡视一次。同样要求系统记录不同次数的状态,一天巡查结束后,系统自动提供巡视结果,能反应出应巡的次数和漏巡的次数,工厂的管理班组将根据情况对相应的车间和安全管理人员进行考核和评价,纳入到月的的绩效考核当中,对于提早发现的故障信息,处置得当的给予奖励和奖金。

        以上就是大致的需求,其实刚开始拿到这个需求的时候,对于状态的记录到底用什么字段来存储,如何能达到快速保存状态和检索。比如可以快速的设置第一次和第二次的巡视状态为已完成。同时在查询时能快速查询第N次是否已完成等等操作。在实际业务中可有哪些选择来支持以上的需求,既能满足业务需求,也能较少技术的复杂度。

        这就是本文的理由,本文以一个实际的工厂日常检查工作的状态标记场景为例,首先讲解可以有哪些技术方案来实现上述需求,然后讲解表结构的设计,其次着重介绍位运算的表设计方式,以及位运算的处理方式,最后以MySQL和PostgreSQL为例介绍如何在这两个数据库中实现位运算,通过实际例子的讲解,让朋友理解位运算在数据库状态位的场景中的具体使用。通过本文,您可以掌握在面对状态标记时的数据库设计方法,如何在位运算中体现多次,如何操作位运算来设置值,以及如何快速查询定位等知识。

一、两种不同的数据库设计

        本节将根据拿到的用户初步需求,对需求进行分析,根据分析结果完成数据库的设计,同时具体阐述如何去进行表状态字段的更新和查询操作。设计没有好坏,根据不同的场景,有不同的应用。这里欢迎各位朋友进行讨论。由于在实际情况下,在上面的巡视工作中,根据不同的工作需求,每个组的巡视次数可能不固定的,所以这里要考虑实现次数的动态标记的需求。

1、状态字段存储JSON

        之所以考虑使用JSON的方式来进行存储,第一个考虑就是实现灵活的状态,如果是固定的次数,比如每个班就巡视两次,那么我们不妨设计出多个字段,比如早班第一次状态,早班第二次状态,一直到早班第N次,如果N有限,我们的字段尚且还可以设计出来,如果N是一个不固定的值,那么这种设计也是一种灾难。这里我想可能有朋友会说,多设计一些冗余字段是否可行,比如一次性设计8个字段出来。诚然,这种方案是没有大毛病,但是你想想这些问题,万一超了怎么办,还有就是一个班组8个字段,3个班组就是24个字段,光用来标记状态的字典就有24个,再加上其它的业务字段,这就是25+了,如此设计不仅浪费,同时查询性能也低,扩展性也不好。

        这个方式的改良办法就是将多字段改成JSON,通过JSON的存储来实现动态的次数,比如{“1”:0,“0”:1}用这样的字符串表示第一次已完成,第二次未完成。然后在查询的时候每次只需要进行json的关联查询即可,性能暂时还在可以接受的范围之内。你可以想想使用JSON存储值究竟有什么问题。

2、使用位运算

        其实在上面一节中已经大体讲了一下,在我们的业务中,我们只要顺序的记录每一次任务的执行状态,比如用0表示未完成,1表示已完成。0和1是二进制中最简单的表示,应用到应用程序中也是,使用他们来进行数据查询和检索,速度也是非常快的。而且也能实现动态的效果。比如,我们设计一个8位的二进制数,如下所示:

第8位第7位第6位第5位第4位第3位第2位第1位
00000000

        在上面的二进制数表示中,我们采用8位(可以根据实际情况增加长度)来表示对应次数的状态位,第一位表示第一次的状态,0表示未完成。在设计字段时,我们会将状态只设计成一个字段,它的值则有这8位二进制数转成对应的十进制数来表示,这样子不仅大大的减少了字段数,同时还能实现不同次数的状态分别记录。下面举个例子:

        第一次巡视已完成的二进制表示如下:

第8位第7位第6位第5位第4位第3位第2位第1位
00000001

        这里的第一位表示第一次为1,其它的仍为0,这是计算出来的二进制值是1。下面再来举一个例子,我们将第二次和第四次的状态设置为1,则8位的二进制表示如下:

第8位第7位第6位第5位第4位第3位第2位第1位
00001010

        此时,00001010这个数字换算成十进制的值为10,也就是十进制10表示二进制的00001010,表示该班组的第4次和第2次的巡视工作已完成,其它次数尚未完成。通过以上例子的讲解,您是否发现,使用位运算是否极大的简化了相关的数据库设计,也降低了数据的数据更新和检索的难度。因此我们在此情况下决定采用位运算的方式进行对应工作状态的标记。

二、数据库中的位运算实践

        前一节详细的介绍了我们选择的两种方案,也重点比较了两种方案的不同,优缺点也都进行了说明。当然,以上两种方案都可以实现业务需求,也能实现动态灵活的方案,但是相比于复杂度,我们选择位运算来实现。

        本节将结合MySQL和PostgreSQL数据库来分别详细讲解如何在这两个数据库中实现位运算,如何在这两个数据库中设置位运算结果和查询位状态。通过本节大家可以了解在MySQL和PostgreSQL数据库中熟练的进行位运算的操作。

1、MySQL中的位运算实践

        这里使用的MySQL的版本是5.7.14-x的版本,位运算是基础的计算,在更高级的版本中应该都是兼容的。本博客使用MySQL 5.7来做实验环境。

        查询MySQL版本,使用以下sql:

select VERSION();

5.7.14-log

        我们首先来创建一张表,表仅用作演示,不代表实际的业务,实际的业务表还请各位朋友自己去设计。主要体现的位运算的处理过程,表的物理结构如下所示:

CREATE TABLE `example_table` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `status` INT NOT NULL DEFAULT 0
);

        表结构非常简单,只有两个字段,第一个字段是主键ID,设置位自增,而第二个字段为状态位,存储的值是十进制的表示数,请注意,这里的数据类型请结合具体的二进制状态位的计算过来定,int是一个非常大的数字了,用来存储是足够了,它的默认值用0表示,因为二进制8个0对应的十进制数也是0。

        首先我们查询一下表的数据,默认情况下,表里是没有数据的,需要我们手动插入数据,插入数据后的表数据如下:

select * from example_table;
idstatus
10
20
30
40
50
60
70

        下面我们来修改表记录的值,比如我们设置id为3的数据,第一次为1即标记已完成。sql语句如下:

-- 设置第N次为已完成 正确的做法
UPDATE example_table SET status = status | (1 << (N - 1)) WHERE id = 3;

-- N表示具体的次数,即N=1
UPDATE example_table SET status = status | (1 << (1 - 1)) WHERE id = 3;

        在数据库客户端执行以下sql之后,客户端返回如下:

UPDATE example_table SET status = status | (1 << (1 - 1)) WHERE id = 3
> Affected rows: 1
> 时间: 0.089s

        表名id=3的这条记录已经发生了变更且更新成功。为了验证这个结果是不是二进制的正确表达呢?我们选择在数据库中进行进制转换的查询展示:

select *, CONCAT(REPEAT('0', 8 - CHAR_LENGTH(BIN(status))),BIN(status)) AS binary_status FROM example_table ;

        这里用到的函数有三个,最里面的是bin函数,表示将值转为二进制,然后用char_length函数求出转换出来的二进制数长度,再repeat函数和concat函数,最终拼成一个二进制字符串的表示,总的长度为8位,如果位数不足,则在前面补0,比如十进制0,二进制表达为:00000000。十进制1,二进制表示位:00000001。在客户端中执行以上的sql后可以在返回的结果中看到如下:

idstatusbinary_status
1000000000
2000000000
3100000001
4000000000
5900001001
6000000000
7000000000

        到这里,我们已经实现了状态位运算的动态更新,比如指定第几位为1,那么与之对应的另一个问题就是,如何查询出第几位为已完成。同样的我们也还是需要使用位运算,计算的方法如下:

-- 查询第N次是否完成,N表示第几次
select *, CONCAT(REPEAT('0', 8 - CHAR_LENGTH(BIN(status))),BIN(status)) AS binary_status FROM example_table 
WHERE (status & (1 << (N - 1))) > 0 ;

-- 查询第N次是否完成,1表示第1次即N=1
select *, CONCAT(REPEAT('0', 8 - CHAR_LENGTH(BIN(status))),BIN(status)) AS binary_status FROM example_table 
WHERE (status & (1 << (1 - 1))) > 0 ;

        上述查询的结果如下:

idstatusbinary_status
3100000001
5900001001

        到此,关于如何在MySQL中进行位运算的设置以及查询的效果演示就到此结束,关于其它的位运算可以参考其它网页的资料。

2、PostgreSQL中位运算实践

        在讲解了位运算在MySQL中的应用之后,下面也来讲讲在PG的运用。其实位运算在SQL中的运用效果是差不多的,MySQL和PG的位运算过程效果差别不大,为什么这里还要拿出来讲呢?主要是在PG中,要想实现二进制字符串的展示不太直观,这里分享一种在PG中的处理方式,供大家参考。

        本文使用的PG版本如下:

        查询sql:SELECT version(); 执行后查询结果如下:

PostgreSQL 12.3, compiled by Visual C++ build 1914, 64-bit

        与MySQL一样的,为了演示上述的效果,我们依然创建一张Pg的表,相关DDL语言稍微有点区别:

CREATE TABLE "public"."example_table" (
  "id" int8 NOT NULL,
  "status" int4,
  CONSTRAINT "example_table_pkey" PRIMARY KEY ("id")
);

        在PG中设置第N次为已完成即为1的sql语句与MySQL是一致的,如下:

-- 设置第N次为已完成 正确的做法
UPDATE example_table SET status = status | (1 << (N - 1)) WHERE id = 6;

        这里有小伙伴会问,上面的SQL是设置为已完成,那么重置为0应该怎么实现呢?可以使用下面的sql来实现:

-- 设置第几次为0
UPDATE example_table SET status = status & (~(1 << (N - 1))) WHERE  id = 5;

        在PG数据库中,直接将二进制转换为十进制的字符串的方法没有,因此我们不能直接使用内置函数来完成,需要使用自定义函数的方式,自定义函数的逻辑与MySQL差不多,整体长度是8,不够的位数用0来补齐。函数的实现如下:

CREATE OR REPLACE FUNCTION "public"."decimal_to_binary_string"("num" int8)
  RETURNS "pg_catalog"."text" AS $BODY$
  
DECLARE  
    binary_str TEXT := '';  
    temp_num BIGINT := num;  
BEGIN  
    WHILE temp_num > 0 LOOP  
        -- 使用模2运算来获取最低位的二进制值  
        binary_str := CAST(temp_num % 2 AS TEXT) || binary_str;  
        -- 使用整除2来去掉已经处理的最低位  
        temp_num := temp_num / 2;  
    END LOOP;  
      
    -- 如果输入为0,则直接返回'0'  
    IF binary_str = '' THEN  
        binary_str := '0';  
    END IF;  
      
    RETURN binary_str;  
END;  
 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100

        在创建好以上的转换函数之后,在数据库中执行以下SQL:

select *, LPAD(decimal_to_binary_string(status), 8, '0') AS binary_status FROM example_table
order by id;

        查询结果如下:

idstatusbinary_status
1000000000
2000000000
3000000000
4000000000
5000000000

        到此,我们将如何在PG中实现位运算进行了详细的说明。

三、总结

        以上就是本文的主要内容,本文以一个实际的工厂日常检查工作的状态标记场景为例,首先讲解可以有哪些技术方案来实现上述需求,然后讲解表结构的设计,其次着重介绍位运算的表设计方式,以及位运算的处理方式,最后以MySQL和PostgreSQL为例介绍如何在这两个数据库中实现位运算,通过实际例子的讲解,让朋友理解位运算在数据库状态位的场景中的具体使用。通过本文,您可以掌握在面对状态标记时的数据库设计方法,如何在位运算中体现多次,如何操作位运算来设置值,以及如何快速查询定位等知识。行文仓促,难免有不足之处,如果有不足之处,还请各位专家朋友在评论区不吝赐教,不甚感激。

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

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

相关文章

记录一次mysql死锁问题的分析排查

记录一次死锁问题的分析排查 现象 底层往kafka推送设备上线数据应用层拉取设备上线消息,应用层有多个消费者并发执行将设备上线数据同步数据库表pa_terminal_channel日志报&#xff1a;&#xff08;Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: …

skywalking-1-服务端安装

skywalking很优秀。 安装服务端 skywalking的服务端主要是aop服务&#xff0c;为了方便查看使用还需要安装ui。另外采集的数据我们肯定要存起来&#xff0c;这个数据库就直接用官方的banyandb。也就是aop、ui、banyandb都使用官方包。 我们的目的是快速使用和体验&#xff0c…

【Go系列】 Go语言的入门

为什么要学习Go 从今天起&#xff0c;我们将一同启程探索 Go 语言的奥秘。我会用简单明了的方式&#xff0c;逐一讲解 Go 语言的各个知识点&#xff0c;帮助你从基础做起&#xff0c;一步步深化理解。不论你之前是否有过 Go 语言的接触经验&#xff0c;这个系列文章都将助你收获…

电脑引导坏了怎么修复?电脑引导坏了全自动修复教程

电脑怎么修复引导?我们知道目前电脑有两种引导模式legacy和uefi&#xff0c;所以会出现legacy和uefi引导修复的问题&#xff0c;随着uefi的流行&#xff0c;越来越多的小伙伴经常遇到电脑引导丢失的问题&#xff0c;也不知道怎么修复&#xff0c;以前的一些修复工具都只能修复…

【JavaEE】文件IO

&#x1f921;&#x1f921;&#x1f921;个人主页&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;JavaEE专栏&#x1f921;&#x1f921;&#x1f921; 文章目录 1.什么叫文件IO1.1IO的概念1.2文件的概念 2.用java来操作文件2.1文件的分类2…

Internet Download Manager6.42最新下载器互联网冲浪小能手们!

今天我要来种草一个超级棒的宝贝——Internet Download Manager&#xff08;简称 IDM&#xff09;。这个小家伙简直是下载界的“速度与激情”代言人&#xff0c;让我彻底告别了等待的日子。&#x1f389; IDM马丁正版下载如下: https://wm.makeding.com/iclk/?zoneid34275 …

本地部署,强大的面部修复与增强网络CodeFormer

目录 什么是 CodeFormer&#xff1f; 技术原理 主要功能 应用场景 本地部署 运行结果 结语 Tip&#xff1a; 在图像处理和计算机视觉领域&#xff0c;面部修复和增强一直是一个备受关注的研究方向。近年来&#xff0c;深度学习技术的飞速发展为这一领域带来了诸多突破性…

uniapp x — 跨平台应用开发的强大助力

摘要&#xff1a; 随着前端技术的不断演进&#xff0c;跨平台应用开发框架成为了提升开发效率、降低开发成本的重要工具。uni-app以其跨平台兼容性和丰富的功能受到了开发者的广泛青睐。然而&#xff0c;随着应用需求的日益增长&#xff0c;对框架的功能和性能要求也在不断提高…

防御---001

一、实验拓扑二、要求 1&#xff0c;DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00 - 18:00)可以访问&#xff0c;生产区的的设备全天可以访问. 2&#xff0c;生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网 3,办公区设备10.0.2.10不允许访问DMZ…

Errno2:No such file or directory,在当前文件确实没有该图片,怎么解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【论文速读】《面向深度学习的联合消息传递与自编码器》

这篇文章来自华为的渥太华无线先进系统能力中心和无线技术实验室&#xff0c;作者中有大名鼎鼎的童文。 一、自编码架构的全局收发机面临的主要问题 文章对我比较有启发的地方&#xff0c;是提到自编码架构的全局收发机面临的主要问题&#xff1a; 问题一&#xff1a;基于随…

Ae After Effects2024 for Mac 视频处理软件

Mac分享吧 文章目录 效果一、准备工作二、开始安装1、Anticc简化版安装1.1双击运行软件&#xff0c;安装1.2 解决来源身份不明的开发者问题1.3 再次运行软件&#xff0c;即可进行AntiCC安装 2. Ae2024安装2.1 打开 Ae 2024 安装包组2.2 将 Ae 安装包拖至桌面2.3 安装 Ae2024 &…

运维锅总详解进程、内核线程、用户态线程和协程

I/O 密集型应用、计算密集型应用应该用什么实现&#xff1f;进程、内核线程、用户态线程、协程它们的原理和应用场景又是什么&#xff1f;如何组合它们才能让机器性能达到最优&#xff1f;它们的死锁和竞态又是什么&#xff1f;如何清晰地表示它们之间的关系&#xff1f;希望读…

UnityHub 无法添加模块问题

文章目录 1.问题描述2.问题解决 1.问题描述 在Hub中无法添加模块 2.问题解决 1、点击设置 2、设置版本安装位置 可以发现installs的安装位置路径设置不是unity安装位置&#xff0c;这里我们更改成自己电脑unity安装位置的上一级路径 添加模块正常&#xff1a;

第二证券:70万手封单,超3亿元资金盯上这只绩优股

今天A股商场收盘共50股涨停&#xff0c;剔除9只ST股后&#xff0c;41股涨停&#xff1b;25股封板未遂&#xff0c;全体封板率为67.78%。 涨停战场&#xff1a; 超3亿元资金封板盛屯矿业 据证券时报数据宝核算&#xff0c;从收盘涨停板封单量来看&#xff0c;盛屯矿业封单量最…

Vue从零到实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

电子电气架构 --- 关于DoIP的一些闲思 下

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

软件架构之软件架构概述及质量属性

软件架构之软件架构概述及质量属性 第 9 章&#xff1a;软件架构设计9.1 软件架构概述9.1.1 软件架构的定义9.1.2 软件架构的重要性9.1.3 架构的模型 9.2 架构需求与软件质量属性9.2.1 软件质量属性9.2.2 6 个质量属性及实现 第 9 章&#xff1a;软件架构设计 像学写文章一样&…

Zynq系列FPGA实现SDI视频编解码+图像缩放+多路视频拼接,基于GTX高速接口,提供8套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本博已有的FPGA图像缩放方案本方案的无缩放应用本方案在Xilinx--Kintex系列FPGA上的应用 3、详细设计方案设计原理框图SDI 输入设备Gv8601a 均衡器GTX 解串与串化SMPTE SD/HD/3G SDI IP核BT1120转RGB自研…

27. 738.单调递增的数字,968.监控二叉树,贪心算法总结

class Solution { public:int monotoneIncreasingDigits(int n) {string strNum to_string(n);// flag用来标记赋值9从哪里开始// 设置为这个默认值&#xff0c;为了防止第二个for循环在flag没有被赋值的情况下执行int flag strNum.size();for(int i strNum.size() - 1; i &…