news 2026/10/5 3:28:36

Oracle联表查询与集合操作:JOIN、UNION、MINUS的实战避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Oracle联表查询与集合操作:JOIN、UNION、MINUS的实战避坑指南

1. 联表查询与集合操作:一对常被混淆的兄弟

做Oracle开发的人,基本都会遇到一个场景:报表数据对不上。明明逻辑看着没问题,Left Join也加了,条件也写了,结果不是多出来几行,就是死活少几条数据。排查到最后,问题往往出在一个地方——你把JOIN和集合运算搞混了,或者用错了场景。

这期内容聚焦“联表查询集合”,说白了就是两件事:联表查询(JOIN)和集合操作(UNION / INTERSECT / MINUS)。很多文章把它们分开讲,但在实际工作中,这两个东西经常是配合着用的。你处理EBS工单数据、做物料核算、核对接口数据差异时,单靠JOIN解决不了的问题,往往是集合操作一句话的事;反过来,集合操作搞不定的字段对齐,又得回到JOIN。

先说清楚一个核心概念:JOIN和集合操作虽然都用于“合并数据”,但维度完全不同。

  • JOIN是横向合并——把两个表的列拼在一起,行数通常不变,列数变多了。好比两列队伍按编号对齐,牵手组成新队伍。
  • 集合操作是纵向拼接——把两个查询的行堆在一起,列结构相同,行数变多了。好比两摞砖头叠成一摞。

知道这个区别,很多问题就迎刃而解。比如你发现查询结果多行了,第一反应不应该是查JOIN条件,而是先想:是不是我本来想要纵向合并,却写成了JOIN,导致数据被重复放大了?这类问题我在实际项目里见过太多次,尤其是新手在写多表查询时,顺手就把UNION写成JOIN,结果同一个业务单号出现在多行里,汇总金额直接翻倍。

所以这一期我把它们放在一起拆解,目标就一个:让你在面对“多表数据怎么拼”这个问题时,能快速判断该用JOIN还是该用集合,并且两者组合使用的时候,知道怎么避坑。

这套内容适合三类人:刚入门Oracle、只会单表查询的初学者;写报表SQL但经常对不上数、需要系统性梳理JOIN和集合用法的开发;以及需要在生产环境做数据核对、接口对账的运维或数据人员。基础概念我会讲透,但更重要的是那些踩过的坑和排查思路。

2. JOIN核心类型拆解:从内连接到全连接的适用场景

2.1 五类JOIN,一张表说清

Oracle的JOIN类型很多人在面试和工作中都背过,但用起来还是容易混。我不按教科书的讲法来,直接按“保留谁的数据”这个维度去理解,你在写SQL时就能少想三秒。

JOIN类型保留数据范围典型场景
INNER JOIN两表都匹配上的行取有效匹配数据,比如有订单又有客户资料
LEFT JOIN左表全部,右表无匹配则补NULL主表数据不能丢,比如查所有工单及其物料信息
RIGHT JOIN右表全部,左表无匹配则补NULL不常用,能用LEFT改写的就别用RIGHT
FULL JOIN两表全部,无匹配则补NULL全量对账、查两表差异
CROSS JOIN笛卡尔积,两表行数相乘极少直接用,一般是漏了关联条件出现的“事故”

说个直观例子。假设你有两张表:订单表orders(订单号、客户ID、金额)和客户表customers(客户ID、客户名)。需求是查所有订单及对应的客户名,即使客户信息缺失也要把订单显示出来。这时候必须用LEFT JOIN,因为orders是主表,数据不能因为客户缺失就消失。如果你用了INNER JOIN,客户ID对不上的订单就会被静默过滤掉——这是数据核对时最坑的地方,因为SQL不报错,结果就是少数据。

左连接的SQL长这样:

SELECT o.order_id, o.customer_id, c.customer_name, o.amount FROM orders o LEFT JOIN customers c ON o.customer_id = c.customer_id;

这里有个关键点:关联条件用ON,过滤条件用WHERE。如果你在LEFT JOIN的WHERE里写了c.customer_id = 'C001',那这条语句的效果跟INNER JOIN没区别——因为WHERE是在JOIN完成之后才过滤的,一旦过滤掉右表为NULL的行,左表保留的意义就没了。这个细节是很多报表数据“莫名变少”的根源之一。

2.2 自连接:给表“自己和自己牵手”

自连接不是额外的JOIN类型,而是同一张表自己JOIN自己。很多实际需求靠它实现,典型场景是层级关系查询——比如员工表里有一个manager_id指向员工的employee_id,想查出每个员工及其上级姓名,就得自己跟自己关联:

SELECT e.employee_name AS 员工姓名, m.employee_name AS 上级姓名 FROM employees e LEFT JOIN employees m ON e.manager_id = m.employee_id;

这种写法看着简单,但新手容易栽在别名上。两个别名e和m必须写清楚,不然Oracle都不知道你在连谁。还有一个坑:如果manager_id是空的(比如老板没有上级),这里用LEFT JOIN才能把员工显示出来,INNER JOIN会把老板下面没有上级信息的行丢掉,也可能导致员工列表少人。

自连接还有一种用法是行转列,比如每行只存了用户的起始日期和结束日期,想找时间区间重叠的记录,本质上也是自己对自己做JOIN,条件是区间交叉:a.start_date <= b.end_date AND a.end_date >= b.start_date。这类SQL看起来复杂,思路其实就是“同一张表,虚拟成两批人去比较”。

2.3 ON条件与WHERE条件的边界感

这是联表查询里最容易被忽略的细节。ON负责控制“怎么连”,WHERE负责控制“连完之后留哪些”。两者写错了位置,结果天差地别。

我举个例子,你有员工表和部门表,想统计各部门人数:

-- 写法A:过滤条件放在WHERE SELECT d.dept_name, COUNT(e.emp_id) FROM dept d LEFT JOIN emp e ON e.dept_id = d.dept_id WHERE e.emp_id IS NOT NULL; -- 写法B:过滤条件放在ON SELECT d.dept_name, COUNT(e.emp_id) FROM dept d LEFT JOIN emp e ON e.dept_id = d.dept_id AND e.salary > 5000;

写法A里执行顺序是先连接、再过滤,跟INNER JOIN没本质区别;写法B是先对emp表做条件筛选,再与dept表的全部行连接——这样空部门也能保留下来,统计结果中部门不会消失。

所以如果你要的是“主表全保留 + 副表按条件关联”,那个条件一定要写在ON后面。如果你只是普通过滤,就写WHERE。这个边界感我在处理EBS工单数据时尤为注意——工单主表和物料分配行关联时,如果想把没有分配行的工单也列出来,关联条件必须放ON,而不是WHERE。

3. 集合操作详解:UNION、UNION ALL、INTERSECT、MINUS

3.1 UNION与UNION ALL:去掉重复的那几行,性能差多少?

UNION和UNION ALL这两个操作符,初看只差一个ALL,实际差异很大。

UNION会对两个结果集做去重,UNION ALL是直接堆砌,保留所有行,包括重复的。所以UNION ALL的执行效率通常比UNION高很多,因为UNION内部要做排序和去重(Oracle实现里通常涉及SORT UNIQUE操作)。数据量大时,这个差异是肉眼可见的慢。

-- 查两个部门的所有员工,去重合并 SELECT emp_name FROM dept_a_employees UNION SELECT emp_name FROM dept_b_employees; -- 不去重,保留所有记录 SELECT emp_name FROM dept_a_employees UNION ALL SELECT emp_name FROM dept_b_employees;

使用上有个不成文的实践:如果你确定两个结果集不会重复,或者业务上允许重复,一律用UNION ALL。比如按月查询历史分区数据做汇总,每个月的数据天然互斥,用UNION纯粹是浪费性能。但如果你是要合并两张可能含有相同记录的临时表,又想要干净的结果,UNION才派得上用场。

另外注意,UNION的列名以第一个SELECT的列名为准。第二个查询的列别名会被忽略,所以别指望在第二个查询里起别名来改变输出列名。

3.2 INTERSECT:两表交集有哪些?

INTERSECT取两个结果集的共同部分,类似数学里的交集。实际工作中我用它最多的场景就是对比两个表的相同数据——比如接口同步过来的员工表,和本地正式表,查两边都存在的员工ID:

SELECT employee_id FROM employee_staging INTERSECT SELECT employee_id FROM employee_official;

这个操作有个隐含行为:它也会做去重,结果集中相同行只出现一次。这个特性在某些场景下是优点(省得再去重),但如果你关注的是“各自出现了几次”,那就不能用INTERSECT,得退回JOIN加COUNT。

3.3 MINUS:差集的妙用

MINUS是Oracle特有的集合操作符,取第一个结果集中有、第二个结果集中没有的数据。翻译成业务语言就是“查差异”。

数据对账场景中,MINUS可以说是神兵利器。比如你要核对EBS系统中的物料清单,系统A有1000条记录,系统B有998条,怎么快速找出哪些物料在系统A有而系统B没有?

SELECT item_id, item_code, qty FROM system_a_items MINUS SELECT item_id, item_code, qty FROM system_b_items;

一条语句就能列出差异数据,不需要写复杂的NOT EXISTS嵌套,也不需要担心NULL匹配问题(MINUS处理NULL的方式和NOT IN不一样,后者遇到NULL会直接返回空结果,你查不出任何数据)。这一点很关键,我后面会在问题排查部分详谈。

3.4 集合操作的硬性规则,踩过坑的都懂

集合操作虽然写起来短,但约束条件很严格,违反一条就报ORA错误:

  • 两个查询的列数必须一致。否则报ORA-01789。
  • 对应列的数据类型必须兼容。不兼容时Oracle会做隐式转换,但转不了就会报ORA-01790。
  • 可以加ORDER BY,但只能放在整个集合语句的最末尾,不能放在某个子查询内部。它作用于最终合并后的结果。
  • 列名以第一个查询的列名为准。

还有一个细节很多人不知道:集合操作符的优先级。INTERSECT的优先级高于UNION和MINUS。所以如果你混用INTERSECT和UNION,Oracle会先执行INTERSECT。这时候务必加括号明确逻辑,不然结果很可能出乎意料:

SELECT emp_id FROM t1 UNION SELECT emp_id FROM t2 INTERSECT SELECT emp_id FROM t3;

上面这个语句,Oracle实际执行的是t2和t3先取交集,再和t1做并集。如果你本意是先UNION再INTERSECT,必须写成:

SELECT emp_id FROM t1 UNION (SELECT emp_id FROM t2 INTERSECT SELECT emp_id FROM t3);

这类优先级问题在复杂报表SQL里排查起来非常费劲,因为执行结果不报错,只是数不对。

4. 集合与联表组合实战:从EBS工单到对账场景

4.1 场景一:工单与物料分配行的数据核对

EBS里WIP模块的非标工单,常涉及工单表WIP_DISCRETE_JOBS和物料分配表WIP_OPERATION_INSTRUCTIONS或WIP_MATERIAL_TRANSACTIONS(具体表名按版本有差异,重点是逻辑)。需求通常是:找出哪些工单没有物料分配记录,或者哪些工单的分配记录在完工后还有余额。

第一步,用LEFT JOIN把工单主表和分配表关联,查有空分配的情况:

SELECT w.job_id, w.job_name, m.operation_seq_num, m.item_id, m.quantity FROM wip_discrete_jobs w LEFT JOIN wip_material_transactions m ON w.job_id = m.job_id AND m.transaction_type = 'ISSUE' AND m.quantity > 0;

这里把过滤条件全放ON里,是为了即使没有发料记录,工单本身也保留下来。如果你把transaction_type条件写进WHERE,那没有发料的工单就被过滤了——这恰恰是对账时最忌讳的“数据悄悄变少”。

第二步,如果想快速找出“系统中存在,但尚未完工的工单”中哪些缺少发料记录,MINUS更直接。用发料记录作为第二个集合,工单全集作为第一个集合:

SELECT job_id FROM wip_discrete_jobs WHERE status_type NOT IN ('CLOSED', 'COMPLETED') MINUS SELECT job_id FROM wip_material_transactions WHERE transaction_type = 'ISSUE';

一条语句,不用JOIN,不用子查询嵌套,直接列出需要关注的工单号。这就是集合操作相比JOIN解决差异类问题的天然优势——不关心为什么没匹配上,只关心谁不在另一个集合里。

4.2 场景二:用UNION ALL整合多来源数据

做报表时经常遇到“同一个业务数据散落在多张结构相同的表里”。比如订单数据分成订单表和退货表,想统计分析客户的总发生额,就需要把两边的记录合并起来再聚合:

SELECT customer_id, order_amount AS amount, 'ORDER' AS biz_type, order_date AS biz_date FROM orders UNION ALL SELECT customer_id, return_amount, 'RETURN', return_date FROM returns;

这段SQL加了一个字段biz_type用来区分数据来源,这是处理多源数据合并时的标准做法。好处是后续可以按来源分组分析,排查问题也方便。如果你不需要区分来源,甚至可以简化到只有业务字段。

这里有个实践经验:多源合并时,宁可让SQL多写几行,也要把来源标识加上。否则一旦数据对不上,你根本不知道问题出在哪个表,排查成本成倍增加。

4.3 场景三:INTERSECT与MINUS组合完成全量对账

对账场景里最经典的组合拳是:先INTERSECT找相同数据,再MINUS找差异数据。

比如财务要对两个系统的应收余额:

-- 完全相同的数据(数量和金额一致) SELECT customer_id, SUM(amount) AS total_amount FROM system_a_ar GROUP BY customer_id INTERSECT SELECT customer_id, SUM(amount) FROM system_b_ar GROUP BY customer_id; -- A系统有而B系统没有的数据 SELECT customer_id, SUM(amount) FROM system_a_ar GROUP BY customer_id MINUS SELECT customer_id, SUM(amount) FROM system_b_ar GROUP BY customer_id;

这个思路能快速定位“两边一致的部分”和“有差异的部分”,再做针对性排查。比一个个客户去对账效率高太多了。我实际处理过千万级流水对账,用MINUS定位差异,配合全表扫描,几分钟就能圈定问题范围。

4.4 JOIN加集合组合的注意事项

组合使用JOIN和集合操作时,有几个细节会直接影响正确性:

  • 先JOIN再做集合操作,确保JOIN结果是“干净的语义单元”。不要在JOIN之后还依赖集合操作的某种隐式行为,一切显式表达。
  • 集合操作前,各子查询的字段顺序和业务含义必须一致,别只盯着类型对、长度对,语义也要对。A查询的第二列是金额,B查询的第二列是数量——类型都是NUMBER,能执行,但结果毫无意义。
  • 大数据量下集合操作会消耗较多临时表空间。UNION的排序去重尤其吃资源。生产环境执行前,先评估数据量,必要时用UNION ALL加外层GROUP BY去重,反而比UNION快很多。

5. 常见问题与排查技巧实录

5.1 笛卡尔积事故:为什么我的结果行数疯涨?

联表查询最常见的灾难就是结果集行数暴涨。比如两张表各有1000行,你只写了FROM a, b而忘了关联条件,结果是100万行。这在生产报表里就是事故。

排查思路很简单:先看执行计划的Cartesian Merge或者Rows估算值,其次是看结果集的DISTINCT计数和总数是否一致。如果你预期1000行,实际出来98万行,99.9%的概率是JOIN条件写错或漏写。

还有一种隐蔽情况:关联字段存在重复值。就算你写了ON a.id = b.id,如果b表里id=100有3条记录,a表里id=100也有2条,那么JOIN结果里id=100会出现6行(2×3)。这本质上是笛卡尔积的局部放大。处理办法是先用GROUP BY在子查询里把重复行聚合掉,再做JOIN。

SELECT a.order_id, b.total_qty FROM orders a LEFT JOIN (SELECT order_id, SUM(qty) AS total_qty FROM order_lines GROUP BY order_id) b ON a.order_id = b.order_id;

这种做法在多表关联时是标准解法,先聚合再关联,能有效防止行数膨胀。

5.2 NULL值陷阱:IN与NOT IN的“隐身杀手”

联表查询里NULL值带来的坑,最典型的就是NOT IN遇到子查询结果含NULL时,整个查询返回空集。原因不复杂:NULL参与等值比较的结果是“未知”,NOT IN本质是“不等于集合里任何一个值”,一旦集合里有NULL,判断结果全是“未知”,最终一行都不返回。

实际案例:查哪些员工不在离职名单里:

-- 这段SQL如果离职名单里有NULL,结果为空 SELECT employee_name FROM employees WHERE employee_id NOT IN (SELECT employee_id FROM exited_employees);

解决方法是改用NOT EXISTS,或者在子查询里过滤掉NULL:

-- 推荐写法:NOT EXISTS 对NULL天然免疫 SELECT employee_name FROM employees e WHERE NOT EXISTS (SELECT 1 FROM exited_employees x WHERE x.employee_id = e.employee_id);

同理,LEFT JOIN时如果右表关联字段为NULL,你期望的结果可能是“不匹配”,但NULL和任何值做等值比较都不成立,所以你必须清楚ON条件的判断逻辑。联表查询中NULL的处理是新手最头疼的问题,我都建议统一用NVL函数显式兜底,或者用NOT EXISTS代替NOT IN。

5.3 执行计划看哪里?HASH JOIN与NESTED LOOP的差异

处理性能问题时,执行计划是必须看的东西。Oracle里两表关联最常见两种执行方式:

  • NESTED LOOP JOIN:适合小表驱动大表、关联字段有索引。外层表每取一行,内层表通过索引快速定位匹配行。数据量小的时候快。
  • HASH JOIN:适合大表等值关联。把一张表的数据加载到哈希分区里,再扫描另一张表探测匹配。数据量大、没有索引时,HASH JOIN通常比NESTED LOOP效率高。

看执行计划时,重点看两件事:哪个表是驱动表(一般显示在计划树外层),以及关联字段有没有走索引。如果驱动表选错了,比如用大表驱动小表,NESTED LOOP会疯狂放大IO。解决办法是用提示(hint)指定驱动顺序,或者干脆改写SQL,用子查询控制执行顺序。

SELECT /*+ leading(b) use_nl(a b) */ a.order_id, b.customer_name FROM small_table b JOIN big_table a ON a.customer_id = b.customer_id;

这类hint在生产中要谨慎使用,因为数据分布变化后,固定执行计划可能反而不优。更好的思路是确保关联字段上有索引,让优化器自己选对路径。

5.4 分页与联表查询的配合:ROWNUM与ORDER BY的先后问题

Oracle分页查询是一个经典话题。联表查询之后要分页,新手经常把ROWNUM和ORDER BY写反,导致分页结果乱序。正确写法是:先排序,再套ROWNUM,最后取分页区间。

SELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM ( SELECT o.order_id, c.customer_name, o.amount FROM orders o LEFT JOIN customers c ON o.customer_id = c.customer_id ORDER BY o.amount DESC ) t WHERE ROWNUM <= 100 ) WHERE rn >= 81;

这个三层嵌套的顺序不能乱:最内层排序并完成JOIN,中间层加ROWNUM,外层做区间过滤。如果你把ORDER BY放在最外层,中间层的ROWNUM分配顺序就不是按业务排序来的,分页结果会错。这个问题我在实际开发中见过不止一次,查问题的时候数据明明没问题,就是排序乱,根源都是内层忘了先排好序。

5.5 IN顺序查询:如何保持传入顺序返回结果

热搜词里有个“oracle执行按in顺序查询”,这是典型的业务需求:传入一组ID,希望返回结果保持传入顺序。但Oracle里IN子句不保证按传入顺序返回结果,默认按索引或物理存储顺序返回。

解决办法是使用DECODE或CASE映射一个排序值:

SELECT order_id, order_date, amount FROM orders WHERE order_id IN (102, 105, 101, 103) ORDER BY DECODE(order_id, 102, 1, 105, 2, 101, 3, 103, 4, 99);

这样就实现了按指定顺序输出。如果是动态拼接的ID列表,可以在Java或Python端生成对应的DECODE片段,或者使用高级一点的集合方法(Oracle 12.2以后可以用JSON_ARRAY + JSON_TABLE,但复杂场景下DECODE的办法更通用)。这里提一下方法,够应对大多数情况即可。

5.6 常见错误速查表

现象可能原因解决思路
ORA-00918:列定义有歧义关联字段未加表别名前缀所有重复列名加表别名
ORA-01789:查询块具有错误的列数集合操作两查询列数不一致检查两边SELECT的字段数
ORA-01790:必须对应相同数据类型集合操作列类型不匹配用TO_CHAR / TO_NUMBER显式转换
笛卡尔积,行数暴涨ON条件缺失或写错检查JOIN条件,确认关联字段无误
LEFT JOIN后数据变少过滤条件误放WHERE移到ON,或者改用RIGHT JOIN理解语义
NOT IN返回空结果子查询包含NULL值改NOT EXISTS或过滤NULL
分页顺序乱ROWNUM和ORDER BY层级错误按三层嵌套结构写
集合操作结果少数据使用了UNION而非UNION ALL,隐式去重明确业务上是否需要去重

ORA-01428(参数超出范围)这类错误虽然不算联表查询专属,但在查询带边界参数时也可能遇到,排查时先确认绑定的数值和数据类型是否与列定义匹配。

6. 个人经验补充:联表查询的索引设计与习惯养成

讲完排查,再分享两个我在实践中反复验证过的观点。

第一个观点是:联表查询的性能天花板,往往由索引设计决定,而不是SQL技巧。再精妙的SQL写法,碰上关联字段没索引,也是巧妇难为无米之炊。建索引时重点看JOIN的ON条件和WHERE过滤条件。两表关联,关联字段类型不一致时(比如一边是VARCHAR2存数字,一边是NUMBER),Oracle会做隐式转换,导致索引失效。解决方法是统一字段类型,或者在SQL里显示转换,别依赖隐式转换。这个细节排查起来极其隐蔽,因为SQL不报错,只是执行计划里明明有索引却没走,Full Table Scan。

第二个观点是:联表查询的SQL逻辑,要在写之前就把JOIN和集合的角色分配好。我的习惯是:先问自己“我要横向扩展字段,还是纵向扩展行”,一个字不同,SQL结构完全不同。再问“有没有哪一步可以用MINUS或INTERSECT直接做集合判断,避免写复杂子查询”。这两个问题想清楚,写出来的SQL基本不会跑偏。

还有一个经验:生产环境里,如果发现一条联表查询SQL跑了很久,优先检查是不是存在NESTED LOOP和全表扫描的搭配。大数据集下全表扫描一旦做成驱动表嵌套循环,IO次数会呈乘积式爆炸。这种情况与其调SQL,不如考虑加索引后重建统计信息,很多时候统计信息过期会导致优化器选择错误的执行计划。

写SQL这事,经验一定是从实际排错里积累的。这期内容没有堆砌复杂的高级语法,全部是联表查询和集合操作里最常见、最容易出错、也最实用的部分。你可以拿手头的报表SQL对照检查一下:LEFT JOIN的过滤条件写在哪个位置?UNION和UNION ALL有没有想清楚?NOT IN的子查询里会不会有NULL?改一遍,可能就修复了一批“历史遗留问题”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 3:26:58

插件系统加载失败排查指南:从plugin.json到激活机制

1. 从“plugins”这个标题说起&#xff1a;一个被低估的工程话题“plugins”这个词看起来平平无奇&#xff0c;但如果你最近在折腾 Cursor、Codex CLI、ZCode CLI 这类工具&#xff0c;或者被failed to load plugins web boot: 2 entries did not activate这类报错卡住过&#…

作者头像 李华
网站建设 2026/10/5 3:26:51

函数到底是什么?从基础概念到工程实战一次讲透

函数这个词&#xff0c;我在写代码、处理数据、做算法调优的这几年里几乎天天碰。它不是什么高深的概念&#xff0c;但你拆开任何一个软件项目、一条SQL查询、甚至一张Excel表格&#xff0c;底层全是函数在撑场面。最近逛技术社区&#xff0c;跟函数相关的帖子特别密集&#xf…

作者头像 李华
网站建设 2026/10/5 3:26:42

JQuick-Excel dateFormat 转换实战:日期值与 FORMAT 显示格式的边界

JQuick-Excel dateFormat 转换实战&#xff1a;日期值与 FORMAT 显示格式的边界 tags: #JQuickExcel #Java #Excel日期 #DSL 简介 日期字段是 Excel 导入导出中最容易产生误解的字段之一&#xff1a;用户看到的格式、单元格实际保存的值、下游接口要求的文本可能并不相同。JQui…

作者头像 李华
网站建设 2026/10/5 3:26:10

Sql Server随机查询一条记录:从NEWID到存储过程封装实践

开头最近在做一个题库系统的小功能&#xff0c;客户提了个很实在的需求&#xff1a;每次考试从题库里随机抽一道题&#xff0c;而且抽题逻辑要能复用——以后不仅抽题要用&#xff0c;抽奖品、抽幸运用户、做数据抽查可能都要用。说白了就是一个“Sql Server里随机查询一条表记…

作者头像 李华
网站建设 2026/10/5 3:25:31

Python独立双样本t检验与z检验:场景选择、代码实现与避坑指南

做Python数据分析的人&#xff0c;十有八九都遇到过这种需求&#xff1a;手里有两组数据&#xff0c;可能是A/B测试跑出来的两个版本&#xff0c;可能是两个门店同一时期的营业额&#xff0c;也可能是两种治疗方案下患者的指标变化&#xff0c;业务方丢给你一句话&#xff1a;“…

作者头像 李华