如何查找与其他记录组匹配的记录组(关系分区?)

时间:2012-04-10 19:08:09

标签: sql-server sql-server-2008 relational-division

为了设置合并帐户处理,我想查找帐户 拥有“完全相同”的所有者。

我认为用动态sql转动所有者可能会有效,然后使用 排名功能,但我不想采用这种方法;我不 有一个上限可以与a关联的名称 给定帐户,所以我想避免动态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帐户,SVG 17中的持有者组合也不会出现在任何一个DDA帐户中。每个DDA帐户都将匹配自己,除非a 存在相同所有者和较低DDA的dda帐户(如DDA 222的情况)。

我可以看到一种通用的方法是转动每个帐户,组 旋转表,并使用row_number。鉴于无限数量 与每个帐户相关联的持有人,我认为转移将采取 动态SQL我宁​​愿避免。

在我看来,这是一个“关系分裂”的问题 关系分裂可能被交叉应用“喂”。我试过了 编写一个将帐户持有人表关联起来的功能 使用特定帐户并找到最低的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;

2 个答案:

答案 0 :(得分:2)

我相信这就是你要找的东西(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

答案 1 :(得分:1)

如果我理解正确的话,这实际上非常简单。试试这个:

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