user680865
user680865

Reputation: 299

Create hierarchical formatted output from table

I have a SQL table like this:

CC     Descr  C_NO     Vol   Wt

2050   Des1   123      20    40
2060   Des2   123      30    50
2050   Des1   125      20    40
2060   Des2   125      30    50
2050   Des1   126      20    40

and I want output like this:

2050
    Des1

 123
    20
    40
 125
    20
    40   
 126
    20
    40

2060
    Des2

 123
    30
    50
 125
    30
    50

How can I do that using TSQL code?

For every similar CC value which always have similar Descr value, it shows all the C_No, Vol and Wt values related to that particular CC value in the sequence written in the output section.

Upvotes: 4

Views: 846

Answers (4)

gotqn
gotqn

Reputation: 43666

One of the best way to represent data in hierarchical order is using XML. Something more, often the use of XML is connected with great performance.

Using your example information and this peace of code:

;WITH DistinctValues (CC,Descr ) AS
(
    SELECT DISTINCT CC,Descr 
    FROM @TableOne
)
SELECT  (
            SELECT  CC AS "CC/@CC"
                   ,Descr AS "CC/Descr"
                   ,(
                        SELECT C_NO AS "C_NO/@C_NO",
                               Vol AS "C_NO/Vol",
                               Wt AS "C_NO/Wt"
                        FROM @TableOne AS Data
                        WHERE Data.CC=DV.CC AND Data.Descr=DV.Descr
                        FOR XML PATH(''),TYPE
                   ) AS  "CC"
            FROM DistinctValues AS DV
            FOR XML PATH(''),TYPE
        )
FOR XML PATH('Source'),TYPE

I get the following result (in the form of one row return by the statement above):

<Source>
  <CC CC="2050">
    <Descr>Des1</Descr>
    <C_NO C_NO="123">
      <Vol>20</Vol>
      <Wt>40</Wt>
    </C_NO>
    <C_NO C_NO="125">
      <Vol>20</Vol>
      <Wt>40</Wt>
    </C_NO>
    <C_NO C_NO="126">
      <Vol>20</Vol>
      <Wt>40</Wt>
    </C_NO>
  </CC>
  <CC CC="2060">
    <Descr>Des2</Descr>
    <C_NO C_NO="123">
      <Vol>30</Vol>
      <Wt>50</Wt>
    </C_NO>
    <C_NO C_NO="125">
      <Vol>30</Vol>
      <Wt>50</Wt>
    </C_NO>
  </CC>
</Source>

Note that the XML functions in T-SQL combined with the XML language structure gives you a huge of possible variations of output data formats - you use one or many nodes, you can represent the the data as attributes or node text. Combine this as you like and as you feel it will be easiest to work with in your application level.

Here is the whole code (just copy and paste) that I have used to generated the previous XML structure:

DECLARE @TableOne TABLE
(
    CC BIGINT,
    Descr NVARCHAR(10),
    C_NO INT,
    Vol SMALLINT,
    WT SMALLINT
)

INSERT INTO @TableOne(CC,Descr,C_NO,Vol,WT)
VALUES  (2050,'Des1',123,20,40)
       ,(2060,'Des2',123,30,50)
       ,(2050,'Des1',125,20,40)
       ,(2060,'Des2',125,30,50)
       ,(2050,'Des1',126,20,40)

;WITH DistinctValues (CC,Descr ) AS
(
    SELECT DISTINCT CC,Descr 
    FROM @TableOne
)
SELECT  (
            SELECT  CC AS "CC/@CC"
                   ,Descr AS "CC/Descr"
                   ,(
                        SELECT C_NO AS "C_NO/@C_NO",
                               Vol AS "C_NO/Vol",
                               Wt AS "C_NO/Wt"
                        FROM @TableOne AS Data
                        WHERE Data.CC=DV.CC AND Data.Descr=DV.Descr
                        FOR XML PATH(''),TYPE
                   ) AS  "CC"
            FROM DistinctValues AS DV
            FOR XML PATH(''),TYPE
        )
FOR XML PATH('Source'),TYPE

This works like a charm and it is tested on Microsoft SQL Server Management Studio 2012.

Upvotes: 0

Niladri Biswas
Niladri Biswas

Reputation: 4171

Try this

 DECLARE @t TABLE
(
    CC BIGINT,
    Descr NVARCHAR(10),
    C_NO INT,
    Vol SMALLINT,
    WT SMALLINT
)

INSERT INTO @t(CC,Descr,C_NO,Vol,WT)
VALUES  (2050,'Des1',123,20,40)
       ,(2060,'Des2',123,30,50)
       ,(2050,'Des1',125,20,40)
       ,(2060,'Des2',125,30,50)
       ,(2050,'Des1',126,20,40)



;WITH CTE1 AS(
    SELECT 
        t1.CC
        ,t1.Descr
        ,MergedColumn = STUFF(
            (SELECT 
                    ',' 
                    + CAST(C_NO AS VARCHAR(10)) + '/' + CAST(Vol AS VARCHAR(10)) + '/' + CAST(Wt AS VARCHAR(10)) 
            FROM @t AS t2
            WHERE t2.CC=t1.CC AND t2.Descr=t2.Descr
            FOR XML PATH('')),1,1,'')
FROM  @t t1
GROUP BY t1.CC,t1.Descr) 
,CTE2 AS
(
    SELECT
            X.CC
            ,X.Descr
            ,Y.SplitDataByComma         
    FROM
        (
            SELECT 
                    *,
                    CAST('<X>'+REPLACE(F.MergedColumn,',','</X><X>')+'</X>' AS XML) AS xmlfilter 
            FROM CTE1 F
        )X
    CROSS APPLY
    ( 
        SELECT fdata.D.value('.','varchar(50)') AS SplitDataByComma 
        FROM X.xmlfilter.nodes('X') AS fdata(D)
    )Y
)
,CTE3 AS
(
    SELECT
            X.CC
            ,X.Descr
            ,Y.SplitDataBySlash         
            , X.SplitDataByComma AS GrpID
            ,ROW_NUMBER() OVER( PARTITION  BY X.SplitDataByComma ORDER BY X.CC,X.Descr ) AS Rn
            ,X.SplitDataByComma + CAST(CC AS Varchar(200)) + CAST(Descr AS Varchar(200))   CC_Descr 
    FROM
        (
            SELECT 
                    *,
                    CAST('<X>'+REPLACE(F.SplitDataByComma,'/','</X><X>')+'</X>' AS XML) AS xmlfilter 
            FROM CTE2 F
        )X
    CROSS APPLY
    ( 
        SELECT fdata.D.value('.','varchar(50)') AS SplitDataBySlash 
        FROM X.xmlfilter.nodes('X') AS fdata(D)
    )Y
)
,CTE4 AS
(
    SELECT  
        Rn = ROW_NUMBER() OVER(PARTITION BY CC ORDER BY CC)     
        ,CC
        ,Descr      
        ,CASE WHEN  Rn = 1 THEN CAST (SplitDataBySlash AS VARCHAR(10)) ELSE ' '  END C_NO
        ,CASE WHEN  Rn = 1 THEN ' ' ELSE CAST (SplitDataBySlash AS VARCHAR(10)) END Vol_Wt
        ,GrpID  
    FROM Cte3
)
,CTE5 AS(
SELECT 
        CC =  CASE WHEN Rn > 1 THEN ' ' ELSE CAST(CC AS Varchar(200)) END 
        ,Descr =  CASE WHEN Rn > 1 THEN ' ' ELSE CAST(Descr AS Varchar(200)) END 
        ,C_NO
        ,Vol_Wt
        ,GrpID  
FROM Cte4)

SELECT  
          CHAR(10)      
        + REPLICATE(SPACE(1),10) 
        + CAST(CC as VARCHAR(100)) 
        + CHAR(10) 
        + REPLICATE(SPACE(1),15) 
        + Descr
        + CHAR(10)
        + REPLICATE(SPACE(1),10)
        + C_NO
        + CHAR(10) 
        + REPLICATE(SPACE(1),15)
        + Vol_Wt
FROM CTE5

In Text Mode(CTRL + T), the result is

     2050
           Des1
      123
           20           
           40
      125
           20           
           40
      126
           20
           40

      2060
           Des2
      123
           30           
           50
      125
           30
           50

And in Grid Mode(CTRL + D) , the result is

(No column name)
      2050
           Des1
      123 
           20           
           40
      125           
           20           
           40
      126
           20
           40

      2060
           Des2
      123
           30
           50
      125
           30
           50

However, SQL Server (or any database) is not the place to do formatting as others spoke about.Please look into the matter.

Upvotes: 2

BitwiseMan
BitwiseMan

Reputation: 1947

I wouldn't do this. TSQL is used to get the data, then you format that data with application code.
But if you're working in Query analyzer and just want the output there, you can try the following:

DECLARE mytable_cursor CURSOR FOR 
SELECT CC, Descr, C_NO, Vol, Wt
FROM myTable
ORDER BY CC, Descr, C_NO

OPEN mytable_cursor;

FETCH NEXT FROM mytable_cursor 
INTO @CC, @Descr, @C_NO, @Vol, @Wt

SET @oCC = @CC
SET @oDescr = @Descr
SET @oC_NO = @C_NO

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @CC;
    WHILE @@FETCH_STATUS = 0 AND @oCC = @CC
    BEGIN    
        PRINT '    ' + @Descr;
        WHILE @@FETCH_STATUS = 0 AND @oDescr = @Descr
        BEGIN
            PRINT ' ' + @C_NO;
            WHILE @@FETCH_STATUS = 0 AND @oC_NO = @C_NO
            BEGIN
                PRINT '    ' + @Vol;
                PRINT '    ' + @WT;

                FETCH NEXT FROM mytable_cursor 
                INTO @CC, @Descr, @C_NO, @Vol, @Wt

            END
            SET @oC_NO = @C_NO
        END             
        SET @oDescr = @Descr
    END             
    SET @oCC = @CC
END     
CLOSE mytable_cursor;
DEALLOCATE mytable_cursor;

Or you can use a GROUP BY ... WITH ROLLUP and CASE WHEN to get the results a single query.

Here's an example with dashes instead of spaces to make sure the formatting is visible:

SELECT (CASE
        WHEN Descr IS NULL THEN CC
        WHEN C_NO IS NULL THEN '----' + Descr
        WHEN Vol IS NULL THEN '--' + C_NO
        WHEN Wt IS NULL THEN '------' + Vol
        ELSE '------' + Wt
        END)
FROM
    (SELECT CC, Descr, C_NO, Vol, Wt
    FROM my
    GROUP BY CC, Descr, C_NO, Vol, Wt
    WITH ROLLUP) AS x
WHERE CC IS NOT NULL
ORDER BY CC, Descr, C_NO, Vol, Wt

Upvotes: 1

HLGEM
HLGEM

Reputation: 96640

You can't do this in tsql code. You need to do this sort of thing in the application code.

Upvotes: 0

Related Questions