I am trying to building a breadcrumb trail based on a parent/child relationship in a SQL database table. I have done these in the past, but what I need to do now is walk my way up through the hierarchy from the bottom up (backwards).
Here is what my DataTable looks like:
DocID | ParentDocID | Name
7 | 0 | Images (Root)
24 | 7 | JPG (Level 2)
34 | 24 | Size (Level 3)
So this is an N-tiered architecture. I always know what level I am at via a query string variable. I need to loop from the bottom up so that a user can bookmark what level in the tier structure they are at. I am having trouble figuring this out. I cannot use session or viewstate.
Desired Results:
User goes to this link:
Default.aspx?DocId=34 – Then they see the breadcrumb trail as such:
Home > Images > JPG > Size
Default.aspx?DocId=24 – Then they see the breadcrumb trail as such:
Home > Images > JPG
The “Home” element I have hard-coded. I also need each item in the trail to link except for the level you are on.
I would rather this be done in C# code. I also am open to SQL level Stored Procedure options.
Any help would be greatly appreciated. Thank-you in advance!
Here is some SQL I am trying but not getting the results I need. I hope this helps:
WITH Hierachy([DocID], [ParentDocID], [Name], [Level]
AS
(
SELECT [DocID], [ParentDocID], [Name], 0 AS [Level]
FROM [Documents] d
WHERE (d.[ParentDocID] = 7) AND ([Folder] = 1) AND ([Status] = 'A') AND ([AppGroupID] = 4)
UNION ALL
SELECT d.[DocID], d.[ParentDocID], d.[Name], dh.[Level] + 1
FROM [Documents] d
INNER JOIN Hierachy dh
--ON d.ParentDocID = dh.DocID
ON dh.[ParentDocID] = d.[DocID] -- bottom up apporach
WHERE ([Folder] = 1) AND ([Status] = 'A') AND ([AppGroupID] = 4)
)
SELECT [DocID], [ParentDocID], [Name]
FROM Hierachy
--WHERE [Level] > 0
I got it! Here is the SQL that works:
WITH Hierachy([DocID], [ParentDocID], [Name], [Level])
AS
(
SELECT [DocID], [ParentDocID], [Name], 0 AS [Level]
FROM [Documents] d
WHERE (d.[DocID] = #ParentDocID) AND ([Folder] = 1)
AND ([Status] = 'A') AND ([AppGroupID] = #AppGroupID)
UNION ALL
SELECT d.[DocID], d.[ParentDocID], d.[Name], dh.[Level] + 1
FROM [Documents] d
INNER JOIN Hierachy dh
ON dh.[ParentDocID] = d.[DocID]
WHERE ([Folder] = 1)
AND ([Status] = 'A') AND ([AppGroupID] = #AppGroupID)
)
SELECT [DocID], [ParentDocID], [Name]
FROM Hierachy
ORDER BY [Level] DESC
)
Sure you can. You can use CURSOR or WHILE LOOP or XML.
I have an example like you. Here's the WHILE LOOP with a temp Table, it selects parents of a node with a provided ID:
DECLARE #ID INT = 4
DECLARE #Parent_ID INT
DECLARE #Tree TABLE
(
ID int,
Value NVARCHAR(10),
Parent_ID int
)
SELECT #Parent_ID = Parent_ID FROM Tree
WHERE ID = #ID
WHILE #Parent_ID IS NOT NULL
BEGIN
INSERT INTO #Tree
SELECT * FROM Tree
WHERE ID = #Parent_ID
SELECT #Parent_ID = Parent_ID FROM Tree
WHERE ID = #Parent_ID
END
SELECT * FROM #Tree
This describes solving a similar problem, ie walking up a tree. Any inline sql, such as this is best executed within a stored procedure. Solving it in SQL is probably the best approach, as you already have all the data there.
Check below link you will get perfact answer for your question.
SQL Server: How to get all child records given a parent id in a self referencing table
Related
I am in need of updating a few rows in a database at the same time. Now the issue is that I will only know the FIRST row's unique ID but I will have X number of other rows after that that will need to be updated with some data as well. I will know how many loops I will need in order to update them - just need to know how to go about doing it.
My query looks like this:
UPDATE bLine
SET #val1
WHERE theGuid = #val2;
Now the data for #val1 looks like this:
ID | qty_SerialNum | qty_Location | qty_Property | theGUID
---+---------------+--------------+--------------+---------------------------
5 | 6845fg56 | Home | NA | a45vz-u300-2bd-4710j-vf09
6 | fk469fkh | Dock#4 | NA |
7 | geww2 | Dock#1 | Local |
...
Which when in a string to send over for the #val1:
#val1 = qty_SerialNum = '6845fg56,fk469fkh,geww2',
qty_Location = 'Home,Dock#4,Dock#1',
qty_Property = 'NA,NA,Local'
#val2 = theGUID = 'a45vz-u300-2bd-4710j-vf09'
So that's all fine since I know the GUID of the first row that needs updating but every row after that I wont know.
So the 1st update would look like out of 3:
UPDATE
bLine
SET
(qty_SerialNum = '6845fg56',qty_Location = 'Home',qty_Property = 'NA' )
WHERE
theGuid = 'a45vz-u300-2bd-4710j-vf09';
And this would be the 2nd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'fk469fkh',qty_Location = 'Dock#4',qty_Property = 'NA' )
WHERE
???? = ????;
And finally this would be the 3rd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'geww2',qty_Location = 'Dock#1',qty_Property = 'Local' )
WHERE
???? = ????;
So the question here is - how can I loop through the next X rows and update that information with the 2,3,... values in #val1 (skipping the first since its already saved via the theGUID)?
You can treat this as a gaps and islands type problem where you need to update and island row(row containing guid as well all gap rows till next island).
I am taking some assumption here and below is a working demo
create table bline (ID int, qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100), theGUID varchar(100))
insert into bline values
(5,'random','garbage','existing','a45vz-u300-2bd-4710j-vf09')
,(6,'data','random','garbage', NULL)
,(7,'existing','data','.', NULL);
create table #V (qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100))
insert into #V values
('6845fg56','Home','NA'),
('fk469fkh','Dock#4','NA'),
('geww2','Dock#1','Local')
;with map as
(
select id,
rn=row_number() over ( order by id asc)
from bline
where theGUID is NOT NULL
),
mappedBline as
(
select
b1.*,
rn
from
bline b1 join
(
select b.id,
rn=max(rn)
from bline b
join map m
on b.id >=m.id
group by b.id
)b2
on b2.id=b1.id
),
updateSet as
(
select
M.*,
updaterow=row_number() over( order by M.id)
from mappedBline M join
mappedBline M2
on M.rn=M2.rn
and M2.theGUID = 'a45vz-u300-2bd-4710j-vf09' --#val2
)
update U
set
qty_SerialNum=V.qty_SerialNum,
qty_Location= V.qty_Location,
qty_Property =V.qty_Property
from
updateSet U join
( select
*, updaterow =row_number() over (order by (select NULL))
from #V
)V
on U.updaterow=V.updaterow
select * from bline
You have a very arcane problem. In general, SQL updates are not for "adjancent" rows in this fashion. However, you can make it work by storing the values in a (virtual) table and joining the two sides together:
with v as (
select v.*
from (values (1, '6845fg56', 'Home', 'NA'),
(2, 'fk469fkh', 'Dock#4', 'NA'),
(3, 'geww2', 'Dock#1', 'Local')
) v(seqnum, qty_SerialNum, qty_Location, qty_Property)
)
update b
set qty_SerialNum = v.qty_SerialNum,
qty_Location = v.qty_Location,
qty_Property = v.qty_Property
from (select top(3) b.*,
row_number() over (order by id) as seqnum
from bline b
where id >= (select id from bline where theGUID = 'a45vz-u300-2bd-4710j-vf09'
order by id
) b join
v
on b.seqnum = v.seqnum;
I have below table
Source Destination Fare
A B 10
B C 5
B D 1
D C 1
A D 1
Now I wanted to write a query which will give me the minimum fare between two cities.
For example if I want to go A->C then minimum fare is 2 A->D->C
What will be MS-Sql query for this problem.
Try this:
;WITH Paths AS (
-- Anchor query: get first step
SELECT CAST(CONCAT(Source, '->', Destination) AS VARCHAR(MAX)) AS Path,
Destination, Fare,
IIF(Destination = 'C', 1, 0) AS Terminate
FROM mytable
WHERE Source = 'A'
UNION ALL
-- Recursive part: get next step
SELECT CAST(CONCAT(Path, '->', t.Destination) AS VARCHAR(MAX)) AS Path,
t.Destination, Fare = t.Fare + p.Fare,
IIF(t.Destination = 'C', 1, 0) AS Terminate
FROM mytable AS t
JOIN Paths AS p ON t.Source = p.Destination
WHERE p.Destination <> 'C'
)
SELECT Path, Fare
FROM (
SELECT Path, Fare,
RANK() OVER (ORDER BY Fare) AS rnk
FROM Paths
WHERE Terminate = 1) AS t
WHERE t.rnk = 1
This is sort of a brute force method: it uses a recursive CTE to get all possible paths. Then using RANK we can select the one(s) having the minimum fare.
Demo here
have you try to google:
sql server travelling salesman
or
sql server shortest path
There are tons of answers
I have a table valued function [dbo].GetValues() in my SQL Server database which is written in C#. This function returns a table of integers inside the text specified in the arguments. For example, [dbo].GetValues('232dasdg34vb3') will return following table:
| Ints |
| ---- |
| 232 |
| 34 |
| 3 |
Now I am trying to use this resultant table in a WHILE loop:
DECLARE #IntCount int = (SELECT COUNT(*) FROM [dbo].GetValues('232dasdg34vb3'))
WHILE(#IntCount > 0)
BEGIN
-- What to do here??
SET #IntCount = #IntCount - 1
END
So, is there any way I can access the rows one by one in this while loop using some index or row number?
Also, please note that I don't have access to the source code of GetValues().
UPDATE (Actual Problem)
There are three tables in database A1, A2 and A3. All of these tables has a column [ID] that is foreign key to other table in following way:
[A1].[ID] is connected to [A2].[A1ID]
[A2].[ID] is connected to [A3].[A2ID]
The text passed as argument to the function contains integers that are the [ID]s of A3 table. Now, I want the rows from A1 and A2 table using the [ID] of A3 table.
I now have all the options suggested by you people including cursors and joins. But which one is more optimized for this situation and how to do it?
EDIT:
select *
from A1
join A2 on [A1].[ID] = [A2].[A1ID]
join A3 on [A2].[ID] = [A3].[A2ID]
join [dbo].GetValues('232dasdg34vb3') V on A3.ID = v.Ints
You can use a cursor:
DECLARE #i INT
DECLARE cur CURSOR FAST_FORWARD READ_ONLY FOR
SELECT Ints FROM [dbo].GetValues('232dasdg34vb3')
OPEN cur
FETCH NEXT FROM cur INTO #i
WHILE ##FETCH_STATUS = 0
BEGIN
/* cursor logic -- #i will hold 232, then 34, then 3 */
FETCH NEXT FROM cur INTO #i
END
CLOSE cur
DEALLOCATE cur
If you have those IDs in another table you can just join on result of calling table valued function:
select * from SomeTable st
join [dbo].GetValues('232dasdg34vb3') ft on st.SomeID = ft.Ints
If you just need to select some records, a simple select can do the job:
DECLARE #Value VARCHAR(Max) = '232dasdg34vb3'
SELECT A1.Id, A2.Id
FROM A1
JOIN A2 ON A1.Id = A2.A1Id
JOIN A3 ON A2.Id = A3.A2Id
WHERE EXISTS (
SELECT 1
FROM [dbo].GetValues( #Value ) x
WHERE x.Ints = A3.Id
)
Don't use loops or cursors but set based approaches like:
SELECT x.Ints, ot.ID, ot.*
FROM OtherTable ot
WHERE ot.ID IN (SELECT x.Ints FROM [dbo].GetValues('232dasdg34vb3'))
I have the following table structure
| id | parentID | count1 |
2 -1 1
3 2 1
4 2 0
5 3 1
6 5 0
I increase count values from my source code, but i also need the increase in value to bubble up to each parent id row until the parent id is -1.
eg. If I were to increase count1 on row ID #6 by 1, row ID #5 would increase by 1, ID #3 would increase by 1, and ID #2 would increase by 1.
Rows also get deleted, and the opposite would need to happen, basically subtracting the row to be deleted' value from each parent.
Thanks in advance for your insight.
I'm using SQL Server 2008, and C# asp.net.
If you really want to just update counts, you could want to write stored procedure to do so:
create procedure usp_temp_update
(
#id int,
#value int = 1
)
as
begin
with cte as (
-- Take record
select t.id, t.parentid from temp as t where t.id = #id
union all
-- And all parents recursively
select t.id, t.parentid
from cte as c
inner join temp as t on t.id = c.parentid
)
update temp set
cnt = cnt + #value
where id in (select id from cte)
end
SQL FIDDLE EXAMPLE
So you could call it after you insert and delete rows. But if your count field are depends just on your table, I would suggest to make a triggers which will recalculate your values
You want to use a recursive CTE for this:
with cte as (
select id, id as parentid, 1 as level
from t
union all
select cte.id, t.parentid, cte.level + 1
from t join
cte
on t.id = cte.parentid
where cte.parentid <> -1
) --select parentid from cte where id = 6
update t
set count1 = count1 + 1
where id in (select parentid from cte where id = 6);
Here is the SQL Fiddle.
I have a table with the following structure
ID Name Parent
----------- -------------------------------------------------- -----------
1 Root NULL
2 Root_A 1
3 Root_B 1
4 Root_C 1
5 Root_C_A 4
6 Root_C_A_A 5
7 Root_C_A_A_A 6
So if I pass 7, I would like to get the following
Root --> Root_C --> Root_C_A --> Root_C_A_A
That means I want to traverse back to root from a child. How can I do it using SQL Server 2008 Stored Procedures or with other .Net techniques ?
I think I can accomplish the task using recursive function
You can implement recursion in Sql Server 2005+ using a common table expression. CTEs let you join to themselves in order to recurse. The CTE continues to recurse until no rows are returned, so you'll want to ensure you reliably meet that end condition.
declare #folders as table (id int, name nvarchar(20), parent int);
insert into #folders values(1, 'Root', null);
insert into #folders values(2, 'Root_A', 1);
insert into #folders values(3, 'Root_B', 1);
insert into #folders values(4, 'Root_C', 1);
insert into #folders values(5, 'Root_C_A', 4);
insert into #folders values(6, 'Root_C_A_A', 5);
insert into #folders values(7, 'Root_C_A_A_A', 6);
declare #folderID int;
set #folderID=7;
with Folders (id, name, parent, number) as
(
select ID, name, parent, 0 as number
from #folders
where id=#folderID
union all
select i.ID, i.Name, i.Parent, d.number + 1
from #folders as i
inner join Folders as d on d.Parent = i.ID
)
select id, name, number
from Folders
order by number desc;
try this:
declare #result varchar(100)
declare #id int
set #id = 7
select #result=Name,#id=Parent from temp where id=#id
while(exists(select 1 from temp where id=#id))
begin
select #id=Parent,#result=case #result when '' then Name else Name + '-->' end + #result from temp where id=#id
end
select #result