如何通过单条SQL查询获取与指定A表行属性完全相同的其他行?
单条SQL实现属性完全匹配的行查询
当然可以用单条SQL搞定这个需求!核心思路是:先锁定目标行的属性集合,然后从A表其他行里筛选出属性数量完全相同,且每一个属性都和目标行的属性完全匹配的记录。
先补全示例数据(方便测试)
假设我们的表数据是这样的:
A表
| id | name |
|---|---|
| 1 | first item |
| 2 | second item |
| 3 | third item |
| 4 | fourth item |
| 5 | fifth item |
B表
| id | attr_name | value |
|---|---|---|
| 1 | color | red |
| 2 | size | large |
| 3 | color | red |
| 4 | size | large |
| 5 | color | blue |
| 6 | size | small |
| 7 | color | red |
| 8 | weight | heavy |
C表(关联表)
| table_a_id | table_b_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 5 |
| 3 | 6 |
| 4 | 1 |
| 4 | 2 |
| 5 | 7 |
| 5 | 8 |
比如选中A表id=1的行,我们要找出其他和它属性完全相同的行(也就是id=2和id=4,因为它们的属性都是color=red+size=large)。
核心SQL实现
SELECT a.id, a.name FROM A a JOIN C c ON a.id = c.table_a_id JOIN B b ON c.table_b_id = b.id WHERE a.id != 1 -- 排除选中的目标行 GROUP BY a.id, a.name HAVING -- 条件1:当前行的属性数量和目标行完全一致(数量不同直接排除) COUNT(*) = ( SELECT COUNT(*) FROM C c_target JOIN B b_target ON c_target.table_b_id = b_target.id WHERE c_target.table_a_id = 1 ) -- 条件2:当前行的所有属性,都能在目标行的属性集合中找到完全匹配的项 AND NOT EXISTS ( SELECT 1 FROM B b_current JOIN C c_current ON b_current.id = c_current.table_b_id WHERE c_current.table_a_id = a.id AND NOT EXISTS ( SELECT 1 FROM B b_target JOIN C c_target ON b_target.id = c_target.table_b_id WHERE c_target.table_a_id = 1 AND b_target.attr_name = b_current.attr_name AND b_target.value = b_current.value ) );
代码解释
- 外层查询:先关联A、C、B三张表,获取所有非目标行的属性记录,然后按A表的
id和name分组,这样每组代表A表一行的所有属性。 - 第一个HAVING条件:先统计目标行的属性数量,要求当前行的属性数量和它完全相同——如果数量不一样,哪怕属性大部分重合,也直接排除。
- 第二个HAVING条件:用双重
NOT EXISTS做校验:确保当前行的每一个属性(attr_name+value),都能在目标行的属性集合里找到一模一样的,反过来保证当前行没有额外的、目标行没有的属性。
通用化改造
如果要把目标行的id做成可替换的参数(比如在应用程序里用占位符),可以改成这样:
SELECT a.id, a.name FROM A a JOIN C c ON a.id = c.table_a_id JOIN B b ON c.table_b_id = b.id WHERE a.id != ? -- 替换为选中的A表id GROUP BY a.id, a.name HAVING COUNT(*) = ( SELECT COUNT(*) FROM C c_target JOIN B b_target ON c_target.table_b_id = b_target.id WHERE c_target.table_a_id = ? -- 替换为选中的A表id ) AND NOT EXISTS ( SELECT 1 FROM B b_current JOIN C c_current ON b_current.id = c_current.table_b_id WHERE c_current.table_a_id = a.id AND NOT EXISTS ( SELECT 1 FROM B b_target JOIN C c_target ON b_target.id = c_target.table_b_id WHERE c_target.table_a_id = ? -- 替换为选中的A表id AND b_target.attr_name = b_current.attr_name AND b_target.value = b_current.value ) );
注意事项
- 这个方法不依赖B表的
id,只看attr_name和value的组合,完全符合“属性完全相同”的需求。 - 如果你的数据库支持窗口函数或者集合操作(比如PostgreSQL的
ARRAY_AGG+数组对比),也可以用更简洁的写法,但上面的SQL是跨数据库通用的。
内容的提问来源于stack exchange,提问作者Peter222
相关产品推荐
相关产品推荐

