展平包含引用 SQL Server 2005 中其他行的行的表

Posted

技术标签:

【中文标题】展平包含引用 SQL Server 2005 中其他行的行的表【英文标题】:Flattening a table that contains rows that reference other rows in SQL Server 2005 【发布时间】:2010-05-26 15:33:14 【问题描述】:

当您处理未完全规范化的表时,我遇到了一个偶尔会出现的问题。这是问题所在。想象一个有 4 列的表,我们称这个表为 dbo.Hierarchical。这是表的定义:

if OBJECT_ID('dbo.Hierarchical') is not null
 drop table dbo.Hierarchical

create table dbo.Hierarchical
(
  colID   int   not null identity(1,1) primary key
 ,GroupName  varchar(5) not null
 ,IsAtomic  bit   not null
 ,Constituent varchar(5) null
)

此表可以有一个 Atomic 的 GroupName,这意味着它没有组件,或者不能是 Atomic。在这种情况下,一个 GroupName 可以包含其他 GroupName。

为了清楚起见,让我们用一些数据填充表格。

set nocount on
insert into dbo.Hierarchical values ('A',0,'B')
insert into dbo.Hierarchical values ('A',0,'C')
insert into dbo.Hierarchical values ('B',1,'B')
insert into dbo.Hierarchical values ('C',0,'K')
insert into dbo.Hierarchical values ('C',0,'L')
insert into dbo.Hierarchical values ('D',0,'E')
insert into dbo.Hierarchical values ('D',0,'F')
insert into dbo.Hierarchical values ('D',0,'G')
insert into dbo.Hierarchical values ('E',1,'E')
insert into dbo.Hierarchical values ('F',1,'F')
insert into dbo.Hierarchical values ('G',0,'H')
insert into dbo.Hierarchical values ('G',0,'I')
insert into dbo.Hierarchical values ('H',1,'H')
insert into dbo.Hierarchical values ('I',1,'I')
insert into dbo.Hierarchical values ('J',1,'J')
insert into dbo.Hierarchical values ('K',1,'K')
insert into dbo.Hierarchical values ('L',1,'L')
insert into dbo.Hierarchical values ('M',1,'M')
insert into dbo.Hierarchical values ('N',1,'N')
set nocount off

现在,如果我们查看一个简单的 select * from dbo.Hierarchical,我们会得到以下结果:

GroupName  colID      IsAtomic   Constituent
A           1         0          B
A           2         0          C
B           3         1          B
C           4         0          K
C           5         0          L
D           6         0          E
D           7         0          F
D           8         0          G
E           9         1          E
F          10         1          F
G          11         0          H
G          12         0          I
H          13         1          H
I          14         1          I
J          15         1          J
K          16         1          K
L          17         1          L
M          18         1          M
N          19         1          N

唷,那是长篇大论。现在,请注意前两行有 GroupName A 和 Constiuents B 和 C。B 是 Atomic,所以它没有更多的成分。然而,C 有成分 K、L(K 和 L 是原子的)。我怎样才能创建一个将这个表展平的视图,以便我只能看到 GroupName 和 Atomic 成分。在 GroupName A 的情况下,我应该看到 3 行

A B
A K
A L

【问题讨论】:

+1 获取完整问题并提供 DDL 和示例数据。干得好。 成分可以包含自己的成分,还是最多有两个层次结构? 我不理解输出,因为 KL 从未出现在 GroupName A 的行中。您是否希望为每个组名列出原子成分,无论它们是否出现在具有该组名的数据中? 抱歉含糊不清。 GroupNames 可以是原子的,因此它们本身只有 1 个组成部分。所以 K 是原子的,因此它只能包含它自己。另一方面,A 不是原子的,因此它可以包含多个组,在这种情况下是 B 和 C。现在 B 是原子的,因此它包含自身。另一方面,C 包含 K 和 L。逻辑上,最后,A 的原子成分是 B、K 和 L。我想构建一个视图来显示每个 GroupName 及其原子成分。所以在 A 的情况下,我应该看到三行(A-> B,和 A-K,A-L)。关于嵌套,可以嵌套n次 我的回答是你想要的吗? 【参考方案1】:

试一试:

--just a repeat of OP's original table and data
DECLARE @Hierarchical table
( colID   int   not null identity(1,1) primary key
 ,GroupName  varchar(5) not null
 ,IsAtomic  bit   not null
 ,Constituent varchar(5) null)
set nocount on
insert into @Hierarchical values ('A',0,'B');insert into @Hierarchical values ('A',0,'C');
insert into @Hierarchical values ('B',1,'B');insert into @Hierarchical values ('C',0,'K');
insert into @Hierarchical values ('C',0,'L');insert into @Hierarchical values ('D',0,'E');
insert into @Hierarchical values ('D',0,'F');insert into @Hierarchical values ('D',0,'G');
insert into @Hierarchical values ('E',1,'E');insert into @Hierarchical values ('F',1,'F');
insert into @Hierarchical values ('G',0,'H');insert into @Hierarchical values ('G',0,'I');
insert into @Hierarchical values ('H',1,'H');insert into @Hierarchical values ('I',1,'I');
insert into @Hierarchical values ('J',1,'J');insert into @Hierarchical values ('K',1,'K');
insert into @Hierarchical values ('L',1,'L');insert into @Hierarchical values ('M',1,'M');
insert into @Hierarchical values ('N',1,'N');set nocount off

--declare and set starting position
DECLARE @Start  varchar(5)
SET @Start='A'

--get the data
;WITH HierarchicalTree AS
(
    SELECT 
        GroupName, Constituent,  1 AS LevelOf
        FROM @Hierarchical
        WHERE GroupName=@Start
    UNION ALL
        SELECT 
            t.GroupName, h.Constituent, t.LevelOf+1
        FROM HierarchicalTree         t
            INNER JOIN @Hierarchical  h ON t.Constituent=h.GroupName
        WHERE h.Constituent!=h.GroupName AND h.IsAtomic=0
)
SELECT
    t.GroupName,t.Constituent
    FROM HierarchicalTree        t
        INNER JOIN @Hierarchical h ON t.Constituent=h.GroupName
    WHERE h.IsAtomic=1

输出:

GroupName Constituent
--------- -----------
A         B
A         K
A         L

(3 row(s) affected)

【讨论】:

这就是答案!!这工作得很好。我现在正在研究递归逻辑,看看我是否理解它,但它产生了完全正确的表!!!非常感谢。【参考方案2】:

这可以满足您的要求,但只有嵌套一次才会起作用。如果需要递归,则必须使用 CTE。

select a.GroupName,
        b.Constituent

From dbo.Hierarchical a

Left Join dbo.Hierarchical b on a.Constituent = b.GroupName

这是您需要的还是我完全错过了重点?

【讨论】:

这几乎是对的,但问题是它只处理 2 层嵌套。因此,如果我要通过将 insert 插入 dbo.Hierarchical 值 ('L',0,'M') 来使 L 成为非原子插入 dbo.Hierarchical 值 ('L',0,'N'),则此查询不会给我 A -> B A -> K A -> M A -> N 的预期结果我想我需要一些东西,它可以递归地处理表格中的所有嵌套级别,而无需事先知道表格上的嵌套有多深。 是的,您需要使用公用表表达式 (CTE)。 4guysfromrolla.com/webtech/071906-1.shtml 是的,我认为 CTE 是要走的路,但我不知道如何编写 CTE,以便抓取嵌套级别并为我提供 GroupName 和 Atomic Consituents。如果我弄清楚了,我会发布它。【参考方案3】:

为了完整起见,我附上了设置问题并显示解决方案的整个 sql 脚本文件。再次向 KM 致敬。

 use tempdb
go

if OBJECT_ID('dbo.Hierarchical') is not null
    drop table dbo.Hierarchical

create table dbo.Hierarchical
(
     colID          int         not null identity(1,1) primary key
    ,GroupName      varchar(5)  not null
    ,IsAtomic       bit         not null
    ,Constituent    varchar(5)  null
)

set nocount on
insert into dbo.Hierarchical values ('A',0,'B')
insert into dbo.Hierarchical values ('A',0,'C')
insert into dbo.Hierarchical values ('B',1,'B')
insert into dbo.Hierarchical values ('C',0,'K')
insert into dbo.Hierarchical values ('C',0,'L')
insert into dbo.Hierarchical values ('D',0,'E')
insert into dbo.Hierarchical values ('D',0,'F')
insert into dbo.Hierarchical values ('D',0,'G')
insert into dbo.Hierarchical values ('E',1,'E')
insert into dbo.Hierarchical values ('F',1,'F')
insert into dbo.Hierarchical values ('G',0,'H')
insert into dbo.Hierarchical values ('G',0,'I')
insert into dbo.Hierarchical values ('H',1,'H')
insert into dbo.Hierarchical values ('I',1,'I')
insert into dbo.Hierarchical values ('J',1,'J')
insert into dbo.Hierarchical values ('K',1,'K')
insert into dbo.Hierarchical values ('L',1,'L')
insert into dbo.Hierarchical values ('M',1,'M')
insert into dbo.Hierarchical values ('N',1,'N')
set nocount off

--  see what the over nomalized table looks like 
--  before you call the CTE. Notice how A has
--  Constiuents B, and C. And further down
--  C is made up of K, and L.

--  select * from dbo.Hierarchical

go


--  Use the CTE to 
;WITH HierarchicalTree AS 
( 
    SELECT  
        GroupName, Constituent,  1 AS LevelOf 
        FROM dbo.Hierarchical
        --WHERE GroupName=@Start 
    UNION ALL 
        SELECT  
            t.GroupName, h.Constituent, t.LevelOf+1 
        FROM HierarchicalTree         t 
            INNER JOIN dbo.Hierarchical  h ON t.Constituent=h.GroupName 
        WHERE h.Constituent!=h.GroupName AND h.IsAtomic=0 
) 


--  Now, notice this query will give us A with the it's 
--  Constiuent elements B, K, and L
SELECT 
    t.GroupName,t.Constituent, h.IsAtomic, t.LevelOf
    FROM HierarchicalTree        t 
        INNER JOIN  dbo.Hierarchical h  ON  t.Constituent=h.GroupName 
    --WHERE h.IsAtomic=1 
    Where h.Constituent = h.GroupName
order by 
    t.GroupName

if  OBJECT_ID('tempdb..Hierarchical') is not null
    drop table  dbo.Hierarchical

【讨论】:

以上是关于展平包含引用 SQL Server 2005 中其他行的行的表的主要内容,如果未能解决你的问题,请参考以下文章

在 sql server 2005 中添加外键

SQL Server 2005 事务复制无法发布包含索引创建的存储过程

SQL Server 2005:删除优化

使用存储过程检查字符串是不是包含 SQL Server 2005 中的子字符串

SQL Server 2005 中的复杂串联

在 SQL Server 2005 中截断数据库的所有表