2010-03-01 22 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 
相關問題