您可以运行所有这些 SQL 并查看结果 here .
我有一张俱乐部表(俱乐部就像一群人或一群人,比如“游泳俱乐部”或“针织俱乐部”)。
DECLARE @club TABLE (
Id INT
,Name NVARCHAR(255)
);
INSERT INTO @club VALUES
(1, 'Swim Club')
,(2, 'Knitting Club')
,(3, 'Bridge Club');
我有一张成员(member)表。
DECLARE @member TABLE (
Id INT
,Name NVARCHAR(255)
);
INSERT INTO @member VALUES
(1, 'John Jones')
,(2, 'Sally Smith')
,(3, 'Rod Roosevelt')
,(4, 'Bobby Burns')
,(5, 'Megan Moore');
成员(member)可以属于许多俱乐部,因此有一个成员(member)表,将俱乐部与成员(member)联系起来(还描述了会费价格)。
DECLARE @membership TABLE (
Id INT
,Member INT --FK to @member
,Club INT --FK to @club
,Dues INT --the cost of membership
);
INSERT INTO @membership VALUES
(1,1,1,10)
,(2,1,2,5)
,(3,2,1,10)
,(4,2,3,20)
,(5,3,1,10)
,(6,3,2,5)
,(7,4,2,5)
,(8,4,3,20)
,(9,5,1,10)
,(10,5,3,20);
大多数成员(member)只需支付相关的会费。但是,有些成员(member)是由其他成员(member)赞助的。因此,这些被赞助成员(member)的会费将由另一名成员(member)(赞助商)支付。因此,我们有一个赞助表。赞助表将赞助商(支付会费)连接到特定俱乐部的赞助商(由赞助商支付会费)。因为赞助是特定于俱乐部的,所以赞助记录连接两个成员(member)记录而不是两个成员(member)记录。
DECLARE @sponsorship TABLE (
Id INT
,Sponsee_Membership INT --FK to Sponsee's @membership record
,Sponsor_Membership INT --FK to Sponsor's @membership record
);
INSERT INTO @sponsorship VALUES
(1,5,1)
,(2,8,4)
,(3,9,3)
,(4,10,4);
为了全面了解我们的俱乐部/成员(member)/赞助商,我们有:
SELECT
mship.Id AS 'Mship'
,mem.Name AS 'Member'
,c.Name AS 'Club'
,mship.Dues
,spons_mem.Name AS 'Sponsor'
FROM
@membership AS mship
JOIN @member AS mem
ON mship.Member = mem.Id
JOIN @club AS c
ON mship.Club = c.Id
LEFT JOIN @sponsorship AS spons
ON spons.Sponsee_Membership = mship.Id
LEFT JOIN @membership AS spons_mship
ON spons_mship.Id = spons.Sponsor_Membership
LEFT JOIN @member AS spons_mem
ON spons_mem.Id = spons_mship.Member;
这给了我们这些 结果 :
Mship Member Club Dues Sponsor
1 John Jones Swim Club 10 NULL
2 John Jones Knitting Club 5 NULL
3 Sally Smith Swim Club 10 NULL
4 Sally Smith Bridge Club 20 NULL
5 Rod Roosevelt Swim Club 10 John Jones
6 Rod Roosevelt Knitting Club 5 NULL
7 Bobby Burns Knitting Club 5 NULL
8 Bobby Burns Bridge Club 20 Sally Smith
9 Megan Moore Swim Club 10 Sally Smith
10 Megan Moore Bridge Club 20 Sally Smith
赞助应该涵盖所有共享成员资格。
Mship=7
中看到这一点和 Mship=8
. 抱歉设置太长了。 这是我的实际问题 :
在示例中,我们有行
Mship=5
和 Mship=6
. 我可以使用游标/WHILE 循环来完成此操作,但我知道此类解决方案通常不会采用适当的基于集合的方法。对此的正确查询是什么样的?
非常感谢。
最佳答案
这是一个可能响应您的要求的 SQL 查询。
逻辑是使用子查询根据映射member.id
生成赞助商和赞助商之间的映射。而不是 memberships.id
;为此,我们使用聚合。然后,外部查询搜索赞助商和赞助商都参加但在赞助表中没有声明关系的俱乐部
该查询为每个违规成员返回一条记录,其中包含 sponsee 和赞助商名称。
SELECT mship1.Id, m1.Name Member, m2.Name Sponsor, c.Name Club, mship1.Dues
FROM
@membership mship1
INNER JOIN @club c ON c.Id = mship1.Club
INNER JOIN (
SELECT ms1.Member Sponsee_Member , MAX(ms2.Member) Sponsor_Member
FROM @sponsorship ss
INNER JOIN @membership ms1 ON ms1.Id = Sponsee_Membership
INNER JOIN @membership ms2 ON ms2.Id = Sponsor_Membership
GROUP BY ms1.Member
) rels ON rels.Sponsee_Member = mship1.Member
INNER JOIN @membership mship2 ON mship2.Member = rels.Sponsor_Member AND mship2.Club = mship1.Club
INNER JOIN @member m1 ON m1.Id = mship1.Member
INNER JOIN @member m2 ON m2.Id = mship2.Member
LEFT JOIN @sponsorship sship ON sship.Sponsor_Membership = mship2.Id
WHERE sship.Id IS NULL
;
在 the rextester that you provided ,这将返回:
Id | Member | Sponsor | Club | Dues
-----|-----------------|--------------|----------------|-----
6 | Rod Roosevelt | John Jones | Knitting Club | 5
创建此查询的工作让我想到您可以优化数据库设计。当前的模型将难以保持一致性:您的问题本身就证明了这一点。将来,如果赞助商注册一个新俱乐部,而他的一名赞助商已经参加了,会发生什么?您将再次需要检测丢失的
sponshorship
关系,并以某种方式创建它。您实际上在赞助商和 sponsee 之间建立了 1-1 关系,因为您声明赞助应该涵盖所有共享成员资格。看起来您不会允许赞助商拥有多个赞助商,即使是跨不同的俱乐部。
我建议你放弃
sponshorship
表并直接在成员表中存储发起人的自外键。从那里开始,很容易检查两个成员有哪些共同点,并使用 SQL 查询正确分配会费。
关于sql - 识别具有不一致关系的记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54754812/