SQL Server Left Outer Join返回太多行或不正确的行

时间:2015-03-18 20:00:07

标签: sql-server left-join multiple-tables

我有一个SQL Server查询,我想要左边连接到2个表。我需要它来返回第一个表中的所有行,只返回其他2个表中的匹配数据。我的第一个化身创造了某种十字产品,其所需行数超过10倍。所以我添加了一个rowcount函数并让它过滤所有rowcount值= 1.现在它返回正确的行数,但是其他2个表中的数据错误。

我有一些过滤规则(case语句)我应用于2个右表的内容,以确定返回的内容(如果有的话)。我认为这可能是问题的根源,但我不确定。我已尝试在select语句中使用过滤器代码,并在查询结束时使用where语句,甚至作为join on语句的一部分。我也尝试过使用cross和outer。一切都没有用。

我试图在不使用循环的情况下实现此目的。可能吗?请参阅下面的代码。此代码生成太多行。我不会重复第二次迭代,因为它在顶部添加了一个额外的选择,并且在底部添加了m = 1的过滤器以生成正确的行数。

这是我要加入的2个表格。请注意,第二个表只是更大表的一小部分。

感谢!!!

SELECT
GLENTRY.Fac + GLENTRY.Rundt + GLENTRY.Jrnllog AS UniqueID,
GLENTRY.Fac AS Fac,
(CASE WHEN 
    EntityTranslate2014.AcctEnd <> '' AND EntityTranslate2014.AcctEnd = SUBSTRING(GLENTRY.Acct,6,3)
THEN 
    EntityTranslate2014.Entity 
ELSE
    CASE WHEN
        EntityTranslate2014.AcctEnd <> 0 AND EntityTranslate2014.Acct = GLENTRY.Acct
    THEN 
        EntityTranslate2014.Entity 
    ELSE
        CASE WHEN 
            EntityTranslate2014.SSDept <> 0 AND EntityTranslate2014.SSDept = SUBSTRING(GLENTRY.Acct,1,4)
        THEN 
            EntityTranslate2014.Entity 
        ELSE
            CASE WHEN 
                EntityTranslate2014.SSDept = 0
                AND
                EntityTranslate2014.AcctEnd = ''
                AND
                EntityTranslate2014.Acct = 0 
            THEN 
                EntityTranslate2014.Entity
            ELSE
                '' 
            END
        END
    END
END)
AS NEWEntity,

(CASE WHEN AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0' 
THEN 
    AcctTranslate2014.NEWDept 
ELSE 
    Null 
END)
AS Department,

(CONVERT(DATETIME,GLENTRY.Period + '/01/' + 
                        CASE WHEN 
                            CONVERT(NVARCHAR(4),GLENTRY.Yearz) = '' 
                        THEN 
                            '2014' 
                        ELSE 
                            GLENTRY.Yearz
                        END) 
AS YEARPER,

GLENTRY.Acct) AS SSAcct,
CONVERT(NVARCHAR(20),
            CASE WHEN 
                AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
            THEN 
                AcctTranslate2014.NEWAcct 
            ELSE 
                Null 
            END)
AS NEWAccount,
GLENTRY.Rundt,
GLENTRY.Jrnlid,
GLENTRY.Amount,
GLENTRY.Dc,
GLENTRY.Ref,
GLENTRY.Refdt,
CONVERT(NVARCHAR(100),'Import of SS ' + CONVERT(VARCHAR(2),GLENTRY.Period) + '/' + CONVERT(VARCHAR(4),GLENTRY.Yearz) + ' GL activity') AS Descr,
GLENTRY.Invnr,
GLENTRY.Jrnllog,
ROW_NUMBER() OVER(PARTITION BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period, GLENTRY.Pagez, GLENTRY.Acct, GLENTRY.Rundt, GLENTRY.Jrnlid,
GLENTRY.Amount, GLENTRY.Dc, GLENTRY.Ref, GLENTRY.Refdt, GLENTRY.Descr, GLENTRY.Invnr, GLENTRY.Origfac, GLENTRY.Jrnllog, GLENTRY.Seq
ORDER BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period) AS m

FROM GLENTRY 
LEFT OUTER JOIN
    EntityTranslate2014 ON GLENTRY.Fac = EntityTranslate2014.Fac 
LEFT OUTER JOIN
    AcctTranslate2014 ON CONVERT(VARCHAR(8),GLENTRY.Acct) = AcctTranslate2014.SSAcct
WHERE GLENTRY.Yearz = 2014 AND GLENTRY.Period = 11


EntityTranslate2014 File

Fac Entity  Descr       AcctEnd SSDept  Acct
1   51900   Entity1     0               0
2   50901   Entity2     0               0
3   10100   Entity3     0               0
3   10500   Entity4     4016            0
3   10500   Entity4     4020            0
3   10500   Entity4     4022            0
3   10500   Entity4     4024            0
3   10500   Entity4     4028            0
3   10500   Entity4     7016            0
4   30900   Entity5     0               0
5   10300   Entity6     0               0
6   11300   Entity7     0               0
7   11100   Entity8     0               0
7   11500   Entity9     4016            0
7   11500   Entity9     4020            0
7   11500   Entity9     4022            0
7   11500   Entity9     4024            0
7   11500   Entity9     4028            0
7   11500   Entity9     7016            0
9   32909   Entity10    0               0
10  12100   Entity11    0               0
11  32901   Entity12    0               0
12  53900   Entity13    0               0
13  10200   Entity14    0               0
14  32914   Entity15    0               0
15  32915   Entity16    0               0
16  11200   Entity17    0               0
17  32917   Entity18    0               0
18  32918   Entity19    0               0
19  32919   Entity20    0               0
20  32920   Entity21    0               0
21  13100   Entity22    0               0
22  52900   Entity23    0               0
89  99900   Entity24    0               0
123 12300   Entity25    0               0
124 12200   Entity26    0               0
133 13300   Entity27    0               0
201 11201   Entity28    0               0
202 11202   Entity29    0               0
402 25402   Entity30    0               0
403 25403   Entity31    0               0
549 25430   Entity32    0               0
549 25432   Entity33    7195            0
910 50910   Entity34    0               0
911 50911   Entity35    0               0
21  13500   Entity36    4016            0
21  13500   Entity36    4020            0
21  13500   Entity36    4022            0
21  13500   Entity36    4024            0
21  13500   Entity36    4028            0
21  13500   Entity36    7016            0
16  11202   Entity37    0        002    0
16  11201   Entity37    0        001    0
16  11200   Entity38    0               30918000
16  11200   Entity38    0               31918000
16  11200   Entity38    0               32110000


AcctTranslate2014

NewAcct     SSAcct      NewDEpt Fac
10111500    111200010   000     0
10111600    111200050   000     0
10111700    111550010   000     0
10113092    111050450   000     0
10115090    111050010   000     0

5 个答案:

答案 0 :(得分:1)

...更新

您可以使用ROW_NUMBER计算联接结果中的序列,并使用ORDER BY对它们进行排序,以便您想要的行在该序列中排在第一位。

感谢您发布循环结构,我认为我能够通过一系列CTE(WITH别名为(...))来近似它的功能。我不确定我的所有表/字段名称是否正确,但这应该接近功能。我的一个主要猜测是&#34;第一个&#34;应该确定实体,您可能需要在CTE中的行号函数中调整顺序。

WITH ENTFIND AS (
    SELECT  
        EntityTranslate2014.Fac,
        EntityTranslate2014.Acct,
        EntityTranslate2014.AcctEnd,
        EntityTranslate2014.Entity,
        EntityTranslate2014.SSDept,
        EntityTranslate2014.Entity,
        ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E1Sort
    FROM
        EntityTranslate2014
),
E2 AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E2Sort
    FROM
        ENTFIND
    WHERE
        AcctEnd <> 0
),
E3 AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E3Sort
    FROM
        ENTFIND
    WHERE
        SSDept <> 0
),
E4 AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E4Sort
    FROM
        ENTFIND
    WHERE
        Acct = 0
        AND
        AcctEnd = ''
        AND
        SSDept = 0
)
SELECT
    GLENTRY.Fac + GLENTRY.Rundt + GLENTRY.Jrnllog AS UniqueID,
    GLENTRY.Fac AS Fac,,
    E1.Entity,
    E2.Entity,
    E3.Entity,
    E4.Entity,
    COALESCE(E1.Entity,E2.Entity,E3.Entity,E4.Entity) as 1stFoundEntity,
(CASE WHEN 
    EntityTranslate2014.AcctEnd <> '' AND EntityTranslate2014.AcctEnd = SUBSTRING(GLENTRY.Acct,6,3)
THEN 
    EntityTranslate2014.Entity 
ELSE
    CASE WHEN
        EntityTranslate2014.AcctEnd <> 0 AND EntityTranslate2014.Acct = GLENTRY.Acct
    THEN 
        EntityTranslate2014.Entity 
    ELSE
        CASE WHEN 
            EntityTranslate2014.SSDept <> 0 AND EntityTranslate2014.SSDept = SUBSTRING(GLENTRY.Acct,1,4)
        THEN 
            EntityTranslate2014.Entity 
        ELSE
            CASE WHEN 
                EntityTranslate2014.SSDept = 0
                AND
                EntityTranslate2014.AcctEnd = ''
                AND
                EntityTranslate2014.Acct = 0 
            THEN 
                EntityTranslate2014.Entity
            ELSE
                '' 
            END
        END
    END
END)
AS NEWEntity,

(CASE WHEN AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0' 
THEN 
    AcctTranslate2014.NEWDept 
ELSE 
    Null 
END)
AS Department,

(CONVERT(DATETIME,GLENTRY.Period + '/01/' + 
                        CASE WHEN 
                            CONVERT(NVARCHAR(4),GLENTRY.Yearz) = '' 
                        THEN 
                            '2014' 
                        ELSE 
                            GLENTRY.Yearz
                        END) 
AS YEARPER,

GLENTRY.Acct) AS SSAcct,
CONVERT(NVARCHAR(20),
            CASE WHEN 
                AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
            THEN 
                AcctTranslate2014.NEWAcct 
            ELSE 
                Null 
            END) AS NEWAccount,
    GLENTRY.Rundt,
    GLENTRY.Jrnlid,
    GLENTRY.Amount,
    GLENTRY.Dc,
    GLENTRY.Ref,
    GLENTRY.Refdt,
    CONVERT(NVARCHAR(100),'Import of SS ' + CONVERT(VARCHAR(2),GLENTRY.Period) + '/' + CONVERT(VARCHAR(4),GLENTRY.Yearz) + ' GL activity') AS Descr,
    GLENTRY.Invnr,
    GLENTRY.Jrnllog,
    ROW_NUMBER() OVER(PARTITION BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period, GLENTRY.Pagez, GLENTRY.Acct,         GLENTRY.Rundt, GLENTRY.Jrnlid,
        GLENTRY.Amount, GLENTRY.Dc, GLENTRY.Ref, GLENTRY.Refdt, GLENTRY.Descr, GLENTRY.Invnr, GLENTRY.Origfac,          GLENTRY.Jrnllog, GLENTRY.Seq
    ORDER BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period) AS m
FROM
    GLENTRY
    LEFT JOIN 
        ENTFIND E1 ON GLENTRY.Fac = E1.Fac
                AND SUBSTRING(GLENTRY.Acct,6,3) = E1.AcctEnd
                    AND E1Sort = 1
    LEFT JOIN 
        E2 ON GLENTRY.Fac = E2.Fac
                AND GLENTRY.Acct = E2.Acct
                    AND E2.E2Sort = 1
    LEFT JOIN 
        E3 ON GLENTRY.Fac = E3.Fac
                AND SUBSTRING(GLENTRY.Acct,1,4) = E3.SSDept
                    AND E3.E3Sort = 1
    LEFT JOIN 
        E4 ON GLENTRY.Fac = E4.Fac
                AND E4.E4Sort = 1
    LEFT OUTER JOIN
            EntityTranslate2014 ON GLENTRY.Fac = EntityTranslate2014.Fac 
    LEFT OUTER JOIN
            AcctTranslate2014 ON CONVERT(VARCHAR(8),GLENTRY.Acct) = AcctTranslate2014.SSAcct
WHERE 
    GLENTRY.Yearz = 2014 AND GLENTRY.Period = 11

答案 1 :(得分:1)

FROM GLENTRY
LEFT OUTER JOIN EntityTranslate2014
    ON GLENTRY.Fac = EntityTranslate2014.Fac
LEFT OUTER JOIN AcctTranslate2014
    ON CONVERT(VARCHAR(8), GLENTRY.Acct) = AcctTranslate2014.SSAcct

EntityTranslate2014AcctTranslate2014之间没有定义关系。如果GLENTRY中相应记录的任一表中有多个记录,则这两个表将有效地相互交叉连接。例如,如果单个GLENTRY加入EntityTranslate2014中的两个记录和AcctTranslate2014中的两个记录,那么您将获得4个记录,每个记录对应一个可能的组合。这就是JOIN的定义方式。

如果您知道这种情况正在发生并且您知道实际上没有任何关系,并且您只想要GLENTRY EntityTranslate2014AcctTranslate2014中的每条记录与第一条记录相匹配EntityTranslate2014以及AcctTranslate2014中与FROM GLENTRY LEFT OUTER JOIN ( SELECT *, ROW_NUMBER() OVER (PARTITION BY Fac ORDER BY <SomeField>) row_order FROM EntityTranslate2014) ET2014 ON GLENTRY.Fac = ET2014.Fac LEFT OUTER JOIN ( SELECT *, ROW_NUMBER() OVER (PARTITION BY SSAcct ORDER BY <SomeField>) row_order FROM AcctTranslate2014) AT2014 ON CONVERT(VARCHAR(8), GLENTRY.Acct) = AT2014.SSAcct AND AT2014.row_order = ET2014.row_order 中的第二条记录匹配的第二条记录,依此类推,您可以做我所听到的称为“ZIP加入”的内容:

SELECT *

显然,不要使用NULL;这只是一个例子。所以,这样做是为每个OUTER表分配一些顺序,并将它们相互匹配。因此,每个OUTER表中的每条记录只会出现一次。如果不匹配,则其他表中的记录将显示为GLENTRY s。

另一个常见的替代方法是简单地返回每个OUTER表的第一条记录,这样您就可以保证只有一条{{1}}条记录,因为每个OUTER表只为每个键字段贡献一条或零条记录。

如果 不能用于您的目的,那么您可能需要使用两个查询,并且需要在您的应用程序中进行匹配。

答案 2 :(得分:1)

您可以使用相关子查询来&#34;循环&#34;通过一个表中的每一行将它与另一个表中的行进行比较而不实际编写循环逻辑(在SQL中应避免使用)。内部查询中的行将根据where谓词与外部查询中的每一行进行比较。如果在WHERE子句查询中返回多于1条记录,则相关子查询还允许您通过TOP 1过滤WHERE子句(如下面的代码所示)。这只是一个简单的例子,因为我没有时间完整地写出代码,但它可能会有所帮助。

SELECT Entity
  FROM EntityTranslate2014 ent
 WHERE ent.AcctEnd <> ''
   AND AcctEnd = (SELECT TOP 1 SUBSTRING(glt.Acct,6,3) 
                    FROM Glentry glt 
                   WHERE glt.fac = ent.fac
                     AND glt.Year = 2014
                     AND glt.Period = 11
                   ORDER BY glt.Fac, glt.Yearz, glt.Period)

在此示例中,EntityTranslate2014表中的每条记录都会根据Glentry.Fac = EntityTraslate2014.fac的谓词与内部查询的结果进行比较。希望有所帮助。

有关相关子查询的更多信息,请查看以下链接。

https://technet.microsoft.com/en-us/library/ms187638%28v=sql.105%29.aspx

答案 3 :(得分:1)

我想在我试图让我的查询工作时感谢大家的想法。我最终得到了以下解决方案。注意,为了简洁起见,我只包括用于确定实体字段的代码。它不漂亮,我确信它是最佳的,但它确实有效。

再次感谢您的帮助!!

CASE WHEN 
    (SELECT 
        BET.Entity 
    FROM 
        BET 
    WHERE 
        GL.Fac = BET.Fac 
        AND 
        BET.AcctEnd <> '' 
        AND 
        BET.AcctEnd = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),6,3)) 
        IS NOT Null
THEN
    (SELECT 
        BET.Entity 
    FROM 
        SOSViews.dbo.BI360EntityTranslate2014 BET 
    WHERE 
        GL.Fac = BET.Fac AND BET.AcctEnd <> '' 
        AND 
        BET.AcctEnd = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),6,3)) 
ELSE
    CASE WHEN 
        (SELECT 
            BET.Entity 
        FROM 
            SOSViews.dbo.BI360EntityTranslate2014 BET 
        WHERE 
            GL.Fac = BET.Fac 
            AND 
            BET.SOSDept <> 0
            AND 
            BET.SOSDept = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),1,4)) 
            IS NOT Null
    THEN
        (SELECT 
            BET.Entity 
        FROM 
            BET 
        WHERE 
            GL.Fac = BET.Fac 
            AND 
            BET.SOSDept <> 0
            AND 
            BET.SOSDept = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),1,4)) 
    ELSE
        CASE WHEN 
            (SELECT 
                BET.Entity 
            FROM 
                BET 
            WHERE 
                GL.Fac = BET.Fac 
                AND 
                BET.SOSDept = 0 AND BET.AcctEnd = '' AND BET.Acct = 0) 
                IS NOT Null
        THEN
            (SELECT 
                BET.Entity 
            FROM 
                BET 
            WHERE 
                GL.Fac = BET.Fac 
                AND 
                BET.SOSDept = 0 AND BET.AcctEnd = '' AND BET.Acct = 0) 
        ELSE
            'No Entity Translation'
        END
    END
END)
AS Entity,

答案 4 :(得分:0)

注意:虽然它可能无法直接应用于OP的问题,但有时如果连接中的行太多,则只需加入错误的列即可。这恰好发生在我身上......