如果当前列不返回任何行,则查询以选择下一列

时间:2017-12-18 04:33:45

标签: sql-server stored-procedures dynamic-sql

问题非常广泛,请耐心等待。我有一个具有以下结构的映射表​​:

enter image description here

此特定表用于生成层次结构的过程。表中列的顺序和位置表示层次结构的顺序(组织,类别,大陆,国家等等)。此层次结构中的每个实体都有一个相关的表,其中包含关联的IdName 。例如,Country表格包含CountryIdCountryName。请注意,由于MappingTable的值都可以为空,因此没有外键约束

我想生成一个执行以下操作的过程:

根据提供的条件,检索层次结构中下一个实体的值。例如,如果给出OrganizationIdCategoryId,则需要检索满足所述条件的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

1 个答案:

答案 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