问题非常广泛,请耐心等待。我有一个具有以下结构的映射表:
此特定表用于生成层次结构的过程。表中列的顺序和位置表示层次结构的顺序(组织,类别,大陆,国家等等)。此层次结构中的每个实体都有一个相关的表,其中包含关联的Id
和Name
。例如,Country
表格包含CountryId
和CountryName
。请注意,由于MappingTable的值都可以为空,因此没有外键约束。
我想生成一个执行以下操作的过程:
根据提供的条件,检索层次结构中下一个实体的值。例如,如果给出OrganizationId
和CategoryId
,则需要检索满足所述条件的ContinentId
值。
此外,如果ContinentId
的值为NULL
,则需要检索CountryId
的值。在这里,给定条件OrganizationId = 1 and CategoryId = 1
,过程应该返回RegionId
。
除了检索RegionId
之外,还应从RegionName
表中检索相应的Region
。
到目前为止,程序看起来像这样 - 只需要解释一些事情。
ALTER PROCEDURE [dbo].[GetHierarchy]
(
@MappingTableName VARCHAR(30),
@Position VARCHAR(5),
-- Given in the form of Key-value pairs 'OrganizationId:1,CategoryId:1'
@InputData VARCHAR(MAX),
@Separator CHAR(1),
@KeyValueSeperator CHAR(1)
)
AS
BEGIN
DECLARE @Sql NVARCHAR(MAX)
DECLARE @Result NVARCHAR(MAX)
DECLARE @Sql1 NVARCHAR(MAX);
DECLARE @TableName NVARCHAR(30)
DECLARE @Exists bit
SELECT @TableName = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @MappingTableName AND ORDINAL_POSITION = @position
SET @TableName = SUBSTRING(@TableName,0,LEN(@TableName) - 1)
-- Returns a dynamic query like "SELECT ContinentId from Continent WHERE OrganizationId = 1 and CategoryId = 1".
SELECT @Sql = [dbo].[KeyValuePairs](@TableName, @InputData, @Separator, @KeyValueSeperator)
SET @Sql1 = N'SET @Exists = CASE WHEN EXISTS(' + @Sql + N' AND ' + @TableName + N'Id IS NOT NULL) THEN 1 ELSE 0 END'
PRINT @Sql
EXEC sp_executesql @Sql1,
N'@Exists bit OUTPUT',
@Exists = @Exists OUTPUT
IF(@Exists = 1)
BEGIN
SET @Sql1 = 'SELECT ' + @TableName + 'Id, ' + @TableName + 'Name FROM '+ @TableName+' WHERE ' + @TableName +'Id IN (' + @Sql + ')';
PRINT @Sql1
EXECUTE sp_executesql @SQL1
END
ELSE
BEGIN
--PRINT 'NOT EXISTS'
DECLARE @nextPosition INT
SELECT @nextPosition = CAST(@position AS INT)
SET @nextPosition = @nextPosition + 1
SET @Position = CONVERT(VARCHAR(5), CAST(@position AS INT))
EXEC [dbo].[GetHierarchy] @MappingTableName, @Position, @InputData, @Separator, @KeyValueSeperator
END
END
此过程的逻辑是这样的,我在特定位置获取列的名称(基于此处的条件,它是Continent
)并生成动态查询以检索基于下一列的值在输入条件的条件下(我使用单独的函数为我做这个)。
检索后,我运行查询以检查它是否返回任何行。如果查询返回行,那么我从ContinentName
表中检索相应的Continent
。如果没有返回行,我会以下一个位置作为输入再次调用该过程。
在业务方面,这似乎是一个两步过程。但是,作为一个程序,它是相当复杂,广泛的 - 更不用说,递归。有更简单的方法吗?我不熟悉CTE - 使用CTE可以实现相同的逻辑吗?
这与此处提出的内容非常相似:Working with a dynamic hierarchy SQL Server
答案 0 :(得分:0)
Might be the little lengthy approach. Try this
DECLARE @T TABLE
(
SeqNo INT IDENTITY(1,1),
CatId INT,
Country INT,
StateId INT,
DistId INT
)
DECLARE @State TABLE
(
StateId INT,
StateNm VARCHAR(20)
)
DECLARE @Country TABLE
(
CountryId INT,
CountryNm VARCHAR(20)
)
INSERT INTO @State
VALUES(3,'FL')
INSERT INTO @Country
VALUES(2,'USA')
INSERT INTO @T(CatId)
VALUES(1)
INSERT INTO @T(CatId,Country)
VALUES(1,2)
INSERT INTO @T(CatId,StateId)
VALUES(1,3)
;WITH CTE
AS
(
SELECT
*,
IdVal = COALESCE(Country,StateId,DistId),
IdCol = COALESCE('Country '+CAST(Country AS VARCHAR(50)),'StateId '+CAST(StateId AS VARCHAR(50)),'DistId '+CAST(DistId AS VARCHAR(50)))
FROM @T
WHERE CatId = 1
),C2
AS
(
SELECT
SeqNo,
CatId,
Country,
StateId,
DistId,
IdVal,
IdCol = LTRIM(RTRIM(SUBSTRING(IdCol,1,CHARINDEX(' ',IdCol))))
FROM CTE
)
SELECT
C2.SeqNo,
C2.CatId,
S.StateNm,
C.CountryNm
FROM C2
LEFT JOIN @State S
ON C2.IdCol ='StateId'
AND C2.IdVal = S.StateId
LEFT JOIN @Country C
ON C2.IdCol ='Country '
AND C2.IdVal = c.CountryId