OceanBase中NOT EXISTS是否需要被改写

news2024/9/21 22:35:10
作者简介

张瑞远,曾经从事银行、证券数仓设计、开发、优化类工作,现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作。 持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得的专业技能与认证包括 OceanBase OBCE、Oracle OCP 11g、OracleOCM 11g 、MySQL OCP 5.7 、腾讯云TBase、腾讯云TDSQ

背景:

前段时间写了一篇《 关于OB中左外连接和反连接的探究 》的文章。OceanBase官网知识库后来也更新了这部分的内容,链接如下:

https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000475695 

1704244993

这意味着在OceanBase中就不建议使用not exists,或者说只能通过改写来优化它吗?

实际上,并非如此。包括我在文章中的介绍,也仅仅是表明在特定情况下,batch rescan的优化性能可能会优于无法使用该特性的anti join,但这并不意味着not exists的优化途径仅此一种。

为了更直观地说明这一点,我仍倾向于通过实验来验证。

接下来,我将以我之前文章中的sql为例,进行一次优化实验。

实验过程:

sql原文及效率和执行计划如下,因为CTM_GM是视图,所以执行计划中看到的表名是SD_CTM_GM。

#####sql文本
 select  count(1)
    from tttt.mmmmm_sssssale t
   where t.sssss  not in ('e111', 'ddddda')
     and t.stats  = '1'
     and t.parean  is null
     and t.city  = 2208
     AND (t.cusystatus  = 'FFFFGGGGG')
     AND (T.ORGGGGGGNEL  is null or T.ORGGGGGGNEL  != 'infonow')
      AND NOT EXISTS (SELECT  1
            FROM tttt.TTTT_OWN_C  TOW
           WHERE T.PID  = TOW.OID
             AND TOW.CT_ID  = T.city 
             AND TOW.OODDD_sTS   IN
                 (SELECT DDDC
                    FROM tttt.CTM_GM
                   WHERE GPID  = 'OtherThing '
       AND stats  = '1'))  and to_char(createdate,'yyyymmdd') between 20150101 and 202301211;	
###执行时间
 +----------+
| COUNT(1) |
+----------+
|    29493 |
+----------+
1 row in set (43.87 sec)
####执行计划
| ===========================================================================================
|ID|OPERATOR                           |NAME                             |EST. ROWS|COST  |
-------------------------------------------------------------------------------------------
|0 |SCALAR GROUP BY                    |                                 |1        |161064|
|1 | NESTED-LOOP ANTI JOIN             |                                 |808      |161033|
|2 |  TABLE SCAN                       |T                                |1278     |40623 |
|3 |  PX COORDINATOR                   |                                 |1        |94    |
|4 |   EXCHANGE OUT DISTR              |:EX10001                         |1        |94    |
|5 |    SUBPLAN SCAN                   |VIEW2                            |1        |94    |
|6 |     NESTED-LOOP JOIN              |                                 |1        |94    |
|7 |      EXCHANGE IN DISTR            |                                 |1        |92    |
|8 |       EXCHANGE OUT DISTR (BC2HOST)|:EX10000                         |1        |92    |
|9 |        TABLE SCAN                 |TOW(IDX_TTTT_OWN_C _ORDERID)     |1        |92    |
|10|      TABLE SCAN                   |SD_CTM_GM(PK_SD_CTM_GM)          |1        |32    | 
===========================================================================================

Outputs & filters: 
-------------------------------------
  0 - output([T_FUN_COUNT(*)]), filter(nil), 
      group(nil), agg_func([T_FUN_COUNT(*)])
  1 - output([1]), filter(nil), 
      conds(nil), nl_params_([T.PID ])
  2 - output([T.PID ]), filter([T.city  = 2208], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) >= 20150101], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) <= 202301211], [(T_OP_IS, T.ORGGGGGGNEL , NULL, 0) OR T.ORGGGGGGNEL  != ?], [(T_OP_NOT_IN, T.sssss , (?, ?))], [(T_OP_IS, T.parean , NULL, 0)], [T.stats  = ?], [T.cusystatus  = ?]), 
      access([T.sssss ], [T.stats ], [T.parean ], [T.city ], [T.cusystatus ], [T.ORGGGGGGNEL ], [T.PID ], [T.CREATEDATE]), partitions(p0)
  3 - output([1]), filter(nil)
  4 - output([1]), filter(nil), is_single, dop=1
  5 - output([1]), filter(nil), 
      access([VIEW2.TOW.OID])
  6 - output([TOW.OID]), filter(nil), 
      conds(nil), nl_params_([TOW.OODDD_sTS  ])
  7 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil)
  8 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil), is_single, dop=1
  9 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter([TOW.CT_ID  = 2208]), 
      access([TOW.OID], [TOW.CT_ID ], [TOW.OODDD_sTS  ]), partitions(p0)
  10 - output([1]), filter([SD_CTM_GM.stats  = ?]), 
      access([SD_CTM_GM.stats ]), partitions(p0)

可以看到该sql走了NESTED-LOOP ANTI JOIN,执行时间是43s,执行时间比较长。

按照正常的优化思路来分析下,咱们先看下实际的数据量。

obclient>  select  count(1)
    ->     from tttt.mmmmm_sssssale t
    ->    where t.sssss  not in ('e111', 'ddddda')
    ->      and t.stats  = '1'
    ->      and t.parean  is null
    ->      and t.city  = 2208
    ->      AND (t.cusystatus  = 'FFFFGGGGG')
    ->      AND (T.ORGGGGGGNEL  is null or T.ORGGGGGGNEL  != 'infonow') and to_char(createdate,'yyyymmdd') between 20150101 and 202301211; 
+----------+
| COUNT(1) |
+----------+
|    29493 |
+----------+
1 row in set (0.08 sec)

obclient> SELECT count(*)
    ->                     FROM tttt.CTM_GM
    ->                    WHERE GPID  = 'OtherThing '
    ->        AND stats  = '1' ;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

obclient> SELECT count(*) from  tttt.TTTT_OWN_C  TOW;
+----------+
| COUNT(*) |
+----------+
|  5087140 |
+----------+
1 row in set (1.99 sec)

可以看到CTM_GM的结果是0行,扫描速度很快,那么not exists的子查询结果也是0行,但是TOW表有500W数据,原本的执行计划是TOW通过nl连接CTM_GM,要取TOW的结果集去匹配CTM_GM,理论上我们修改这两个表关联顺序,就可以只取CTM_GM的0行消除掉TOW表这么大数据量的消耗代价。


######sql文本
select /*+use_nl(@"SEL$1" ("VIEW2"@"SEL$1" ))*/ count(1)
    from tttt.mmmmm_sssssale t
   where t.sssss  not in ('e111', 'ddddda')
     and t.stats  = '1'
     and t.parean  is null
     and t.city  = 2208
     AND (t.cusystatus  = 'FFFFGGGGG')
     AND (T.ORGGGGGGNEL  is null or T.ORGGGGGGNEL  != 'infonow')
      AND NOT EXISTS (SELECT  /*+leading(CTM_GM) use_nl(CTM_GM,TOW) */ 1
            FROM tttt.TTTT_OWN_C  TOW
           WHERE T.PID  = TOW.OID
             AND TOW.CT_ID  = T.city 
             AND TOW.OODDD_sTS   IN
                 (SELECT DDDC
                    FROM tttt.CTM_GM
                   WHERE GPID  = 'OtherThing '
       AND stats  = '1'))  and to_char(createdate,'yyyymmdd') between 20150101 and 202301211;		

#######执行效率
+----------+
| COUNT(1) |
+----------+
|    29493 |
+----------+
1 row in set (0.21 sec)	  


####执行计划

| =========================================================================================
|ID|OPERATOR                |NAME                                      |EST. ROWS|COST  |
-----------------------------------------------------------------------------------------
|0 |SCALAR GROUP BY         |                                          |1        |275986|
|1 | NESTED-LOOP ANTI JOIN  |                                          |808      |275955|
|2 |  PX COORDINATOR        |                                          |1278     |41514 |
|3 |   EXCHANGE OUT DISTR   |:EX10000                                  |1278     |40623 |
|4 |    TABLE SCAN          |T                                         |1278     |40623 |
|5 |  SUBPLAN SCAN          |VIEW2                                     |1        |183   |
|6 |   NESTED-LOOP JOIN     |                                          |1        |183   |
|7 |    TABLE SCAN          |SD_CTM_GM(INX_SD_CTM_GM_GPID )|1        |92    |
|8 |    MATERIAL            |                                          |1        |92    |
|9 |     PX COORDINATOR     |                                          |1        |92    |
|10|      EXCHANGE OUT DISTR|:EX20000                                  |1        |92    |
|11|       TABLE SCAN       |TOW(IDX_TTTT_OWN_C _ORDERID)            |1        |92    |
=========================================================================================

Outputs & filters: 
-------------------------------------
  0 - output([T_FUN_COUNT(*)]), filter(nil), 
      group(nil), agg_func([T_FUN_COUNT(*)])
  1 - output([1]), filter(nil), 
      conds(nil), nl_params_([T.PID ])
  2 - output([T.PID ]), filter(nil)
  3 - output([T.PID ]), filter(nil), is_single, dop=1
  4 - output([T.PID ]), filter([T.city  = 2208], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) >= 20150101], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) <= 202301211], [(T_OP_IS, T.ORGGGGGGNEL , NULL, 0) OR T.ORGGGGGGNEL  != ?], [(T_OP_NOT_IN, T.sssss , (?, ?))], [(T_OP_IS, T.parean , NULL, 0)], [T.stats  = ?], [T.cusystatus  = ?]), 
      access([T.sssss ], [T.stats ], [T.parean ], [T.city ], [T.cusystatus ], [T.ORGGGGGGNEL ], [T.PID ], [T.CREATEDATE]), partitions(p0)
  5 - output([1]), filter(nil), 
      access([VIEW2.TOW.OID])
  6 - output([TOW.OID]), filter(nil), 
      conds([TOW.OODDD_sTS   = SD_CTM_GM.DDDC]), nl_params_(nil)
  7 - output([SD_CTM_GM.DDDC]), filter([SD_CTM_GM.stats  = ?]), 
      access([SD_CTM_GM.stats ], [SD_CTM_GM.DDDC]), partitions(p0)
  8 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil)
  9 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil)
  10 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil), is_single, dop=1
  11 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter([TOW.CT_ID  = 2208]), 
      access([TOW.OID], [TOW.CT_ID ], [TOW.OODDD_sTS  ]), partitions(p0) 

通过添加hint的方式我们可以看到我只调整了TOW和CTM_GM的连接顺序,效率从43s优化到了0.21s,而我上一篇文章中改写之后的效率是1s,所以该sql不需要改写就可以优化掉。

那这个效率还可以优化吗,看起来还有优化空间,既然not exists的结果集是0,那我用上面同样的方式,干预下view结果集和T表的顺序,能不能得到更好的结果那?

####sql文本
select /*+leading(("VIEW2"@"SEL$1" ))*/ count(1)
    from tttt.mmmmm_sssssale t
   where t.sssss  not in ('e111', 'ddddda')
     and t.stats  = '1'
     and t.parean  is null
     and t.city  = 2208
     AND (t.cusystatus  = 'FFFFGGGGG')
     AND (T.ORGGGGGGNEL  is null or T.ORGGGGGGNEL  != 'infonow')
      AND NOT EXISTS (SELECT  /*+leading(CTM_GM) use_nl(CTM_GM,TOW) */ 1
            FROM tttt.TTTT_OWN_C  TOW
           WHERE T.PID  = TOW.OID
             AND TOW.CT_ID  = T.city 
             AND TOW.OODDD_sTS   IN
                 (SELECT DDDC
                    FROM tttt.CTM_GM
                   WHERE GPID  = 'OtherThing '
       AND stats  = '1'))  and to_char(createdate,'yyyymmdd') between 20150101 and 202301211;		  
####执行效率	   
+----------+
| COUNT(1) |
+----------+
|    29493 |
+----------+
1 row in set (0.08 sec)
#########执行计划	   
| ==========================================================================================
|ID|OPERATOR                |NAME                                      |EST. ROWS|COST   |
------------------------------------------------------------------------------------------
|0 |SCALAR GROUP BY         |                                          |1        |2231317|
|1 | HASH RIGHT ANTI JOIN   |                                          |808      |2231286|
|2 |  SUBPLAN SCAN          |VIEW2                                     |470      |2188504|
|3 |   NESTED-LOOP JOIN     |                                          |470      |2188497|
|4 |    TABLE SCAN          |SD_CTM_GM(INX_SD_CTM_GM_GPID )|1        |92     |
|5 |    MATERIAL            |                                          |299244   |2182932|
|6 |     PX COORDINATOR     |                                          |299244   |2169701|
|7 |      EXCHANGE OUT DISTR|:EX10000                                  |299244   |2075029|
|8 |       TABLE SCAN       |TOW                                       |299244   |2075029|
|9 |  PX COORDINATOR        |                                          |1278     |41514  |
|10|   EXCHANGE OUT DISTR   |:EX20000                                  |1278     |40623  |
|11|    TABLE SCAN          |T                                         |1278     |40623  |
==========================================================================================

Outputs & filters: 
-------------------------------------
  0 - output([T_FUN_COUNT(*)]), filter(nil), 
      group(nil), agg_func([T_FUN_COUNT(*)])
  1 - output([1]), filter(nil), 
      equal_conds([T.PID  = VIEW2.TOW.OID]), other_conds(nil)
  2 - output([VIEW2.TOW.OID]), filter(nil), 
      access([VIEW2.TOW.OID])
  3 - output([TOW.OID]), filter(nil), 
      conds([TOW.OODDD_sTS   = SD_CTM_GM.DDDC]), nl_params_(nil)
  4 - output([SD_CTM_GM.DDDC]), filter([SD_CTM_GM.stats  = ?]), 
      access([SD_CTM_GM.stats ], [SD_CTM_GM.DDDC]), partitions(p0)
  5 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil)
  6 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil)
  7 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter(nil), is_single, dop=1
  8 - output([TOW.OID], [TOW.OODDD_sTS  ]), filter([TOW.CT_ID  = 2208]), 
      access([TOW.OID], [TOW.CT_ID ], [TOW.OODDD_sTS  ]), partitions(p0)
  9 - output([T.PID ]), filter(nil)
  10 - output([T.PID ]), filter(nil), is_single, dop=1
  11 - output([T.PID ]), filter([T.city  = 2208], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) >= 20150101], [cast(cast(TO_CHAR(T.CREATEDATE, ?), VARCHAR2(256 BYTE)), NUMBER(-1, -85)) <= 202301211], [(T_OP_IS, T.ORGGGGGGNEL , NULL, 0) OR T.ORGGGGGGNEL  != ?], [(T_OP_NOT_IN, T.sssss , (?, ?))], [(T_OP_IS, T.parean , NULL, 0)], [T.stats  = ?], [T.cusystatus  = ?]), 
      access([T.sssss ], [T.stats ], [T.parean ], [T.city ], [T.cusystatus ], [T.ORGGGGGGNEL ], [T.PID ], [T.CREATEDATE]), partitions(p0)	   

可以看到现在效率0.08s比一开始的43s提升了500多倍比单纯的改写效率提升了12倍多,所以NOT EXISTS不一定需要改写,可能只是计划走错了,这种情况下我们绑定下计划就好了,很多时候应用改代码的代价也很大。

结论:

很多sql不是一定要去改写才能解决的,虽然改写有可能可以使用到一些优化算子,但是可能问题的根因不在这里,我们一定要提高自己分析问题的能力,准确判断分析问题,这样在工作中可以得心应手,也可以避免很多不必要的冗余的工作。

行之所向,莫问远方。

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

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

相关文章

直播预告丨困气排气解决新方案--毅速金属3D打印随形透气钢

您是否也遇到过这些问题 模具困气造成产品出现注塑瑕疵&#xff0c;但复杂的产品形状导致无法开排气槽 常规透气钢需要拆镶件导致工件强度下降 某些工件部分不接受分模线区域无法拆镶件无法使用常规透气钢 面对越来越复杂的产品和结构&#xff0c;越来越多需要透气、保压、…

全网最新网络安全自学路线,最详细没有之一!!!

在各大平台搜的网安学习路线都太粗略了。。。。看不下去了&#xff01; 我把自己整理的系统学习路线&#xff0c;拿出来跟大家分享了&#xff01; 建议的学习顺序&#xff1a; 一、网络安全学习普法&#xff08;心里有个数&#xff0c;要进去坐几年&#xff01;&#xff09; 1…

Spring 面试——restcontroller/requestmapping

RestController Controller ResponseBody Controller&#xff1a;包含Component&#xff0c;把当前类声明成为一个 bean ResponseBody&#xff1a;表示方法返回的结果直接作为 HTTP 响应的内容&#xff0c;不是返回视图 3.RequestMapping注解的基本用法_哔哩哔哩_bilibili

Linux文件系列:磁盘,文件系统,软硬链接

Linux文件系列:磁盘,文件系统,软硬链接 一.磁盘相关知识1.磁盘机械构成2.磁盘物理存储3.磁盘逻辑存储1.LBA地址2.磁盘的分区和分组 二.文件系统和inode1.inode结构体2.文件系统1.Super Block(超级块)2.Group Descriptor Table(块组描述表GDT)3.inode Table4.Data Blocks5.Block…

如何本地部署Imagewheel并实现无公网IP远程连接打造个人云图床

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

域名SSL证书怎么获取?

获取域名证书的步骤如下&#xff1a; 选择认证机构&#xff1a;域名证书必须从受信任的认证机构(CA)中申请&#xff0c;如JoySSL、GeoTrust、、Thawte等。收集信息&#xff1a;在申请域名证书之前&#xff0c;需要准备一些证明信息&#xff0c;如域名认证等。创建CSR&#xff…

【ARXIV2402】MambaIR

这个工作首次将 Mamba 引入到图像修复任务&#xff0c;关于为什么 Mamba 可以用于图像修复&#xff0c;作者有非常详细的解释&#xff1a;一路向北&#xff1a;性能超越SwinIR&#xff01;MambaIR: 基于Mamba的图像复原基准模型 作者认为Mamba可以理解为RNN和CNN的结合&#xf…

实战 | 微调训练TrOCR识别弯曲文本

导 读 本文主要介绍如何通过微调训练TrOCR实现弯曲文本识别。 背景介绍 TrOCR&#xff08;基于 Transformer 的光学字符识别&#xff09;模型是性能最佳的 OCR 模型之一。在我们之前的文章中&#xff0c;我们分析了它们在单行打印和手写文本上的表现。 TrOCR—基于Transforme…

基于ARM 的Linux系统的交叉编译

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;在 MacOS 中安装 下一篇&#xff1a;MultiArch与Ubuntu/Debian 的交叉编译 警告 本教程可以包含过时的信息。 此步骤已在 Ubuntu Linux 12.04 上进行了测试&#xff0c;但应…

跳蚱蜢(蓝桥杯)

文章目录 跳蚱蜢题目描述答案&#xff1a;20bfs 跳蚱蜢 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 如下图所示&#xff1a; 有 9 只盘子&#xff0c;排成 1 个圆圈。 其中 8 只盘子内装着 8 只蚱蜢&#xff…

JVM 基础知识

JAVA code加载执行顺序&#xff1a; *.java --->*.class->类装载子系统、字节码执行引擎 JVM虚拟机组成&#xff1a; 堆、方法区&#xff08;元空间&#xff09;、栈&#xff08;线程&#xff09;、本地方法栈、程序计数器 栈帧组成&#xff1a; 栈帧&#xff1a;局部…

管理类联考–复试–管理类知识–计划

决策是管理的核心&#xff0c;决策是计划的前提&#xff0c;计划是管理的首要职能&#xff0c;战略是一种计划。 #mermaid-svg-kwa82umCx6PXrp8x {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-kwa82umCx6PXrp8x .e…

Linux操作系统及进程(三)进程优先级及特性

目录 一、优先级概念 二、查看系统进程 三、进程切换 一、优先级概念 1.cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 2.优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用&#xff0c;可以改善系统性能。…

在面对API的安全风险,WAAP全站防护能做到哪些?

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间和企业内部系统交互的核心组件。在应用程序开发过程中&#xff0c;API能够在不引起用户注意的情况下&#xff0c;无缝、流畅地完成各种任务。例如从一个应用程序中提取所需数据并传递给…

Web漏洞-SQL注入之二次、加密、DNS加密注入

实例1&#xff1a;sqli-labs21 输入admin&#xff0c;admin 测试&#xff1a; 可以看到注入点在cookie处&#xff0c;发送到decoder&#xff08;解密&#xff09; 所以如果要注入&#xff0c;需要将注入语句加密 Eg&#xff1a;admin’ and 11加密后&#xff1a;YWRtaW4ZIGFu…

95% 的公司面临 API 安全问题

API 对企业安全发挥着关键作用&#xff0c;但绝大多数企业都为此遭受日益严重的安全风险。据安全公司 Fastly最近做的一项调查显示&#xff0c;84% 的受访企业缺乏足够的API安全措施&#xff0c;95%的企业在过去1年中遇到过 API 安全问题。 此外&#xff0c;79%的受访企业出于A…

Java基于微信小程序的校园请假系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#…

【Java程序设计】【C00366】基于(JavaWeb)Springboot的纹理生产图片系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

PyQt:实现菜单栏的点击拖动效果

一、整体步骤 1.设计UI文件 2.调用显示 3.效果展示 二、设计UI文件 1.添加 Scroll Area控件&#xff0c;作为菜单栏的布置区域 2.设置 Scroll Area控件的属性 3.Scroll Area控件内放置 按钮控件 组成菜单栏 此处&#xff0c;放置了需要了6个按钮&#xff0c;并设置按钮的固…

代码随想录算法训练营第二十九天|Leetcode332 重新安排行程、Leetcode51 N皇后、Leetcode37 解数独

代码随想录算法训练营第二十九天|Leetcode332 重新安排行程、Leetcode51 N皇后、Leetcode37 解数独 ● Leetcode332 重新安排行程● 本题特点● 解题思路● 代码实现 ● Leetcode51 N皇后● 本题特点● 解题思路● 代码实现 ● Leetcode37 解数独● 本题特点● 解题思路● 代码…