Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 CTE - updating records

Author  Topic 

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 11:38:43
Hi

I'm trying to understand why I get this error when trying to update the records returned by a Common Table expression :
Derived table 'MB' is not updatable because a column of the derived table is derived or constant.

Basically, this is part of the code which will update all the child sections of a root menu item if the section level of the root item is modified.

Here is my code :
WITH MenuBranch (Id, [Name], Parent, [Level], [Root]) AS
(
SELECT
section_id, name_en, parent_section, section_level, section_id
FROM
sections
WHERE
parent_section = 0
UNION ALL
SELECT
sec.section_id, sec.name_en, sec.parent_section, sec.section_level, [Root]
FROM
sections sec
INNER JOIN MenuBranch ON
sec.parent_section = MenuBranch.Id
)
UPDATE MB
SET [Level] = [Level] + @level_delta
FROM (SELECT * FROM MenuBranch WHERE [Root] = @section_id) AS MB;


Could anyone please point me in the right direction ? I guess it's the [Root] field in the second select that is posing a problem, but is there an alternative ? Is there an error in my code, or should I place the results in a temp table or use a cursor and the original table instead to execute my updates, and use the CTE only to get the id's of the child sections ?

Thank you

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 11:41:39
can i ask purpose of updating the level of cte outside? shouldnt that be done in recursive part itself? the error happens because cte has a derived field
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 11:43:52
Thanks for the reply. I'm sorry, this is the first time I've used a CTE. How would I perform the update inside the recursive part. Could you please provide an example ?
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 11:46:32
i will do that..but before that can you tell purpose of doing this?
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 11:50:20
I have a table to manage a menu structure for a website.
SECTION_ID
SECTION_LEVEL
PARENT_SECTION
etc.

I am building a content management system to manage the menu structure.
If a person should update the hierarchy of a menu entry, I want all child menus to follow.

Ex.
Jobs
About Us
CF101
-Elements
-- Army
-- Navy
-- Air Force

If I move Elements from under CF101 and move it to be under About Us, I want the Army, Navy and AirForce submenus to follow the Elements menu. Therefore, all their section_level fields must be updated at the same time.

I hope this is a clear explanation.

Thank you
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 11:58:48
yup..for that you dont need this update you just need to update the Elements parent id alone.
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 12:01:11
I'm sorry, I don't understand. Where would I do my update ? Could you post an example please ? Thank you.
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 12:07:45
what you need is just to pass id of node under which you want to move Elements and also its id and set its parent as the former id.
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 12:22:20
But how ? That's what I'm trying to do with the above code. Please post a code example. Thank you
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 12:31:13
how you decide where you want to move from front end? can you explain the user action part?
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 12:40:38
Select parent of section from a dropdown list. I can email you a screenshot if you want.
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 12:44:57
ok..so what you need is to pass the id of selected item from dropdown and use a sp like

CREATE PROC changeparent
@New_Parent_ID int,
@Item_ID int

AS
UPDATE table
SET PARENT_SECTION=@New_Parent_ID
WHERE SECTION_ID=@Item_ID
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 13:02:23
I know how to do all that. My initial question was regarding the CTE. What the CTE does is return the id's of all the child sections of the section which I am updating the parent for. Since I am updating the parent, all it's children must have their section level adjusted if the new parent is higher up or lower down. The CTE returns all the children IDs. I was hoping to execute an update statement on each of the children as stated in my code above. But I get the error (not updatable). I want to know if I can update a CTE, or if I must use the CTE results in a cursor to run an update on the original table. Here is my full SP for your understanding.


ALTER PROCEDURE [dbo].[usp_updatesection]
@section_id INT,
@parent_section INT,
@name_en NVARCHAR(255),
@name_fr NVARCHAR(255),
@display_order INT,
@active BIT,
@link_en NVARCHAR(255),
@link_fr NVARCHAR(255),
@new_page BIT,
@parent_changed BIT
AS
BEGIN
DECLARE @section_level AS INT
DECLARE @old_section_level as INT
DECLARE @new_section_level as INT
DECLARE @level_delta as INT

IF @parent_changed = 0
BEGIN
IF @parent_section = 0
BEGIN
SET @section_level = 1;
END
ELSE
BEGIN
SELECT @section_level = section_level + 1
FROM sections
WHERE section_id = @parent_section;
END

BEGIN
UPDATE sections
SET section_level = @section_level
,parent_section = @parent_section
,name_en = @name_en
,name_fr = @name_fr
,display_order = @display_order
,active = @active
,link_en = @link_en
,link_fr = @link_fr
,new_page = @new_page
WHERE section_id = @section_id;
END
END
ELSE
BEGIN
-- determine section_level delta with new parent
SELECT @old_section_level = section_level
FROM sections
WHERE section_id = @section_id;

SELECT @new_section_level = section_level + 1
FROM sections
WHERE section_id = @parent_section;

SET @level_delta = @new_section_level - @old_section_level;

-- find all child sections of this section (recursively for all children)
-- update section_level of all children (add delta to current section_level)
-- CTE (Common Table Expression : http://blogs.msdn.com/simonince/archive/2007/10/17/hierarchies-with-common-table-expressions.aspx
WITH MenuBranch (Id, [Name], Parent, [Level], [Root]) AS
(
SELECT
section_id, name_en, parent_section, section_level, section_id
FROM
sections
WHERE
parent_section = 0
UNION ALL
SELECT
sec.section_id, sec.name_en, sec.parent_section, section_level, [Root]
FROM
sections sec
INNER JOIN MenuBranch ON
sec.parent_section = MenuBranch.Id
)
UPDATE MB
SET [Level] = [Level] + @level_delta
FROM (SELECT * FROM MenuBranch WHERE [Root] = @section_id) AS MB;

-- update info of this section
IF @parent_section = 0
BEGIN
SET @section_level = 1
END
ELSE
BEGIN
SELECT @section_level = section_level + 1
FROM sections
WHERE section_id = @parent_section
END

BEGIN
UPDATE sections
SET section_level = @section_level
,parent_section = @parent_section
,name_en = @name_en
,name_fr = @name_fr
,display_order = @display_order
,active = @active
,link_en = @link_en
,link_fr = @link_fr
,new_page = @new_page
WHERE section_id = @section_id
END
END

END
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2009-11-09 : 13:05:22
you need update original table based on cte recursion to get child nodes
Go to Top of Page

crichard1983
Starting Member

11 Posts

Posted - 2009-11-09 : 13:07:15
Ok. I've finally figured it out. Here is the portion of code :


WITH MenuBranch (Id, [Name], Parent, [Level], [Root]) AS
(
SELECT
section_id, name_en, parent_section, section_level, section_id
FROM
sections
WHERE
parent_section = 0
UNION ALL
SELECT
sec.section_id, sec.name_en, sec.parent_section, section_level, [Root]
FROM
sections sec
INNER JOIN MenuBranch ON
sec.parent_section = MenuBranch.Id
)
UPDATE sections
SET section_level = section_level + @level_delta
WHERE section_id IN (SELECT section_id FROM MenuBranch WHERE [Root] = @section_id);
Go to Top of Page
   

- Advertisement -