Showing posts with label broken. Show all posts
Showing posts with label broken. Show all posts

Monday, March 26, 2012

Parent Child transversing

I have a parent/child relationship in a relational database broken out like this:

Table Name: categories
[category_id] int (primary_key NOT NULL),
[category_name] varchar(50),
[parent_fk] int

The parent references the category_id in the same table to create the parent/child relationships. I can get all the bottom level categories by doing this:

select category_id, category, parent_fk
from categories
where
category_id not in (
select parent_fk from categories
)

Each bottom-level category has a count attached to it.

The problem I have is getting the counts rolled up for each parent of the bottom level. A parent could/will have multiple bottom-level categories (and counts).

My sql is a little weak, could you help me out? I can utilize everying in SQL 2000 (stored proc, UDF, anything).

Thanks!

Attached is a diagram that might help:
http://www.cnsar.com/images/diagram.gif
|||

This procedure (below) is based on the following table layout:

CREATE TABLE categories (
category_id int,
desc_name varchar(35),
parent_fk int
)
-----------------------
CREATE PROCEDURE GetChildren
@.category_idint --pass in the parent.id in which you want to get the children
AS

SET NOCOUNT ON
CREATE TABLE #temp_children1(category_idintNOT NULL)
CREATE TABLE #temp_children2(category_idintNOT NULL)
CREATE TABLE #temp_allchildren(category_idintNOT NULL)

INSERT INTO #temp_children1VALUES(@.category_id)-- Parent
WHILEEXISTS(SELECT *FROM #temp_children1)
BEGIN
DELETE FROM #temp_children2
-- Save current level children
INSERT INTO #temp_children2
SELECT c.category_id
FROM categories cJOIN #temp_children1 t
ON c.parent_fk = t.category_id
WHERE c.parent_fk <> c.category_id

DELETE FROM #temp_children1
INSERT INTO #temp_children1
SELECT *FROM #temp_children2
-- Add to the list of all children

INSERT INTO #temp_allchildren
SELECT *FROM #temp_children2

END

--return the children ids
select category_id
from #temp_allchildren

--cleanup
DROP TABLE #temp_children1
DROP TABLE #temp_children2
DROP TABLE #temp_allchildren

GO

|||

Based on your diagram, I made the assumption that the count you want is the number of images that are associated with each category. If you just wanted the number of categories that are subchildren, you'll need to change the function a little bit, returning 1 in the first part of the if and returning SUM(dbo.ChildrenImageCount(CategoryID))+1 in the second part of the if in the ChildrenImageCount function (The commented out lines). It will probably be one more than you are expecting because it counts itself as well, but a small price to pay for ease of use. If you want the count of end-nodes, remove the +1.

Just an FYI -- Your root node should have a ParentFK of NULL, otherwise it'd be it's own parent. If you must continue to use 1, don't use the Foreign key constraint (It won't work -- I don't think), and don't ever select the count for the root node (It will loop forever until it blows up) -- unless of course you change it so it won't do that ;-)

GO
/****** Object: Table [dbo].[Categories] Script Date: 01/20/2006 01:46:49 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO

CREATETABLE [dbo].[Categories](

[CategoryID] [int]IDENTITY(1,1)NOTNULL,

[ParentFK] [int]NULL,

[Name] [varchar](50)COLLATE SQL_Latin1_General_CP1_CI_ASNOTNULL,

[ImageCount]AS([dbo].[ChildrenImageCount]([CategoryID])),

CONSTRAINT [PK_Categories]PRIMARYKEYCLUSTERED

(

[CategoryID]ASC

)WITH(IGNORE_DUP_KEY=OFF)ON [PRIMARY]

)ON [PRIMARY]

GO

SETANSI_PADDINGOFF

GO

ALTERTABLE [dbo].[Categories]WITHCHECKADDCONSTRAINT [FK_Categories_Categories]FOREIGNKEY([ParentFK])

REFERENCES [dbo].[Categories]([CategoryID])

GO
/****** Object: Table [dbo].[Images] Script Date: 01/20/2006 01:42:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Images](
[ImageID] [int] IDENTITY(1,1) NOT NULL,
[CategoryID] [int] NOT NULL,
[Image] [image] NULL,
[ImageMime] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
SET ANSI_PADDING OFF

GO
/****** Object: UserDefinedFunction [dbo].[ChildrenImageCount] Script Date: 01/20/2006 01:42:30 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[ChildrenImageCount]
(
-- Add the parameters for the function here
@.CategoryID int
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE @.Result int

IF NOT EXISTS(SELECT CategoryID FROM Categories WHEREParentFK=@.CategoryID)
BEGIN
SELECT @.Result=COUNT(*) FROM Images WHERECategoryID=@.CategoryID

-- SELECT @.Result=1

END
ELSE
BEGIN
-- Add the T-SQL statements to compute the return value here
SELECT @.Result=SUM(dbo.ChildrenImageCount(CategoryID)) FROM Categories WHEREParentFK=@.CategoryID

-- SELECT @.Result=SUM(dbo.ChildrenImageCount(CategoryID))+1 FROM Categories WHEREParentFK=@.CategoryID

END

-- Return the result of the function
RETURN @.Result

END

Once you make those changes, you can just select ImageCount from the categories table for any record and it will have the right answer for you.

Wednesday, March 21, 2012

Partitioned view broken after moving table to new filegroup

I am using SQL Server 2000, SP3.

I created an updatable partitioned view awhile ago and it has been
running smoothly for some time. The partition is on a DATETIME column
and it is partitioned by month. Each month a stored procedure is
scheduled that creates the new month's table, and alters the view to
include it. Again... working like a charm for quite some time.

This past weekend I moved some of the first tables onto a new file
group. I did this through Enterprise Manager, by going into design
mode for the table, then going into the properties for the table and
changing the file group there as well as in all of the indexes. Now
the partitioned view is no longer updatable. It gives the error
message: "UNION ALL view '<view name>' is not updatable because a
partitioning column was not found."

I have extracted the DDL for all of the partition tables and compared
them and they all look the same. I checked and then double-checked the
CHECK constraints to make sure that they were all valid and they are.
If I remove the tables that I moved to the new file group from the
view, then it is once again updatable, but when I put them back in it
fails again.

Any ideas? If you would like samples of the code then I can send it
along, but it's rather large, so I have not included it here.

Thanks!
Thomas R. HummelThomas R. Hummel (tom_hummel@.hotmail.com) writes:
> I created an updatable partitioned view awhile ago and it has been
> running smoothly for some time. The partition is on a DATETIME column
> and it is partitioned by month. Each month a stored procedure is
> scheduled that creates the new month's table, and alters the view to
> include it. Again... working like a charm for quite some time.
> This past weekend I moved some of the first tables onto a new file
> group. I did this through Enterprise Manager, by going into design
> mode for the table, then going into the properties for the table and
> changing the file group there as well as in all of the indexes. Now
> the partitioned view is no longer updatable. It gives the error
> message: "UNION ALL view '<view name>' is not updatable because a
> partitioning column was not found."
> I have extracted the DDL for all of the partition tables and compared
> them and they all look the same. I checked and then double-checked the
> CHECK constraints to make sure that they were all valid and they are.
> If I remove the tables that I moved to the new file group from the
> view, then it is once again updatable, but when I put them back in it
> fails again.
> Any ideas? If you would like samples of the code then I can send it
> along, but it's rather large, so I have not included it here.

Without sample code, only wild guesses are possible.

But one possbility is that as part of the process the CHECK constraints
were disabled and then enabled without WITH CHECK. Then SQL Server does not
trust those constraints.

Try do:

ALTER TABLE tbl NOCHECK CONSTRAINT your_constraint
ALTER TABLE tbl WITH CHECK CHECK CONSTRAINT your_constraint

Moral: don't use Enterprise Manager, if you don't know what it does
behind your back.

--
Erland Sommarskog, SQL Server MVP, esquel@.sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techin.../2000/books.asp|||Thanks Erland! That was exactly the problem.

As a related followup... if I know that the data in the table
satisfies the constraint, can I make a change to sysobjects to set the
status for the check constraint object to "2"? I understand all of the
usual warnings about making changes to system tables. Just wondering
if it tracks the check constraint information anywhere else.

Since the column was part of the clustered primary key doing the check
on the constraint took less than 1 minute for a table of over 20M
rows, but it might be a useful trick if the check would take longer
than that.

Thanks again,
-Tom.

Erland Sommarskog <esquel@.sommarskog.se> wrote in message news:<Xns9590ED2AA1AC6Yazorman@.127.0.0.1>...

> But one possbility is that as part of the process the CHECK constraints
> were disabled and then enabled without WITH CHECK. Then SQL Server does not
> trust those constraints.
> Try do:
> ALTER TABLE tbl NOCHECK CONSTRAINT your_constraint
> ALTER TABLE tbl WITH CHECK CHECK CONSTRAINT your_constraint
> Moral: don't use Enterprise Manager, if you don't know what it does
> behind your back.|||Thomas R. Hummel (tom_hummel@.hotmail.com) writes:
> As a related followup... if I know that the data in the table
> satisfies the constraint, can I make a change to sysobjects to set the
> status for the check constraint object to "2"? I understand all of the
> usual warnings about making changes to system tables. Just wondering
> if it tracks the check constraint information anywhere else.

I for one would never try to do such a thing. It might work, but I don't
like unpleasant surprises later on.

> Since the column was part of the clustered primary key doing the check
> on the constraint took less than 1 minute for a table of over 20M
> rows, but it might be a useful trick if the check would take longer
> than that.

The clustered index does not have much to do it, but the non-clustered
indexes on the table were. Since the clustered index columns act as
row locator in the NC indexes, SQL Server could take a shortcut and scan
the smallest NC index, rather than the whole table.

--
Erland Sommarskog, SQL Server MVP, esquel@.sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techin.../2000/books.asp