记一次在数据库中查询:“包含”或者“仅包含”某些商品的订单的方法

Harrison Harrison     2022-08-19     623

关键词:

有这样一个需求:

  1. 从数据库中查出包含“商品1”和“商品2”的订单;
  2. 从数据库中查出包含“商品1”或“商品2”的订单;
  3. 从数据库中查出仅包含“商品1”和“商品2”的订单;
  4. 从数据库中查出仅包含“商品1”或“商品2”的订单;

这里只用“商品1”、“商品2”举例,可以扩展到多个商品的需求。

涉及到的表大概如下图:

该怎么做呢?以第一点需求为例,一种可行的方法是:先查出所有包含“商品1”的订单,然后遍历这些订单,选出包含“商品2”的订单,如果要查询包含更多商品的订单,需要进行多次遍历,层层筛选,效率低下。况且,在实际情况下,往往需要支持分页查询,这种方式基本不可行,或者实现起来很复杂。

第二种方法是这样的:假设查询包含“商品1”、“商品2”、“商品3”的订单,其goods_id 分别为1、2、3,sql如下:

SELECT * FROM `order` 
WHERE order_id IN ( 
SELECT tmp.order_id FROM ( SELECT order_id FROM order_item WHERE goods_id = 1 ) AS tmp 
INNER JOIN ( SELECT order_id FROM order_item WHERE goods_id = 2 ) AS t2 ON tmp.order_id = t2.order_id 
INNER JOIN ( SELECT order_id FROM order_item WHERE goods_id = 3 ) AS t3 ON tmp.order_id = t3.order_id 
)
LIMIT 0,10

如果要求仅包含的话,可以这样写:

SELECT * FROM `order` 
WHERE order_id IN ( 
SELECT tmp.order_id FROM ( SELECT order_id FROM order_item WHERE goods_id = 1 ) AS tmp 
INNER JOIN ( SELECT order_id FROM order_item WHERE goods_id = 2 ) AS t2 ON tmp.order_id = t2.order_id 
INNER JOIN ( SELECT order_id FROM order_item WHERE goods_id = 3 ) AS t3 ON tmp.order_id = t3.order_id 
WHERE (SELECT count(*) from order_item WHERE order_id = tmp.order_id) = 3
)
LIMIT 0,10

增加where条件,限制该订单只有三个商品即可。

以上说明的需求点1和3的实现方式,需求点2和4可以用类似的方式实现。

这种实现方式确实能够满足需求,但是有一个比较严重的问题:当关联的商品多了以后,多个inner join的使用,会使查询效率非常低,尤其是订单量大的时候,会更慢,如果再加上其他查询条件,如:下单时间、发货时间、订单状态等等各类条件以后,(此处已无法描述)。亲测查询7到8个商品,几十万订单的时候,已经慢到不要不要的了。

最后一种实现方式,也即本文的重点,这种方式需要对数据结构做一小小改动,如下图:

如图所示,goods表和order表都增加了一个字段:bit。

先说goods表中的bit,该字段表示对商品编码,取值为2的n次方(n>=0),假设goods表有5个商品,那么bit值依次为1、2、4、8、16、32.

再说order表中的bit,该字段表示该订单所包含商品的bit值之和,假设某订单包含bit值为1和2的商品,那么其bit为3.

如何实现需求呢?先说需求1,假设“商品1”的bit为1,“商品2”的bit为2,那么查询包含“商品1”和“商品2”的订单的sql如下:

SELECT * from `order` where bit & 3 = 3

这里的3为“商品1”和“商品2”bit之和。这个要怎么理解呢,从goods的bit说起,商品的bit用二进制表示如下图:

通过上图,可以想象order表中的bit的值的二进制形式,如果订单包含商品1、2,则二进制位11;如果包含商品1、3,则二进制位101;如果包含商品1、2、3,则二进制位111,。。。商品的各种组合,其bit值相加不会产生进位,因为每个商品bit值的“1”位对应其他商品bit值都为“0”位,所以商品的各种组合对应的订单的bit值唯一,这也是为何商品的bit要求取值为2的n次方(n>=0)。

因此,上述sql中,如果order的bit 和 所要查询商品的bit之和按位与运算,如果结果为查询商品的bit之和,那么说明order的bit相应位为1,订单中有该商品。

需求2,包含“商品1”或“商品2”怎么表示?sql如下:

SELECT * from `order` where bit & 3 > 0 

意思是说,订单bit的相应位至少有一个商品的bit即可。

需求3:仅包含“商品1”和“商品2”怎么表示?sql如下:

SELECT * from `order` where bit = 3

需求4:仅包含“商品1”或“商品2”怎么表示?这种情况下要求订单中只有一个商品,为“商品1”或“商品2”。sql如下:

SELECT * from `order` where bit & (bit - 1) =0 and bit & 3 != 0

这里 bit & (bit -1) = 0 保证订单中只有一个商品,bit & 3  != 0 保证订单中的商品为“商品1”或者“商品2”。

这种方法效率很高,满足需求的同时也简化了sql。

 

记一次在deployment中添加灰度暂停功能(代码片段)

本文主要聊聊如何在k8sdeployment中添加灰度暂停功能。因为是基于deployment原本支持的RollingUpdate更新方式和pause进行设计,所以文章中大篇幅会对deployment源码阅读分析。k8sv1.16deployment目前处理逻辑首先deployment是k8s暴露给用户的... 查看详情

如何使用正则表达式查询 pymongo 以获取仅包含数字的值

...时间】:2021-08-1822:39:28【问题描述】:我正在使用mongodb数据库,其中我在某些文档中具有特定值包含字段parentDocId,而其他文档则不包含。除了ParentDocId有多种形状外,我还想获得500个文档,其中该字段不仅存在 查看详情

无法在 Nhb 4 中构建 2 个表 JOIN,仅包含某些字段可供选择

...-2920:24:26【问题描述】:我尝试使用Nhibernatev.4运行这样的查询:Selecto.Number,c.AddressFromOrderojoinClientcono.ClientId=c.IdWherec 查看详情

mysql查询一次在单个查询中在多个表中插入记录

...发布时间】:2015-05-3000:27:46【问题描述】:如何使用MySql数据库在不同的表中插入数据?就像我想插入cid=1,cname=sahil,bid=12345,amount=12000顾客cid,cname银行出价,cid,金额【问题讨论】:【 查看详情

记一次在deployment中添加灰度暂停功能(代码片段)

本文主要聊聊如何在k8sdeployment中添加灰度暂停功能。因为是基于deployment原本支持的RollingUpdate更新方式和pause进行设计,所以文章中大篇幅会对deployment源码阅读分析。k8sv1.16deployment目前处理逻辑首先deployment是k8s暴露给用户的... 查看详情

SQL-查找仅包含某些项目的订单

】SQL-查找仅包含某些项目的订单【英文标题】:SQL-FindingOrderswithonlycertainitems【发布时间】:2017-04-2815:07:15【问题描述】:我有一个订单表,每个订单中的每个项目都有一行。订单号在每一行中(当然不是唯一的)。商品编号是... 查看详情

记一次在github上提交issue的经历

参考技术A在一次使用vscode时,发现一个bug:使用中文路径splitterminal(终端分屏)时会报异常并且失败,英文路径是ok的。然后我在不同场景下试图复现这个bug:目前来看,这个应该是vscode的一个bug,实锤了。因此我决定将这个bu... 查看详情

无数据返回 如果选择查询的搜索条件仅包含数字并且查询表的 nVarChar 列

...题描述】:我有一个windows应用程序来同步存储在SQLServerCE数据库中的windowsmobi 查看详情

记一次在broadcastreceiver或service里弹窗的“完美”实践

  事情是这样的,目前在做一个医疗项目,需要定时在某个时间段比如午休时间和晚上让我们的App休眠,那么这个时候在休眠时间段如果用户按了电源键点亮屏幕了,我们就需要弹出一个全屏的窗口去做一个人性化的提示,“... 查看详情

记一次es查询结果集失败

参考技术A使用es存储查询时有分页功能,数据量较少时并没有发现该问题。但是当数据量比较大时,要查询第101页的数据,每页数据量为100,100页数据相当于要查第10000条以后的数据,这时发现es查询报错:从字面理解,es的默认... 查看详情

使用 Postgres 一次在 3 个表中插入数据

】使用Postgres一次在3个表中插入数据【英文标题】:Insertdatain3tablesatatimeusingPostgres【发布时间】:2013-12-3121:53:23【问题描述】:我想通过一个查询将数据插入3个表中。我的表格如下所示:CREATETABLEsample(idbigserialPRIMARYKEY,lastnamevarch... 查看详情

通过查看 Oracle 中数值的某些字符来选择语句

...6:35【问题描述】:第一次发帖。请放轻松。:)在我的Oracle数据库中,我有一个名为fun_table的表。它包含一个名为FIRSTCOLUMN的列。此列仅包含六位纯数字值。以下是当前fun_table中 查看详情

记一次mysql查询慢的优化历程

 有一个项目,代称​​cc​​,用了我们公司的产品,单表数据量在200万左右。在做业务操作的时候,点击一下按钮,需要等待2~3分钟。及其难以忍受,特此让我们修改。PS:涉及的一些​​sql​​命令,简化了很多,不能... 查看详情

记一次在广播(broadcastreceiver)或服务(service)里弹窗的“完美”实践

事情是这样的,目前在做一个医疗项目,需要定时在某个时间段比如午休时间和晚上让我们的App休眠,那么这个时候在休眠时间段如果用户按了电源键点亮屏幕了,我们就需要弹出一个全屏的窗口去做一个人性化的提示,“当前... 查看详情

在 Woocommerce wc_get_loop_prop 产品循环中仅包含某些类别

】在Woocommercewc_get_loop_prop产品循环中仅包含某些类别【英文标题】:IncludeonlycertaincategoriesintheWoocommercewc_get_loop_propproductloop【发布时间】:2021-12-2404:40:36【问题描述】:我正在使用archive-product.php作为基础构建自定义类别归档模板... 查看详情

如何一次在多个表中添加行?

...问题描述】:我在netbeans中制作了一个java程序,它在我的数据库中执行不同的查询。我在表中添加数据时遇到问题。它在前两个表中插入数据,但不在第三个表中。代码如下:Stringsql="INSERTINTO`adresa`(`ID`,`TARA`,`JUDET`,` 查看详情

一次在多个表中插入/更新数据的最佳实践

...nce【发布时间】:2011-06-2507:34:30【问题描述】:所以我的数据库中有两个表,联系人和地址:联系人:ContactID|地址ID|名字|姓氏地址:地址ID|地址1|地址2|城市|状态|邮政编码我有一个页面,您可以在其中添加联系人。它包含联系... 查看详情

过滤现有项目组,使其仅包含符合某些条件的文件

】过滤现有项目组,使其仅包含符合某些条件的文件【英文标题】:Filteranexistingitemgroupsoitonlyincludesfilesthatmatchsomecondition【发布时间】:2012-08-2617:10:09【问题描述】:如何根据特定条件(例如文件扩展名或项目的元数据)过滤现... 查看详情