A common requirement when dealing with tables with recursive relationship where a record points to another (parent) record in the same table, is to get the full path of the record name. like the case with full folder paths in a file system hierarchy.
For example: (data from http://www.ida.liu.se/~iislab/projects/secont/main/)
For the above data, we need to get this:
This can be achieved using Recursive Common Table Expressions:
It works like this:
For example: (data from http://www.ida.liu.se/~iislab/projects/secont/main/)
| CategoryId | Name | ParentCategoryId |
| 1 | Asset | NULL |
| 2 | Countermeasure | NULL |
| 3 | Cryptography | 2 |
| 4 | Encryption | 3 |
| 5 | SignatureAlgorithm | 4 |
| 6 | CryptographicHashFunction | 5 |
| 7 | DSA | 6 |
| 8 | MD5 | 6 |
| 9 | EncryptionAlgorithm | 4 |
| 10 | BlockCipher | 9 |
| 11 | AES | 10 |
| 12 | DES | 10 |
For the above data, we need to get this:
| CategoryId | Name |
| 1 | Asset |
| 2 | Countermeasure |
| 3 | Countermeasure > Cryptography |
| 4 | Countermeasure > Cryptography > Encryption |
| 5 | Countermeasure > Cryptography > Encryption > SignatureAlgorithm |
| 9 | Countermeasure > Cryptography > Encryption > EncryptionAlgorithm |
| 10 | Countermeasure > Cryptography > Encryption > EncryptionAlgorithm > BlockCipher |
| 11 | Countermeasure > Cryptography > Encryption > EncryptionAlgorithm > BlockCipher > AES |
| 12 | Countermeasure > Cryptography > Encryption > EncryptionAlgorithm > BlockCipher > DES |
| 6 | Countermeasure > Cryptography > Encryption > SignatureAlgorithm > CryptographicHashFunction |
| 7 | Countermeasure > Cryptography > Encryption > SignatureAlgorithm > CryptographicHashFunction > DSA |
| 8 | Countermeasure > Cryptography > Encryption > SignatureAlgorithm > CryptographicHashFunction > MD5 |
This can be achieved using Recursive Common Table Expressions:
WITH CategoryCTE
AS
(
SELECT C.CategoryId, CONVERT(NVARCHAR(500), C.Name) AS Name FROM dbo.Category C
WHERE ParentCategoryId IS NULL
UNION ALL
SELECT C.CategoryId, CONVERT(NVARCHAR(500), CTE.name + N' > ' + C.Name) AS Name FROM dbo.Category C
JOIN CategoryCTE CTE ON C.ParentCategoryId = CTE.CategoryId
)
SELECT * FROM CategoryCTE
It works like this:
- The CTE selects from the base table data, the level which has no parents
- The result is union-ed with the recursive part, which joins the base table with the last value of the CTE up to the current level of recursion
- The name field of a record is a concatenation between its parent name and its own name
- Select the result of the CTE
No comments:
Post a Comment