2010-03-01 40 views
4

使用SQL Server 2008和TSQL的任何可用功能,我正在尝试解决如何在不涉及临时表的基于集合的解决方案存在以下问题:棘手的逻辑问题 - 如何为这个查询创建一个基于集合的解决方案?

给定一组节点有一个父 - 子关系和一组适用于每个关键值对的键值对,并且假设 在节点层次结构的更深层级上的值(对于给定键 - 值对)将覆盖具有相同值的值密钥 从祖先节点继承,选择:

  1. 全套适用于给定节点的键 - 值对
  2. 该节点

设定继承的值的模式如下:

create table Node 
(
    ID bigint identity primary key, 
    ParentID bigint null foreign key references Node(ID), 
    Name nvarchar(100) 
); 

create table KeyValuePair 
(
    ID bigint identity primary key, 
    KeyName nvarchar(100) not null, 
    Value nvarchar(1000) not null, 
    NodeID bigint not null foreign key references Node(ID), 

    unique (KeyName, NodeID) 
); 

结果集将基本上包括列KeyNameValueInheritedValue

我一直在尝试使用公用表表达式来做这件事,但它的逻辑有点棘手。

+1

你能澄清:你的意思是它继承的值吗?说,有问题的节点,它的父节点和它的祖父节点都有不同的值 - 它们中的哪一个在结果中被认为是InheritedValue? – VladV 2010-03-01 10:33:28

+0

几个例子的确会让我们更容易理解。我会建议添加一些插入语句和预期的输出,包括你的角落案例。 – 2010-03-01 10:43:56

+0

@VladV - 任何给定节点的值总是覆盖其祖先的值。因此,如果父母和祖父母都有一个给定的密钥的值,我想为父母,而不是祖父母的价值。如果家长没有钥匙的价值,那么我会采取祖父母的价值(假设祖父母有一个该钥匙的价值) – 2010-03-01 11:03:11

回答

3

我安装的节点上,KeyValuePair表按的问题,并与一些样本值填充,这样我的层次结构如下:

Root
| --- A
|       | --- A1
|       | --- A2
|
| ---乙
        | --- B1
        | --- B2

我分配两个属性,命名为 “属性1” 和 “属性2”,每个在根中分别用值“根支柱1”和“根支柱2”定义。在A中,我将“属性1”替换为“A属性1”的值,在B中,我将“属性2”替换为值“B属性2”。

set identity_insert Node on 
insert into Node(ID,ParentID,Name) 
values (1,null,'Root'),(2,1,'A'),(3,1,'B'),(4,2,'A1'),(5,2,'A2'), 
     (6,3,'B1'),(7,3,'B2') 
set identity_insert Node off 

insert into KeyValuePair(KeyName, [Value], NodeID) 
values ('Property 1','Root Prop 1',1), 
('Property 2','Root Prop 2',1), 
('Property 1','A Prop 1',2), 
('Property 2','B Prop 2',3) 

调用Nathan的节点A1解决方案不会产生行!

的其中Nathan的解决方案条款应的键和V之间的加入条件,导致如下所示的修订程序(也我已经改名DataValue到KeyValuePair要与原来的问题一致):

create procedure dbo.ListDataValues 
    @nodeid bigint 
as 
begin 
    with nodes as (
     select ID, ParentID, 0 as Level 
     from Node n where [email protected] 
     union all 
     select n.ID, n.ParentID, c.Level+1 as Level 
     from Node n inner join nodes c on c.ParentID = n.ID 
    ), 
    keys as (
     select distinct(KeyName) 
     from KeyValuePair 
     where NodeID in (select ID from nodes) 
    ) 
    select 
     keys.KeyName, 
     v.Value, 
     i.Value as [InheritedValue], 
     i.NodeID as [InheritedFromNodeID] 
    from 
     keys 
     left join KeyValuePair v on v.KeyName = keys.KeyName 
            and v.NodeID = @nodeid 
     left join KeyValuePair i on i.KeyName = keys.KeyName 
      and i.NodeID = (select top 1 NodeID from KeyValuePair d 
          inner join nodes k on k.ID = d.NodeID 
          where Level > 0 and d.KeyName = i.KeyName 
          order by [Level]) 
end 
go 

这得到正确的结果如预期:

KeyName  Value InheritedValue InheritedFromNodeID 
------------ ------- ----------------- -------------------- 
Property 1 NULL A Prop 1   2 
Property 2 NULL Root Prop 2  1 
2

您应该考虑使用嵌套集模型来存储您的层次结构。以下是描述它的链接: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

它支持更多的SQL友好方法来检索有关分层信息的公用信息。

然后,您可以通过Node上的查询和KeyValuePair的单个连接满足您的需求。

+0

有趣的方法;我会考虑更多地使用它,但它需要对现有应用程序进行太多更改,而现有应用程序已经有几个月的工作。它似乎也不能解决我的问题,或者至少,对于确定扁平化的键值对集合,逻辑不是更明显。 – 2010-03-01 10:24:41

+1

+1。一个很好的阅读,但它看起来像从复杂的选择切换到复杂的插入/更新/删除。 – 2010-03-01 10:24:58

+0

您几乎可以肯定地在存储过程中封装复杂的插入/更新/删除操作,以使它们更容易。如果您要更频繁地选择而不是更改数据,那么这种方法很好。 当然,如果你知道你有多少层级,那么你总是可以自己加入适当的次数。这在SQL方面让所有事情都相对容易。 – 2010-03-01 10:46:01

0

好的,我自己解决了。可能有其他的方法来做到这一点,但这似乎工作不够好:

create procedure dbo.ListDataValues 
    @nodeid bigint 
as 
begin 
    with nodes as (
     select ID, ParentID, 0 as Level 
     from Node n where [email protected] 
     union all 
     select n.ID, n.ParentID, c.Level+1 as Level 
     from Node n inner join nodes c on c.ParentID = n.ID 
    ), 
    keys as (
     select distinct(KeyName) 
     from DataValue 
     where NodeID in (select ID from nodes) 
    ) 
    select v.KeyName, v.Value, i.Value as [InheritedValue], i.NodeID as [InheritedFromNodeID] 
    from keys 
    left join DataValue v on v.KeyName = keys.KeyName 
    left join DataValue i on i.KeyName = keys.KeyName 
     and i.NodeID = (select top 1 NodeID from DataValue d 
         inner join nodes k on k.ID = d.NodeID 
         where Level > 0 and d.KeyName = i.KeyName 
         order by Level) 
    where v.NodeID = @nodeid 
end