【问题标题】:Query to select next column if the current column returns no rows如果当前列没有返回行,则查询以选择下一列
【发布时间】:2017-12-18 04:33:45
【问题描述】:

这个问题相当广泛,请多多包涵。我有一个具有以下结构的映射表​​:

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

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

根据提供的条件,检索层次结构中下一个实体的值。比如给定OrganizationIdCategoryId,则需要取出满足上述条件的ContinentId的值。

另外,如果ContinentId 的值为NULL,则需要检索CountryId 的值。在这里,给定条件OrganizationId = 1 and CategoryId = 1,过程应该返回RegionId的列表。

除了检索RegionId之外,还应从Region表中检索对应的RegionName

到目前为止,该过程看起来是这样的 - 这里只需要解释几件事。

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)并生成动态查询以根据输入条件的条件(我正在使用单独的函数为我执行此操作)。 检索到后,我运行查询以检查它是否返回任何行。如果查询返回行,则我从Continent 表中检索相应的ContinentName。如果没有返回行,我会以下一个位置作为输入再次递归调用该过程。

在业务方面,这似乎是一个两步过程。但是,作为一个过程,它非常复杂、广泛,而且——更不用说是递归的了。有没有更简单的方法来做到这一点?我对 CTE 不熟悉 - 可以使用 CTE 实现相同的逻辑吗?

这和这里问的很相似:Working with a dynamic hierarchy SQL Server

【问题讨论】:

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


    【解决方案1】:

    可能是有点冗长的方法。试试这个

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多