将表连接到具有多个外键的另一个表到同一个表

时间:2013-04-24 14:19:23

标签: sql-server sql-server-2008 tsql join

我有一个表格,其中的类别代码列在另一个表格中,有8个类别代码,目前我正在进行以下操作(下面是大量简化的):

SELECT HeaderCode
     , C1.CategoryName AS C1
     , C2.CategoryName AS C2
     , C3.CategoryName AS C3
     , C4.CategoryName AS C4
     , C5.CategoryName AS C5
     , C6.CategoryName AS C6
     , C7.CategoryName AS C7
     , C8.CategoryName AS C8


FROM Header H
INNER JOIN Cats C1 ON H.Cat1 = C1.CategoryID
INNER JOIN Cats C2 ON H.Cat2 = C2.CategoryID
INNER JOIN Cats C3 ON H.Cat3 = C3.CategoryID
INNER JOIN Cats C4 ON H.Cat4 = C4.CategoryID
INNER JOIN Cats C5 ON H.Cat5 = C5.CategoryID
INNER JOIN Cats C6 ON H.Cat6 = C6.CategoryID
INNER JOIN Cats C7 ON H.Cat7 = C7.CategoryID
INNER JOIN Cats C8 ON H.Cat8 = C8.CategoryID

SQL Fiddle Example

我确实考虑过获取数据的函数但速度很慢,因为我需要200,000条记录来获取这些信息。

性能目前还不错,但有时候我必须使用动态SQL,并且使用另外5个以上的连接执行此操作是一件很麻烦的事。

有更好/更方便/更容易维护的方法吗?

2 个答案:

答案 0 :(得分:1)

假设您无法修复架构(意味着规范化架构),您可以创建一个内联(!)表值函数,如下所示:

CREATE FUNCTION dbo.EightCats(@C1 INT, @C2 INT, @C3 INT, ...)
RETURNS TABLE
AS
RETURN
 SELECT * 
   FROM (SELECT CategoryName AS C1 FROM dbo.Cats WHERE CategoryId = @C1) T1
   CROSS JOIN (SELECT CategoryName AS C2 FROM dbo.Cats WHERE CategoryId = @C2) T2
   CROSS JOIN (SELECT CategoryName AS C3 FROM dbo.Cats WHERE CategoryId = @C2) T3
   ...

然后你可以像这样编写你的查询:

SELECT H.HeaderCode, EC.*
  FROM dbo.Header H
 CROSS APPLY dbo.EightCats(H.Cat1,H.Cat2,H.Cat3,H.Cat4,H.Cat5,H.Cat6,H.Cat7,H.Cat8) EC;

这应该导致相同的执行计划,因此不会对此造成性能损失。重要的是,您仅将其用于八个类别。如果您有另一个只有七个类别的查询,则需要创建一个新函数。否则你(或者更确切地说SQL Server)将最终做不必要的连接。

答案 1 :(得分:0)

我会用桥牌表来做这件事。

CREATE TABLE HeaderCategory (
  HeaderCategoryId int IDENTITY(1,1) NOT NULL
, HeaderId int
, CategoryId int
, SortBy int
)

将HeaderId和CategoryId外键设为各自的表。 SortBy列用于指示它是否为Category1,2,3等

这是更“规范化”的架构。

然后,为了获得您在问题中获得的相同查询输出,请执行简单的PIVOT查询。

如果您不想更改表格,还有两种可能性: 1)编写一个GetCategoryName(CategoryId)函数,并在你的选择中使用它,如下所示: SELECT Header,GetCategoryName(Category1)AS C1,...

2)创建一个包含CategoryNames的VIEW。