Question

I'm a bit of a SQL novice, so I could definitely use some assistance hashing out the general design of a particular query. I'll be giving a SQL example of what I'm trying to do below. It may contain some syntax errors, and I do apologize for that- I'm just trying to get the design down before I go running and testing it!

Side note- I have 0 control over the design scheme, so redesign is not an option. My example tables may have an error due to oversight on my part, but the overall design scheme of bottom-up value searching will remain the same. I'm querying an existing database filled with tons of data already in it.

The scenario is this: There is a tree of elements. Each element has an ID and a parent ID (table layouts below). Parent ID is a recursive foreign key to itself. There is a second table that contains values. Each value has an elementID that is a foreign key to the element table. So to get the value of a particular variable for a particular element, you must join the two tables.

The variable hierarchy goes Bottom-Up by way of inheritance. If you have an element and want to get its variable value, you first look at that element. If it doesn't have a value, then check the element's parent. If that doesn't check the parent's parent- all the way to the top. Every variable is guaranteed to have a value by the time you reach the top! (if I search for variableID 21- I know that 21 will exist. If not at the bottom, then definitely at the top) The lowest element on the tree gets priority, though- if the bottom element has a value for that variable, don't go any farther up!

The tables would look roughly like this:

Element_Table
--------------
elementID (PK)
ParentID (FK to elementID)

Value_Table 
--------------
valueID (PK)
variableID
value (the value that we're looking for)
elementID (FK to Element_Table.elementID)

So, what I'm looking to do is create a function that cleanly (key word here. Nice, clean and efficient code) search, bottom-up, across the tree looking for a variable value. Once I find it- return that value and move on!

Here is an example of what I'm thinking:

CREATE FUNCTION FindValueInTreeBottomUp
(@variableID int, @element varchar(50))
RETURNS varchar(50) 
AS
BEGIN
DECLARE @result varchar(50)
DECLARE @ID int
DECLARE @parentID int

SET @result = NULL, @ID = @element
WHILE (@result IS NULL)
    BEGIN
       SELECT @result = vals.value, @parentID = eles.ParentID
       FROM Value_Table vals 
       JOIN Element_Table eles 
       ON vals.elementID = eles.elementID
  WHERE eles.elementID = @ID AND vals.variableID = @variableID
  IF(@result IS NULL)
      @ID = @parentID
      CONTINUE
  ELSE
      BREAK
END

RETURN @result
END

Again, I apologize if there are any syntactical errors. Still a SQL novice and haven't run this yet! I'm especially a novice at functions- I can query all day, but functions/sprocs are still rather new to me.

So, SQL gurus out there- can you think of a better way to do this? The design of the tables won't be changing; I have NO control over that. All I can do is produce the query to check the already existing design.

Was it helpful?

Solution

I think you could do something like this (it's untested, have to try it in sql fiddle):

;with cte1 as (
    select e.elementID, e.parentID, v.value
    from Element_Table as e
        left outer join Value_Table as v on e.elementID = e.elementID and v.variableID = @variableID
), cte2 as (
    select v.value, v.parentID, 1 as aDepth
    from cte1 as v
    where v.elementID = @elementID
    union all
    select v.value, v.parentID, c.aDepth + 1
    from cte2 as c
    inner join cte1 as v on v.elementID = c.ParentID
    where c.value is null
)
select top 1 value
from cte2
where value is not null
order by aDepth

test infrastructure:

declare @Elements table (ElementID int, ParentID int)
declare @Values table (VariableID int, ElementID int, Value nvarchar(128))
declare @variableID int, @elementID int

select @variableID = 1, @elementID = 2

insert into @Elements
select 1, null union all
select 2, 1

insert into @Values
select 1, 1, 'test'

;with cte1 as (
    select e.elementID, e.parentID, v.value
    from @Elements as e
        left outer join @Values as v on e.elementID = e.elementID and v.variableID = @variableID
), cte2 as (
    select v.value, v.parentID, 1 as aDepth
    from cte1 as v
    where v.elementID = @elementID
    union all
    select v.value, v.parentID, c.aDepth + 1
    from cte2 as c
        inner join cte1 as v on v.elementID = c.ParentID
    where c.value is null
)
select top 1 value
from cte2
where value is not null
order by aDepth
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top