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)
 Cumulative related records from hierarchy

Author  Topic 

pootle_flump

1064 Posts

Posted - 2008-05-29 : 10:21:15
Oh hai

This is closely related to this problem where Ryan & Peter helped me out loads (thanks again guys):
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=101989

I now have a slightly different problem. Instead of returning the cumulative counts of people related to a business unit in the hierarchy I now need to return the actual people records. I had some code, derived from the above, that worked fine until I loaded some new data. It took a while but eventually I figured out the (now obvious) reason for the error. I have tried several changes to the query and not got anywhere. Please could someone have a look and see if you can suggest the required alteration(s)?

USE tempdb
go

---------------------------------------------------------------
-- Set up the structure & data...
---------------------------------------------------------------
CREATE TABLE dbo.org
(
unit_code VARCHAR(10) NOT NULL
, parent_code VARCHAR(10) NULL
, CONSTRAINT pk_org PRIMARY KEY CLUSTERED (unit_code) WITH (FILLFACTOR = 100)
)
GO

CREATE TABLE dbo.org_peeps
(
unit_code VARCHAR(10) NOT NULL
, person_code INT NOT NULL
, CONSTRAINT pk_org_peeps PRIMARY KEY CLUSTERED (unit_code, person_code) WITH (FILLFACTOR = 100)
, CONSTRAINT fk_org_peeps_other_org_peeps FOREIGN KEY (unit_code) REFERENCES dbo.org (unit_code)
)
GO

CREATE VIEW dbo.org_peeps_parents
--WITH SCHEMABINDING
AS
SELECT org.unit_code
, org.parent_code
, org_peeps.person_code
FROM dbo.org
LEFT OUTER JOIN
dbo.org_peeps
ON org_peeps.unit_code = org.unit_code
GO

INSERT INTO dbo.org (unit_code, parent_code)
SELECT 'a', 'y' UNION ALL
SELECT 'b', 'y' UNION ALL
SELECT 'c', 'z' UNION ALL
SELECT 'y', 'z' UNION ALL
SELECT 'z', NULL

--Insert people data (NOTE - the people are in units at the BOTTOM of the structure only)
INSERT INTO dbo.org_peeps (unit_code, person_code)
SELECT 'a', 1 UNION ALL
SELECT 'c', 3

---------------------------------------------------------------
SELECT 'This works great....'
---------------------------------------------------------------
;WITH materialised_paths
AS
(
SELECT unit_code
, parent_code
, person_code
, unit_path = '/' + CAST(unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents
WHERE parent_code IS NULL
UNION ALL
SELECT all_people.unit_code
, all_people.parent_code
, all_people.person_code
, mp.unit_path + CAST(all_people.unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents AS all_people
INNER JOIN
materialised_paths AS mp
ON mp.unit_code = all_people.parent_code
)

SELECT parents.unit_code
, children.person_code
, children.unit_path
FROM materialised_paths AS children
CROSS APPLY --Correlated derived table - get the child records per unit
(
SELECT unit_code
, parent_code
FROM dbo.org AS parents_sub
WHERE children.unit_path LIKE '%/' + parents_sub.unit_code + '/%'
) AS parents
WHERE children.person_code IS NOT NULL
ORDER BY person_code
, unit_code


--Add data to INTERMEDIATE levels of organisation
INSERT INTO dbo.org_peeps (unit_code, person_code)
SELECT 'y', 4 UNION ALL
SELECT 'y', 5 UNION ALL
SELECT 'y', 6


---------------------------------------------------------------
SELECT 'Since there is now data in the intermediate tables there are ' +
'duplicate outputs....'
---------------------------------------------------------------
;WITH materialised_paths
AS
(
SELECT unit_code
, parent_code
, person_code
, unit_path = '/' + CAST(unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents
WHERE parent_code IS NULL
UNION ALL
SELECT all_people.unit_code
, all_people.parent_code
, all_people.person_code
, mp.unit_path + CAST(all_people.unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents AS all_people
INNER JOIN
materialised_paths AS mp
ON mp.unit_code = all_people.parent_code
)
SELECT parents.unit_code
, children.person_code
, children.unit_path
FROM materialised_paths AS children
CROSS APPLY --Correlated derived table - get the child records per unit
(
SELECT unit_code
, parent_code
FROM dbo.org AS parents_sub
WHERE children.unit_path LIKE '%/' + parents_sub.unit_code + '/%'
) AS parents
WHERE children.person_code IS NOT NULL
ORDER BY person_code
, unit_code

---------------------------------------------------------------
SELECT 'This is the output I want - but I can''t simply apply distinct - the volumes of data preclude this'
---------------------------------------------------------------
;WITH materialised_paths
AS
(
SELECT unit_code
, parent_code
, person_code
, unit_path = '/' + CAST(unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents
WHERE parent_code IS NULL
UNION ALL
SELECT all_people.unit_code
, all_people.parent_code
, all_people.person_code
, mp.unit_path + CAST(all_people.unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org_peeps_parents AS all_people
INNER JOIN
materialised_paths AS mp
ON mp.unit_code = all_people.parent_code
)
SELECT DISTINCT
parents.unit_code
, children.person_code
, children.unit_path
FROM materialised_paths AS children
CROSS APPLY --Correlated derived table - get the child records per unit
(
SELECT unit_code
, parent_code
FROM dbo.org AS parents_sub
WHERE children.unit_path LIKE '%/' + parents_sub.unit_code + '/%'
) AS parents
WHERE children.person_code IS NOT NULL
ORDER BY person_code
, unit_code


--Clean up
IF EXISTS (SELECT NULL FROM sys.views WHERE object_id = OBJECT_ID('dbo.org_peeps_parents')) BEGIN
DROP VIEW dbo.org_peeps_parents
END

IF EXISTS (SELECT NULL FROM sys.tables WHERE object_id = OBJECT_ID('dbo.org_peeps')) BEGIN
DROP TABLE dbo.org_peeps
END
IF EXISTS (SELECT NULL FROM sys.tables WHERE object_id = OBJECT_ID('dbo.org')) BEGIN
DROP TABLE dbo.org
END
Just paste and run the code and hopefully it should be reasonably clear. If you need any further info please let me know

pootle_flump

1064 Posts

Posted - 2008-05-29 : 10:32:14
To make it easier to read & quicker to see the meat & drink of it all I've changed all the DDL and set up code to red font.
Go to Top of Page

pootle_flump

1064 Posts

Posted - 2008-05-30 : 05:34:43
FWIW this is my solution so far. I don't rate it much and the optimiser doesn't seem to think a great deal of it either. Is there a more efficient way to get the same result?
;WITH	materialised_paths
AS
(
SELECT unit_code
, parent_code
, unit_path = '/' + CAST(unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org
WHERE parent_code IS NULL
UNION ALL
SELECT all_orgs.unit_code
, all_orgs.parent_code
, mp.unit_path + CAST(all_orgs.unit_code AS VARCHAR(MAX)) + '/'
FROM dbo.org AS all_orgs
INNER JOIN
materialised_paths AS mp
ON mp.unit_code = all_orgs.parent_code
)
SELECT all_orgs.unit_code
, org_peeps.person_code
, all_orgs.unit_path
, org_peeps.unit_code
FROM dbo.org_peeps
INNER JOIN
(
SELECT org.unit_code
, mp.unit_path
FROM materialised_paths AS mp
CROSS APPLY
(
SELECT unit_code
, parent_code
FROM dbo.org
WHERE mp.unit_path LIKE '%/' + org.unit_code + '/%'
) AS org
) AS all_orgs
ON all_orgs.unit_path LIKE '%/' + org_peeps.unit_code + '/'
ORDER BY person_code
, all_orgs.unit_code
Go to Top of Page

pootle_flump

1064 Posts

Posted - 2008-05-30 : 06:06:24
I have asked for input on improving the last query above over at DBF too. I will feed back if anything comes of it.
http://www.dbforums.com/showthread.php?t=1630814
Go to Top of Page

georgev
Posting Yak Master

122 Posts

Posted - 2008-05-30 : 10:02:50
An answer: http://www.dbforums.com/showthread.php?t=1630814#post6341416


George
<3Engaged!
Go to Top of Page

pootle_flump

1064 Posts

Posted - 2008-05-30 : 11:34:04
George's code is much leaner and better than my bodge. I will be going with that.
Go to Top of Page
   

- Advertisement -