September 14, 2006 at 12:40 am
Not too sure if the subject of this post accurately reflects the nature of the question, but it is worth a shot:
I have a table with 2 columns, Old and New.
For example, the old and new columns are populated so:
old new
--- ---
x y
y z
a b
I am struggling to write a query which would return me the latest new value for an input of an old one.
Example: If I input 'x', it should return me 'z' and if i input 'z' it should return me 'z' as well. Along the same token, if i input 'y', i should get 'z' and for 'a' should get 'b'.
I was thinking whether I would need to use recursion or not?
Any help would be appreciated.
Thanks.
September 14, 2006 at 1:30 am
How do you define 'latest'? Do you have a date or timestamp somewhere in there or some other way to order the records?
If the table has
y a
x a
x y
a c
y z
z x
what should it return? for x? for y? for a?
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
September 14, 2006 at 1:38 am
There is no timestamp, unfortunately. Just the fact that the column name is New and Old defines the latest value. For your example, im afraid that is not a possible case since i am dealing with unique values for the old value. so the column old would infact be a key column(no duplicates)
But again, for your example, x and y are a bad case but fir a it would return c.
hope that makes sense.
September 14, 2006 at 1:49 am
Ok, a bit better.
so...
z a
x z
y x
if I ask x, I'll get a
if I ask y, I'll also get a
Is that right?
Is there any limit on how deep this nesting can go?
Are you using sql 2000, or sql 2005? (very easy in the latter with CTEs)
In SQL 2000, it'll probably be necessary to define the max possible depth.
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
September 14, 2006 at 2:15 am
Spot on there. That is exactly what I want.
I am using SQL 2000. On the limit, hard to say, but shouldn't go more than 30 times.
September 14, 2006 at 2:19 am
Hmmm, that's going to be one messy query.
Let me think about this....
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
September 14, 2006 at 4:48 am
I think this will work. I've only done it 3 levels deep, but the principle's there and you should be able to extend it to as many levels as you need.
DECLARE
@StartValue char(1)
SET @StartValue = 'x'
SELECT
DISTINCT COALESCE(Level3.NewValue, Level2.NewValue,Level1.NewValue,Nested.NewValue) FinalValue
FROM Nested
LEFT JOIN Nested Level1 ON Nested.NewValue=Level1.OldValue
LEFT JOIN Nested Level2 ON Level1.NewValue=Level2.OldValue
LEFT JOIN Nested Level3 ON Level2.NewValue=Level3.OldValue
WHERE Nested.OldValue = @StartValue
OR COALESCE(Level3.NewValue, Level2.NewValue, Level1.NewValue, Nested.NewValue) = @StartValue
My test table was as follows:
z a
x z
y x
b c
c d
a p
d e
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
September 14, 2006 at 4:54 am
Hmm...looks interesting. Join the table with itself.
Yeah, seems viable for this instance. Will check it out and see how it goes.
Thanks for your help mate.
Cheers.
September 14, 2006 at 5:13 am
Yes, and you have to join it as meny times as there are levels in the nesting.
Just a warning. It's not going to run fast. I'd recomend that you index both columns of the table.
For faster returns you can do the following, but then it won't pick up cases where the value passed is only in the new column
DECLARE
@StartValue char(1)
SET @StartValue = 'x'
SELECT
COALESCE(Level3.NewValue, Level2.NewValue,Level1.NewValue,Nested.NewValue) FinalValue
FROM Nested
LEFT JOIN Nested Level1 ON Nested.NewValue=Level1.OldValue
LEFT JOIN Nested Level2 ON Level1.NewValue=Level2.OldValue
LEFT JOIN Nested Level3 ON Level2.NewValue=Level3.OldValue
WHERE Nested.OldValue = @StartValue
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
September 14, 2006 at 5:20 am
Yeah, I was just thinking about the speed of the query. With so many joins, it would definitely affect the performance. But yeah, indexing should limit the damage.
When the value is only in the new column, guess will hv to do some sort of a count(1) where oldvalue = 'x', and can then decide whether to do the join, or not.
Should be interesting.
September 14, 2006 at 6:36 am
It's better to do it in function which will use a loop to reach the end of that chain.
Otherwise you can never guarantee that you are using enough of joins.
_____________
Code for TallyGenerator
September 14, 2006 at 3:33 pm
Have you ever thought what would happen if someone put a clustered index, then all ur assumption is blown away. This is very tricky cos u don't know which one is the "Current Record" and your current is relative.
Thanks
Sreejith
September 14, 2006 at 7:35 pm
Try something like this:
IF EXISTS (SELECT * FROM sysobjects WHERE name = N'Find_Newest')
DROP FUNCTION Find_Newest
GO
CREATE FUNCTION dbo.Find_Newest
(@OldOne nvarchar(20) )
RETURNS nvarchar(20)
AS
BEGIN
Declare @N nvarchar(20), @O nvarchar(20)
Declare @Time datetime
select @N = O.New
FROM YourTable O
WHERE O.Old = @OldOne
IF @@Rowcount = 0
RETURN @OldOne
WHILE @N IS NOT NULL
BEGIN
SET @O = @N
SET @N = (select New
FROM YourTable
WHERE Old = @N and New <> Old) -- in case chains are terminated by repeating old value
END
RETURN @O
END
GO
CREATE TABLE YourTable (
Old nvarchar(20),
New nvarchar(20)
 
INSERT INTO YourTable (Old, New)
SELECT 'a', 'b'
union
select 'x', 'y'
union
SELECT 'y', 'z'
GO
SELECT dbo.Find_Newest ('a'),
dbo.Find_Newest ('b'),
dbo.Find_Newest ('x'),
dbo.Find_Newest ('y'),
dbo.Find_Newest ('z')
GO
DROP TABLE YourTable
But be careful: cycles in your references may cause endless looping.
If it's possible you may need to use Table variable and check if this value has already appeared.
_____________
Code for TallyGenerator
September 14, 2006 at 7:43 pm
I hadn't thought about using a function to loop n times. But that is a good thought actually.
And it may be faster as well. Actually, I think it will definitely be faster.
I am still thinking if the structure of the table can be altered. That would help in the end. Like maybe putting a timestamp (which would make the whole thing extremely easy).
Trouble is getting permission to change the structure.
But the function is what I am going with for the moment. Lets see how the performance is.
Thanks
September 14, 2006 at 8:34 pm
Sure you do... "Old" = Parent, "New" = Child. It's a classic hierarchical problem. There has to be a dozen good articles and lot's of good posts on this forum on how to solve that problem.
--Jeff Moden
Change is inevitable... Change for the better is not.
Viewing 15 posts - 1 through 14 (of 14 total)
You must be logged in to reply to this topic. Login to reply