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

news2024/11/15 4:52:31

目录

前言

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

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.coloradmin.cn/o/1915333.html

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

相关文章

ts语法---泛型和泛型约束

泛型 泛型&#xff0c;动态类型&#xff0c;是一个初始化不明确的类型&#xff0c;类似于函数中的形参&#xff08;不明确参数值&#xff09;&#xff0c; 泛型一般用在function定义函数时动态约束类型&#xff0c;和type定义类型时动态约束类型&#xff0c; 泛型一般使用任…

中小学校园EasyCVR视频综合监管方案:构建安全、智能的校园环境

一、背景需求分析 随着科技的快速发展&#xff0c;校园安全问题日益受到社会各界的关注。尤其是在中小学校园中&#xff0c;学生的安全更是牵动着每一个家庭的心。为了更有效地保障学生的安全&#xff0c;提高校园安全管理水平&#xff0c;视频监控系统在中小学中的应用越来越…

1.10编程基础之简单排序--02:奇数单增序列

OpenJudge - 02:奇数单增序列http://noi.openjudge.cn/ch0110/02/ 描述 给定一个长度为N(不大于500)的正整数序列,请将其中的所有奇数取出,并按升序输出。 输入 共2行: 第1行为 N; 第2行为 N 个正整数,其间用空格间隔。 输出 增序输出的奇数序列,数据之间以逗号间隔。数…

华为od100问持续分享-1

我是一名软件开发培训机构老师&#xff0c;我的学生已经有上百人通过了华为OD机试&#xff0c;学生们每次考完试&#xff0c;会把题目拿出来一起交流分享。 重要&#xff1a;2024年5月份开始&#xff0c;考的都是OD统一考试&#xff08;D卷&#xff09;&#xff0c;题库已经整…

GDidees CMS v3.9.1 本地文件泄露漏洞(CVE-2023-27179)

前言 CVE-2023-27179 是一个影响 GDidees CMS v3.9.1 及更低版本的任意文件下载漏洞。这个漏洞存在于 /_admin/imgdownload.php 文件中&#xff0c;攻击者可以通过向 filename 参数传递恶意输入来下载服务器上的任意文件。 漏洞的根源在于对用户输入的 filename 参数处理不当…

对于多个表多个字段进行查询、F12查看网页的返回数据帮助开发、数据库的各种查询方式(多对多、多表查询、子查询等)。

对于多个表多个字段进行查询、F12查看网页的返回数据帮助开发、数据库的各种查询方式&#xff08;多对多、多表查询、子查询等&#xff09;。 一、 前端界面需要展现多个表的其中几个数据的多表查询。1. 三个表查询其中字段返回&#xff1a;&#xff08;用一下sql语句&#xff…

scipy库中,不同应用滤波函数的区别,以及FIR滤波器和IIR滤波器的区别

一、在 Python 中&#xff0c;有多种函数可以用于应用 FIR/IIR 滤波器&#xff0c;每个函数的使用场景和特点各不相同。以下是一些常用的 FIR /IIR滤波器应用函数及其区别&#xff1a; from scipy.signal import lfiltery lfilter(fir_coeff, 1.0, x)from scipy.signal impo…

数据结构——约瑟夫环C语言链表实现

约瑟夫环问题由古罗马史学家约瑟夫&#xff08;Josephus&#xff09;提出&#xff0c;他参加并记录了公元66—70年犹太人反抗罗马的起义。在城市沦陷之后&#xff0c;他和40名死硬的将士在附近的一个洞穴中避难。起义者表示“宁为玉碎不为瓦全”&#xff0c;约瑟夫则想“留得青…

服务器提交记录有Merge branch消除

背景&#xff1a;在共同开发分支release上&#xff0c;你提交了commit&#xff0c;push到服务器上时&#xff0c;发现有人先比你push了&#xff0c;所以你得先pull&#xff0c; 后再push&#xff0c;然而pull后自动产生了一个Merge branch的一个commit&#xff0c;这个commit本…

Floyd算法——AcWing 343. 排序

目录 Floyd算法 定义 运用情况 注意事项 解题思路 基本步骤 AcWing 343. 排序 题目描述 运行代码 代码思路 改进思路 Floyd算法 定义 Floyd算法&#xff0c;全称Floyd-Warshall算法&#xff0c;是一种用于解决图中所有顶点对之间的最短路径问题的动态规划算法。…

配置Java开发环境

Java是一种广泛使用的编程语言&#xff0c;特别是在企业应用和安卓开发中。本文将详细介绍如何在您的计算机上配置Java开发环境&#xff0c;包括安装JDK、配置环境变量以及选择和设置IDE。 一、安装Java Development Kit (JDK) JDK&#xff08;Java Development Kit&#xff0…

用JavaScript将 NCR(Numeric Character Reference)标记转换为对应字符的方法

0 &#xff0c 、&#11111……是什么鬼&#xff1f; 最近&#xff0c;要将一些网页内容复制到<textarea>文本框中作进一步处理&#xff0c;发现有些网页内容中包含&#xff0c或之类的标记&#xff0c;会被原样复制到<textarea>文本框中。 如果将这些网页内容直…

AI Agent 的发展现状、行业结构与趋势分析

Agent 来自一种哲学概念&#xff0c;是个很古老的哲学术语&#xff0c;从哲学意义上讲&#xff0c;“代理”的概念涉及实体的自主性&#xff0c;具有行使意志、做出选择和采取行动的能力&#xff0c;而不是被动地对外部刺激做出反应。后来人们将这一概念引入计算机科学领域&…

285个地级市-胡焕庸线数据

全国285个地级市-胡焕庸线数据.zip资源-CSDN文库 胡焕庸线&#xff1a;中国人口与生态的分界线 胡焕庸线&#xff0c;一条在中国地理学界具有划时代意义的分界线&#xff0c;由著名地理学家胡焕庸于1935年提出。这条线从黑龙江省的瑷珲&#xff08;现黑河市&#xff09;延伸至…

又上热搜!曝iPhone 16将支持40W快充

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 7月9日晚&#xff0c;微博话题“iPhone16系列或将支持40W快充”上了热搜榜&#xff0c;这已经是iPhone 16系列第N次上热搜了。 据爆料&#xff0c;iPhone 16系列充电功率将提升至40W&#xff0c;并且…

Deepspeed : AttributeError: ‘DummyOptim‘ object has no attribute ‘step‘

题意&#xff1a;尝试在一个名为 DummyOptim 的对象上调用 .step() 方法&#xff0c;但是这个对象并没有定义这个方法 问题背景&#xff1a; I want to use deepspeed for training LLMs along with Huggingface Trainer. But when I use deepspeed along with trainer I get …

书生大模型实战营(暑假场)-入门岛-第二关

目录 关卡任务 任务一 任务二 总结 关卡任务 任务一&#xff1a; 请实现一个wordcount函数&#xff0c;统计英文字符串中每个单词出现的次数。返回一个字典&#xff0c;key为单词&#xff0c;value为对应单词出现的次数。 这个算是比较简单的了&#xff0c;代码如下所示 …

C基础day8

一、思维导图 二、课后习题 #include<myhead.h> #define Max_Stu 100 //函数声明 //学生信息录入函数 void Enter_stu(int *Num_Stu,char Stu_name[][50],int Stu_score[]); //查看学生信息 void Print_stu(int Num_Stu,char Stu_name[][50],int Stu_score[]); //求出成绩…

Rust vs Go: 特点与应用场景分析

目录 介绍Rust的特点Go的特点Rust的应用场景Go的应用场景总结 介绍 Rust和Go&#xff08;Golang&#xff09;是现代编程语言中两个非常流行的选择。凭借各自的独特优势和广泛的应用场景&#xff0c;吸引了大量开发者的关注。本文将详细介绍Rust和Go的特点&#xff0c;并探讨它…