May 16, 2010 at 4:13 pm
@paul-2 White,
Hey Paul... just so you don't think I'm blowing smoke (I know you don't but it makes me feel better to prove I'm not) or have lost my mind (heh... no help there :-P) by publishing "hear say" about performance problems with the HierarchyID, here's the one post that really got my attention and the reason why I dared say such a thing without first proving it myself...
Of particular import is where MS came back with the following...
We were able to track down the issue. [font="Arial Black"]The problem is that CLR calls, including hierarchyID's methods, are opaque to the query optimizer. This is by design. However, it means that the cardinality estimate for them can sometimes be quite wrong.[/font]
It happens that your EXISTS queries (the >0 query is transformed into an EXISTS) cause the query optimizer to choose a plan based on such an incorrect cardinality estimate. This causes the poor performance.
For now, the easiest workaround is to use a hint to force the use of the correct index. However, we will look into improving the cardinality estimation for hierarchyID methods to help address this sort of issue in the future.
Of course, the emphasis in the text is all mine. I imagine it's something to watch out for with all SQLCLR. It shouldn't scare anyone off of SQLCLR... they just need to be aware of what can happen.
And, as a sidebar, look what the MS respondent called it... "CLR". 😉
--Jeff Moden
Change is inevitable... Change for the better is not.
May 20, 2010 at 6:01 pm
Joe Celko (5/20/2010)
Let's make a simple cycle:INSERT INTO AdjTree VALUES ('foobar', 1, 1);
Damn. I knew I was forgetting something. Thanks, Joe.
We also need to compel a root with one or more children:
VALUES ('Root', 1, NULL);
Agreed. Then there's the additional problem that there's nothing to prevent the user from shooting himself in the face on a DAG by deleting that row prior to inserting additional rows unless we include a DELETE trigger.
Then, there's always the problem of them adding a new row with a NULL ParentID which is the start of a forest possibly requiring a bit more code in the form of a trigger to prevent such an eventuality.
Heh... as you said, it does get messy.
Of course, you'd have to do some checking with both the Hierarchical Path and Nested Set methods to make sure someone didn't manually enter some bum "coordinates", as well.
Thanks for the feedback, Joe.
--Jeff Moden
Change is inevitable... Change for the better is not.
May 21, 2010 at 1:13 am
Jeff Moden (5/16/2010)
@Paul White,
Yep. Same issue as for scalar/multi-statement TVFs, many remote operations, table variables, XML methods and so on: no statistics, and Microsoft don't (currently) provide a way for programmers to indicate cardinality effects so the optimiser has to guess.
As they say, this is by design, so I don't see it as unexpected or particularly problematic - that's what hints are for. Using an index hint would be my last choice here, using TOP, FAST n, OPTIMIZE FOR or FORCESEEK for example would be preferable, at least as I see it.
As far as naming is concerned, I'm happy with CLR, SQLCLR, .NET hosting...anything really so long as the meaning is clear. Thanks for the link!
Paul
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
July 16, 2010 at 7:06 am
please give this script in sql server 2005
thank you..
July 16, 2010 at 7:20 am
Vaghasiya Nilesh (7/16/2010)
please give this script in sql server 2005thank you..
Which script?
P.S. I never respond to unsolicited emails
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
July 16, 2010 at 7:42 am
How to count left side nodes and right side nodes for managing a chain link(binary tree)application
in already u posted script in sql server 2008 but i want need in sql server 2005 bcz of Hierarcyid datatype is not in sql server 2005
thank u..
July 16, 2010 at 7:48 am
USE tempdb;
GO
IF OBJECT_ID(N'dbo.Tree', N'U')
IS NOT NULL
DROP TABLE dbo.Tree;
GO
-- Test table
CREATE TABLE dbo.Tree
(
node_id INTEGER NOT NULL,
name VARCHAR(20) NOT NULL,
hid HIERARCHYID NOT NULL,
parent AS hid.GetAncestor(1) PERSISTED,
level AS hid.GetLevel(),
path AS hid.ToString(),
);
-- Indexes and constraints
CREATE UNIQUE CLUSTERED INDEX [CUQ dbo.Tree depth_first] ON dbo.Tree (hid);
CREATE UNIQUE INDEX [UQ dbo.Tree node_id] ON dbo.Tree (node_id);
CREATE INDEX [IX dbo.Tree parent] ON dbo.Tree (parent);
ALTER TABLE dbo.Tree ADD FOREIGN KEY (parent) REFERENCES dbo.Tree (hid);
-- Nodes
INSERT dbo.Tree(node_id, name, hid) VALUES (1, 'Node 1', hierarchyid::GetRoot());
INSERT dbo.Tree(node_id, name, hid) VALUES (2, 'Node 2', hierarchyid::Parse(N'/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (3, 'Node 3', hierarchyid::Parse(N'/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (4, 'Node 4', hierarchyid::Parse(N'/1/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (5, 'Node 5', hierarchyid::Parse(N'/1/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (6, 'Node 6', hierarchyid::Parse(N'/2/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (7, 'Node 7', hierarchyid::Parse(N'/2/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (8, 'Node 8', hierarchyid::Parse(N'/1/1/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (9, 'Node 9', hierarchyid::Parse(N'/1/1/2/'));
Solution:
DECLARE @root HIERARCHYID,
@left HIERARCHYID,
@right HIERARCHYID;
SELECT @root = hid FROM dbo.Tree WHERE node_id = 1;
SELECT @left = (SELECT MIN(hid) FROM dbo.Tree WHERE hid.GetAncestor(1) = @root),
@right = (SELECT MAX(hid) FROM dbo.Tree WHERE hid.GetAncestor(1) = @root);
SELECT left_count =
(
SELECT COUNT_BIG(*)
FROM dbo.Tree T
WHERE T.hid.IsDescendantOf(@left) = 1
),
right_count =
(
SELECT COUNT_BIG(*)
FROM dbo.Tree T
WHERE T.hid.IsDescendantOf(@right) = 1
);
GO
-- Tidy up
DROP TABLE dbo.Tree;
this script is give me in sql server 2005..
thank u..
July 16, 2010 at 8:04 am
USE tempdb;
GO
IF OBJECT_ID(N'dbo.Tree', N'U')
IS NOT NULL
DROP TABLE dbo.Tree;
GO
-- Test table
CREATE TABLE dbo.Tree
(
node_id INTEGER NOT NULL,
name VARCHAR(20) NOT NULL,
hid HIERARCHYID NOT NULL,
parent AS hid.GetAncestor(1) PERSISTED,
level AS hid.GetLevel(),
path AS hid.ToString(),
);
-- Indexes and constraints
CREATE UNIQUE CLUSTERED INDEX [CUQ dbo.Tree depth_first] ON dbo.Tree (hid);
CREATE UNIQUE INDEX [UQ dbo.Tree node_id] ON dbo.Tree (node_id);
CREATE INDEX [IX dbo.Tree parent] ON dbo.Tree (parent);
ALTER TABLE dbo.Tree ADD FOREIGN KEY (parent) REFERENCES dbo.Tree (hid);
-- Nodes
INSERT dbo.Tree(node_id, name, hid) VALUES (1, 'Node 1', hierarchyid::GetRoot());
INSERT dbo.Tree(node_id, name, hid) VALUES (2, 'Node 2', hierarchyid::Parse(N'/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (3, 'Node 3', hierarchyid::Parse(N'/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (4, 'Node 4', hierarchyid::Parse(N'/1/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (5, 'Node 5', hierarchyid::Parse(N'/1/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (6, 'Node 6', hierarchyid::Parse(N'/2/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (7, 'Node 7', hierarchyid::Parse(N'/2/2/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (8, 'Node 8', hierarchyid::Parse(N'/1/1/1/'));
INSERT dbo.Tree(node_id, name, hid) VALUES (9, 'Node 9', hierarchyid::Parse(N'/1/1/2/'));
Solution:
DECLARE @root HIERARCHYID,
@left HIERARCHYID,
@right HIERARCHYID;
SELECT @root = hid FROM dbo.Tree WHERE node_id = 1;
SELECT @left = (SELECT MIN(hid) FROM dbo.Tree WHERE hid.GetAncestor(1) = @root),
@right = (SELECT MAX(hid) FROM dbo.Tree WHERE hid.GetAncestor(1) = @root);
SELECT left_count =
(
SELECT COUNT_BIG(*)
FROM dbo.Tree T
WHERE T.hid.IsDescendantOf(@left) = 1
),
right_count =
(
SELECT COUNT_BIG(*)
FROM dbo.Tree T
WHERE T.hid.IsDescendantOf(@right) = 1
);
GO
-- Tidy up
DROP TABLE dbo.Tree;
This script is give me in sql server 2005 bcz of Hierarchyid datatype is not supported in sql server 2005..
thank u..
July 16, 2010 at 8:09 am
It's precisely because hierarchyid is available in 2008 that I wrote it.
There is no easy equivalent in 2005.
Sorry about that.
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
July 16, 2010 at 8:13 am
i want to talk to u..
is it possible..?
July 16, 2010 at 8:16 am
please try it nd give me proper solution...
thank u..
July 16, 2010 at 8:24 am
Top marks for persistence, but the answer is a polite, but very firm, no.
Try starting your own thread for your problem, others may be willing to help.
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
July 16, 2010 at 11:13 pm
paul pls try to understand me nd give me solution..
July 16, 2010 at 11:16 pm
Joe Celko (7/16/2010)
http://www.simple-talk.com/sql/t-sql-programming/binary-trees-in-sql/
🙂
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
July 17, 2010 at 12:03 am
thank you so much Peter Brinkhaus..
my problem is solved
Viewing 15 posts - 46 through 60 (of 61 total)
You must be logged in to reply to this topic. Login to reply