在 sql 中是否可以根据数据子集执行连接?

Is it possible in sql to perform joins based on a subsets of data?

我 运行 遇到了一个问题,我误解了什么是左连接,而且我真的不知道如何表达这个问题。我下面有一个 MVCE

declare @matchableproperties table
(
    pname varchar(100)
)

declare @users table
(
   userid varchar(100),
   pname varchar(100),
   pvalue varchar(100)
)

insert into @matchableproperties values ('city')
insert into @matchableproperties values ('status')
insert into @matchableproperties values ('position')

insert into @users values (1, 'city', 'Wichita')
insert into @users values (1, 'status', 'Active')
insert into @users values (1, 'position', 'Captain')
insert into @users values (2, 'city', 'Wichita')
insert into @users values (2, 'status', 'Active')

select u.*, mp.* from @matchableproperties mp
left join @users u on mp.pname = u.pname
order by userid, mp.pname

哪个returns

u.userid u.pname u.pvalue mp.pname
1 city Wichita city
1 position Captain position
1 status Active status
2 city Wichita city
2 status Active status

我的问题在于我要实现的目标。我想知道,对于每个用户 ID,哪些 pname 存在,哪些 pname 不存在。例如,对于用户 ID 2,“位置”pname 不存在,所以我想在下面添加一行以显示用户 ID 2 在所有指定属性上都不匹配。

u.userid u.pname u.pvalue mp.pname
2 null null position

考虑之后,我意识到我想对用户的各个分区进行左连接 table——本质上我想要

的结果
select u.*, mp.* from @matchableproperties mp
left join (select * from @users where userid = 1) u on mp.pname = u.pname
union all
select u.*, mp.* from @matchableproperties mp
left join (select * from @users where userid = 2) u on mp.pname = u.pname

上面的查询给了我想要的结果(上面的两个 tables,总共六行),但是因为我不知道有多少用户将成为用户 table 我显然不能硬编码。我可以在左连接上使用一些神奇的“分区依据”或“分组依据”语法,从两个 tables 上的单个左连接语句中获取我想要的内容吗?

您需要 属性 和用户的所有可能组合的投影。那是 CROSS JOIN。获得此投影后,您可以通过 LEFT JOIN 返回用户 table 以查看实际匹配的内容:

SELECT u0.userid, mp.pname, u.pvalue
FROM (
    SELECT DISTINCT pname FROM @matchableproperties
) mp
CROSS JOIN (
    SELECT DISTINCT userid FROM @users
) u0
LEFT JOIN @users u on u.userid = u0.userid and u.pname = mp.pname

在这里查看它的工作原理:

https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=533c75a50d6627fdc831b77c5dea47d3

交叉连接的需要往往会占用数据库服务器上的大量内存和资源,这是最好避免使用此类 EAV 模式的几个原因之一。