如何找到与其他记录组匹配的记录组(关系划分?)
Posted
技术标签:
【中文标题】如何找到与其他记录组匹配的记录组(关系划分?)【英文标题】:How can I find groups of records that match other groups of records (relational division?) 【发布时间】:2012-04-10 19:08:09 【问题描述】:为了设置合并账户处理,我想找出符合以下条件的账户 拥有“完全相同”的所有者。
我认为使用动态 sql 来调整所有者可能会起作用,然后使用 排名功能,但我不想采用这种方法;我不 对可以与一个名称关联的名称有上限 给定帐户,所以我想避免使用动态 SQL。
我的数据(也位于http://www.sqlfiddle.com/#!3/1d36e)
CREATE TABLE allacctRels
(account INT NOT NULL,
module CHAR(3) NOT NULL,
custCode CHAR(20) NOT NULL)
INSERT INTO allacctrels
(account, module, custCode)
VALUES
(1, 'DDA', 'Wilkie, Walker'),
(1, 'DDA', 'Houzemeal, Juvy'),
(2, 'CDS', 'Chase, Billy'),
(2, 'CDS', 'Norman, Storm'),
(3, 'CDS', 'Chase, Billy'),
(3, 'CDS', 'Norman, Storm'),
(7, 'CDS', 'Perkins, Tony'),
(15, 'SVG', 'Wilkie, Walker'), --typo in name before mwigdahl's response
(16, 'SVG', 'Wilkie, Walker'), -- corrected typo here too
(606, 'DDA', 'Norman, Storm'),
(606, 'DDA', 'Chase, Billy'),-- corrected 2nd typo found
(4, 'LNS', 'Wilkie, Walker'),
(4, 'LNS', 'Houzemeal, Juvy'),
(44, 'DDA', 'Perkins, Tony'),
(222, 'DDA', 'Wilkie, Walker'),
(222, 'DDA', 'Houzemeal, Juvy'),
(17, 'SVG', 'Wilkie, Walker'), -- added these three rows in edit, SVG 17 doesn't match any dda
(17, 'SVG', 'Welch, Raquel'),
(17, 'SVG', 'Houzemeal, Juvy')
我想知道,对于每个 MODULE-ACCOUNT,最低 DDA 是多少 帐户具有完全相同的所有者 与之相关联。
在示例数据中,我想要这些结果,第三列是 拥有相同所有者的最低 DDA 帐户。结果应具有与模块/帐户组合相同的行数 - “SELECT DISTINCT 模块,帐户 FROM allAcctRels”中的每行一行)
1, DDA, 1
2, CDS, 606
3, CDS, 606
15, SVG, NULL
16, SVG, NULL
606, DDA, 606
4, LNS, 1
7, CDS, 44
44, DDA, 44
222, DDA, 1
17, SVG, NULL -- added to original post.
SVG 15 和 16 不匹配任何 DDA 帐户,所以没关系 它们相互匹配,它们为要合并的帐户获得 NULL。 编辑:SVG 17 不匹配任何东西,即使在 SVG 17 中有一个 DDA 账户,其所有持有人都在,但任何一个 DDA 账户都不会出现 SVG 17 中持有人的组合。 每个 DDA 帐户都会匹配自己,除非 存在具有相同所有者且 DDA 较低的 dda 帐户(如 DDA 222 的情况)。
我可以看到,一种通用方法是将每个帐户、组 透视表,并使用 row_number。给定无限数量的 与每个帐户关联的持有人,我认为旋转将需要 我宁愿避免使用动态 SQL。
在我看来,这是一个“关系划分”问题,与 关系部门可能被 CROSS APPLY “喂养”。我试过 编写一个函数,该函数将获取关联的帐户持有人表 使用特定帐户并找到最低的 dda 帐户,沿线 如下所示,这个想法是查看给定的所有人数 账户与该账户加入时的人数相同 到给定的 dda 帐户,但我不知道如何“喂”表 帐号中的功能。
-- this is what I tried but I'm not sure it the logic would work
-- and I can't figure out how to pass the account holders for each
-- account in. This is a bit changed from the function I wrote, some
-- extraneous fields removed and cryptic column names changed. So it
-- probably won't run as is.
-- to support a parameter type to a tape
-- CREATE type VisionCustomer as Table
-- (customer varchar(30))
CREATE FUNCTION consolidatable
(@custList dbo.VisionCustomer READONLY)
RETURNS char(10)
AS
BEGIN
DECLARE @retval Varchar(10)
DECLARE @howmany int
select @howmany=Count(*) FROM @custlist;
SELECT @retval = min (acct) FROM allAcctRels
JOIN @custlist
On VendorCustNo = Customer
WHERE acctType = 'DDA'
GROUP BY acct
HAVING (count(*) = @howmany)
and
COUNT(*) = (select Count(*) FROM allAcctRels X
WHERE X.acctType = 'DDA'
AND X.account = AllAcctRels.account) ;
RETURN @retval
END;
【问题讨论】:
请注意,606 DDA 行中的“Chase, Billie”与您说要返回的结果集不相符;我想你希望这是“追逐,比利”,对吧? 是的,没错,很抱歉,谢谢,我现在重新编辑 【参考方案1】:我相信这就是您正在寻找的 (http://www.sqlfiddle.com/#!3/f96c5/1):
;WITH AccountsWithOwners AS
(
SELECT DISTINCT
DA.module
, DA.account
, STUFF((SELECT
',' + AAR.custCode
FROM allacctRels AAR
WHERE AAR.module = DA.module
AND AAR.account = DA.account
ORDER BY AAR.custCode
FOR XML PATH(''))
, 1, 1, '') AS Result
FROM allacctRels DA
)
, WithLowestDda AS
(
SELECT
AWO.module
, AWO.account
, MatchingAccounts.account AS DdaAccount
, ROW_NUMBER() OVER(PARTITION BY AWO.module, AWO.account ORDER BY MatchingAccounts.account) AS Row
FROM AccountsWithOwners AWO
LEFT JOIN AccountsWithOwners MatchingAccounts
ON MatchingAccounts.module = 'DDA'
AND MatchingAccounts.Result = AWO.Result
)
SELECT
account
, module
, DdaAccount
FROM WithLowestDda
WHERE Row = 1
【讨论】:
这看起来像一个胜利者,它在我必须开始一些备份并回家时进入,我今晚会再看看它,但我发现它在我的测试数据上得到了正确的结果。我对 FOR XML 不熟悉,所以我想在接受之前弄清楚它是如何工作的。 @LevinMagruder 它基本上是在制作没有实际标记并用于创建逗号分隔列表的 XML。如果您有任何问题,请告诉我。【参考方案2】:如果我理解正确的话,这实际上非常简单。试试这个:
SELECT a.account, a.module, MIN(b.account)
FROM allacctRels a
LEFT JOIN allacctRels b ON a.custCode = b.custCode AND b.module = 'DDA'
GROUP BY a.account, a.module
编辑:在澄清之后,上述内容不起作用,但这应该。这确实是一种关系划分。可能不是世界上最有效的查询计划,但它确实有效。
SELECT a.account, a.module, MIN(b.account)
FROM allacctRels a
LEFT JOIN allacctRels b ON b.module = 'DDA'
AND
-- first test is to confirm that the number of matching names for this combination equals the number of names for the DDA set...
(
SELECT COUNT(*)
FROM allacctRels b2
INNER JOIN allacctRels a2 ON b2.custCode = a2.custCode
WHERE a.account = a2.account AND b.account = b2.account
) =
(
SELECT COUNT(*)
FROM allacctRels b2
WHERE b.account = b2.account
)
AND
-- second test is to confirm that the number of names for the DDA set equals the number of names for the base set...
(
SELECT COUNT(*)
FROM allacctRels b2
WHERE b.account = b2.account
) =
(
SELECT COUNT(*)
FROM allacctRels a2
WHERE a.account = a2.account
)
GROUP BY a.account, a.module
【讨论】:
+1;当我看到这个时,这是我脑海中突然出现的第一件事。 但是在这里你保留所有在'b'(DDA)端有任何匹配的行,不是吗?例如,如果我将“Welch Raquel”添加到每个非 DDA 帐户,非 DDA 帐户的结果不会改变,但不应该有任何匹配,因为没有非 DDA 帐户有 Raquel Welch。我的示例数据中的“Walker Wilkie”中有两个拼写错误,这使得该查询给出与我的示例相同的结果,而且我的帐户数据中没有任何示例,其中包含不应该匹配的“额外”人员。我已经更新了 sql fiddle,我也会更新上面的例子。 感谢您的澄清。我又看了一遍。 更新的答案,应该是你现在正在寻找的。span> 我不得不将模块引入 where 子句(sqlfiddle.com/#!3/8d7c0/4,以避免 sqlfiddle.com/#!3/cbb1b/1 中显示的错误)。结果看起来不错,我想我知道它是如何工作的,但是 min(b.account) 如何与 a.account “一起使用”的帐户联系起来并不是很明显,因为 LEFT JOIN 只是把所有 DDA 与左侧的每条记录对应。 – Levin Magruder 6 分钟前以上是关于如何找到与其他记录组匹配的记录组(关系划分?)的主要内容,如果未能解决你的问题,请参考以下文章