2017-12-18 22 views
0

質問は非常に広範囲です。この特定のテーブルは、階層を生成する過程で使用される現在の列が行を返さない場合は、次の列を選択するクエリ

enter image description here

:私は、以下の構造を有する単一のマッピングテーブルを有しています。テーブル内の列の順序と位置は、階層(組織、カテゴリ、大陸、国など)の順番を示します。この階層の各エンティティには、関連テーブルIdNameが関連付けられています。たとえば、CountryIdCountryNameという表があります。Countryテーブルがあります。 MappingTableの値はすべてnullableであるため、の外部キー制約はありませんです。

私は、次の手順を実行します手順を生成したい:提供条件に基づいて

、階層内の次のエンティティの値を取得します。例えば、OrganizationIdCategoryIdが与えられている場合、上記の条件を満たすContinentIdの値を検索する必要があります。

また、ContinentIdの値がNULLの場合は、CountryIdの値を取得する必要があります。ここで、条件がOrganizationId = 1 and CategoryId = 1である場合、手順はRegionIdのリストを返すべきです。

RegionIdの検索に加えて、対応するRegionNameRegionテーブルから取得する必要があります。

これまでの手順は、このようになります。ここで説明するにはいくつかのことがあります。

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ある条件に基づいて)特定の位置で列の名前を取得し、ベースの次の列の値を取得するために、動的なクエリを生成するようなものです(私はこれを行うために別の関数を使用しています)。 一度取得すると、問合せを実行して、行を戻すかどうかを確認します。クエリで行が返された場合は、対応するContinentNameContinentテーブルから取得します。行が返されない場合は、次の位置を入力として再帰的にプロシージャを呼び出します。

物事のビジネス面では、2段階のプロセスのようです。しかし、手続きとして、それは非常に複雑で、広範で、再帰的ではありません。これを行う簡単な方法はありますか?私はCTEに精通していません - CTEを使用して同じロジックを実装できますか?

これは、ここで求められているものと非常によく似ています:Working with a dynamic hierarchy SQL Server

答えて

0

は少し長いアプローチである可能性があります。試してみてください

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 
関連する問題