棘手的逻辑问题 - 如何为此查询创建基于集合的解决方案?

发布于 2024-08-23 03:47:26 字数 765 浏览 10 评论 0原文

使用 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)
);

结果集本质上包括列 KeyName< /code>、InheritedValue

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

Using SQL Server 2008 and any available features of TSQL, I am trying to work out how to if a set-based solution that does not involve a temp table exists for the following problem:

Given a set of nodes that have a parent-child relationship, and a set of key-value pairs that apply to each, and given that
the value (for a given key-value pair) at a deeper level of the node hierarchy will override a value with the same key that
is inherited from an ancestor node, select:

  1. the full set of key-value pairs that apply to a given node
  2. the set of inherited values for that node

The schema is as follows:

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)
);

The result set would essentially include the columns KeyName, Value, InheritedValue.

I've been trying to do this using a common table expression but the logic of it is a bit tricky.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尝蛊 2024-08-30 03:47:26

我根据问题设置了 Node 和 KeyValuePair 表,并填充了一些示例值,这样我的层次结构如下:

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

我分配了两个属性,名为“Property 1”和“Property 2”,每个属性在根中定义,分别具有值“Root Prop 1”和“Root Prop 2” 。在 A 中,我用值“A Prop 1”覆盖“属性 1”,在 B 中,我用值“B Prop 2”覆盖“属性 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)

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

Nathan 解决方案中的 where 子句应该是键和 v 之间连接的条件,导致修改后的过程如下所示(我还将 DataValue 重命名为 KeyValuePair 以与原始问题一致):

create procedure dbo.ListDataValues
    @nodeid bigint
as
begin
    with nodes as (
        select ID, ParentID, 0 as Level
        from Node n where ID=@nodeid
        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

I setup the Node and KeyValuePair tables as per the question, and populated with some sample values, such that my hierarchy was as follows:

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

I assigned two properties, named "Property 1" and "Property 2", each defined in root with values "Root Prop 1" and "Root Prop 2" respectively. In A, I overrode "Property 1" with value "A Prop 1" and in B, I overrode "Property 2" with value "B Prop 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)

Calling Nathan's solution for node A1 yields no rows!

The where clause in Nathan's solution should be a condition of the join between keys and v, resulting in the revised procedure shown below (also I've renamed DataValue to KeyValuePair to be consistent with the original question):

create procedure dbo.ListDataValues
    @nodeid bigint
as
begin
    with nodes as (
        select ID, ParentID, 0 as Level
        from Node n where ID=@nodeid
        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

This yielded the correct results as expected:

KeyName      Value   InheritedValue    InheritedFromNodeID
------------ ------- ----------------- --------------------
Property 1   NULL    A Prop 1          2
Property 2   NULL    Root Prop 2       1
清秋悲枫 2024-08-30 03:47:26

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

它支持一种更加 SQL 友好的方法来检索有关分层信息的公共信息。

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

You should consider using the nested set model to store your hierarchy. Here is a link that describes it:
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

It supports a much more SQL friendly approach to retrieving common information about hierarchical information.

Your requirement here could then be satisfied with a query on Node and a single join to KeyValuePair.

琉璃繁缕 2024-08-30 03:47:26

好吧,我自己解决了。可能还有其他方法可以做到这一点,但这似乎效果很好:

create procedure dbo.ListDataValues
    @nodeid bigint
as
begin
    with nodes as (
        select ID, ParentID, 0 as Level
        from Node n where ID=@nodeid
        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

Ok, I've solved it myself. There are probably other ways to do it, but this seems to work well enough:

create procedure dbo.ListDataValues
    @nodeid bigint
as
begin
    with nodes as (
        select ID, ParentID, 0 as Level
        from Node n where ID=@nodeid
        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
马蹄踏│碎落叶 2024-08-30 03:47:26

这是物化路径大放异彩的案例之一。以下是我如何使用它解决类似问题:

将您的配置设置作为层次结构存储在数据库中。

This is one of those cases when materialized path shines. Here is how I solved a similar problem using it:

Store your configuration settings as a hierarchy in a database.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文