Home > OS >  Display SQL results in columns instead of a several lines
Display SQL results in columns instead of a several lines

Time:12-03

I have the following tables:

ProfilRights:

ProfilRightId    ProfilId    AccessId     Right 
-------------    --------    --------    --------    
      1             1           1           full  
      2             1           2           r-o
      3             2           1           none
      4             2           2           full

Profil table:

    ProfilId    ProfilName    
    --------    ----------    
       1           IT            
       2           Admin        

Access table:

        AcessId       AcessName    
        --------      ----------    
           1          Employess
           2          Clients

Translation table for Acess: AccessTranslation

   AccessTransId   AcessId       TranslatedLabel   Language
   -------------   --------      ---------------   -------- 
         1           1           Employees         English
         2           2           Clients           English
         3           1           EmployeesFR       French
         4           2           ClientsFR         French

I want the results to be displayed in the following way:

    EnglishAcess    FrenchAcess     IT        Admin
    -------------    --------      ------     ------
    Employees        EmployeesFR   full        none    
    Clients          ClientsFR      r-o        full

What I want is that, no matter how many profiles I have, these will be displayed as an additional column in the results set. Like if, say we add a new profil, Director, in the profil table and configure the rights in the ProfilRights table, this will display another column, on the right of Admin in the result set.

I have tried doing the below query but it does not work:

SELECT P.ProfilName, AT.TranslatedLabel,  PR.Right
FROM ProfilRights PR
INNER JOIN Profil P ON PR.PofilId = P.PofilId
INNER JOIN Access A ON PR.AccessId = A.AccessId
INNER JOIN AccessTranslation [AT] ON A.AccessId = AT.AccessId;

However, this is returning me the results as below:

 ProfilName    TranslatedLabel     Right
 -----------   --------------      ------
 Admin         EmployeesFR         none
 Admin         ClientsFR           full        
 Admin         Employees           none
 Admin         Clients             full                
 IT            EmployeesFR         full        
 IT            ClientsFR           r-o        
 IT            Employees           full        
 IT            Clients             r-o 

Please help on how to return the above results set?

CodePudding user response:

To solve your problem, i would use dynamic query PIVOT.

Here is an article that explains how to do it: Dynamic Pivot

CodePudding user response:

ANSWER


DECLARE @Counter INT;
DECLARE @totalCount INT;

-- Generate query for accessId and Language mapping temp resultSet

-- Get distinct Language values and store in a temp table
DECLARE @distinctLanguage TABLE (rn INT IDENTITY,Languages VARCHAR(20));
INSERT INTO @distinctLanguage(Languages) (SELECT DISTINCT Languages FROM Translation);


DECLARE @languageQuery NVARCHAR(MAX);


SET @totalCount = (SELECT count(*) FROM @distinctLanguage);

SET @Counter = @totalCount;

WHILE (@counter>0)
BEGIN
  DECLARE @languageName NVARCHAR(20) = (SELECT Languages FROM @distinctLanguage WHERE rn=@counter);

  IF @counter <> @totalCount SET @languageQuery = CONCAT(@languageQuery,' FULL OUTER JOIN ') 
  
  SET @languageQuery = CONCAT(@languageQuery,' (SELECT AccessId, TranslatedLabel AS ',@languageName,'Access FROM Translation WHERE Languages = ''',@languageName,''')','sq',@counter,' ');
  
  IF @counter <> @totalCount SET @languageQuery = CONCAT(@languageQuery,' ON (sq',@totalCount,'.AccessId=sq',@counter,'.AccessId) ') 

  SET @counter = @counter - 1;

END

DECLARE @allLanguageString NVARCHAR(MAX);
SET @allLanguageString = (SELECT STRING_AGG(CONCAT(Languages,'Access'),',') FROM @distinctLanguage);

SET @languageQuery = CONCAT(' SELECT ',@allLanguageString,', sq',@totalCount,'.AccessId FROM ', @languageQuery);


-- Generate query for accessId and profileName mapping temp resultSet

-- Get distinct Profile values and store in a temp table
DECLARE @distinctProfiles TABLE (rn INT IDENTITY,ProfilName VARCHAR(20));
INSERT INTO @distinctProfiles(ProfilName) (SELECT DISTINCT ProfilName FROM Profil);

DECLARE @profileQuery NVARCHAR(MAX);


SET @totalCount = (SELECT count(*) FROM @distinctProfiles);

SET @Counter = @totalCount;

WHILE (@counter>0)
BEGIN
  DECLARE @profileName NVARCHAR(20) = (SELECT ProfilName FROM @distinctProfiles WHERE rn=@counter);

  IF @counter <> @totalCount SET @profileQuery = CONCAT(@profileQuery,' FULL OUTER JOIN ') 

  SET @profileQuery = CONCAT(@profileQuery,' (SELECT AccessId, Rights AS ',@profileName,' FROM @profilRights WHERE ProfilName = ''',@profileName,''')','sq',@counter,' ');
  
  IF @counter <> @totalCount SET @profileQuery = CONCAT(@profileQuery,' ON (sq',@totalCount,'.AccessId=sq',@counter,'.AccessId) ') 

  SET @counter = @counter - 1;

END

DECLARE @allProfileString NVARCHAR(MAX);
SET @allProfileString = (SELECT STRING_AGG(ProfilName,',') FROM @distinctProfiles);

SET @profileQuery = CONCAT(' SELECT ',@allProfileString,', sq',@totalCount,'.AccessId FROM ', @profileQuery);


-- Construct final query
DECLARE @finalQuery NVARCHAR(MAX);
SET @finalQuery = CONCAT(' SELECT ' ,@allLanguageString, ', ',@allProfileString,' FROM ( ',@languageQuery,' ) a FULL OUTER JOIN ( ',@profileQuery,' ) b ON (a.AccessId=b.AccessId)');


-- Create a temp table for profile, accessId and rights mapping
DECLARE @profileRightMappingQuery NVARCHAR(MAX);
SET @profileRightMappingQuery= 'DECLARE @profilRights TABLE (ProfilName VARCHAR(20), AccessId INT, Rights VARCHAR(20)); INSERT INTO @profilRights(ProfilName, AccessId, Rights) SELECT ProfilName, AccessId,Rights FROM ProfilRights a LEFT JOIN Profil b ON(a.ProfilId=b.ProfilId);';

SET @finalQuery = CONCAT(@profileRightMappingQuery,' ',@finalQuery);

execute(@finalQuery);

EXPLANATION

DISCLAIMER

  • In my opinion, following solution is sql-gymnastics. For the sake of simplicity, performance and maintenance, I would recommended to solve this problem using some code. First bring the request dataset in your application and construct the result there.
  • This query is using STRING_AGG function to convert rows of a table to comma-separated string. This function is available for version >=SQL Server 2017. In case you are using earlier version, you might need to do that manually.
  • Though I have tested the query, still some minor edge cases might have been left.

So that being said, let's start with the explanation.

Following is the static query that would give you the desired result.

DECLARE @profilRights TABLE (ProfilName VARCHAR(20), AccessId INT, Rights VARCHAR(20)); 

INSERT INTO @profilRights(ProfilName, AccessId, Rights)
SELECT ProfilName, AccessId,Rights
FROM ProfilRights a
LEFT JOIN Profil b ON(a.ProfilId=b.ProfilId);


SELECT EnglishAccess, FrenchAccess, IT, Admin
FROM
(
  SELECT EnglishAccess,FrenchAccess,a.AccessId
  FROM
   (
    SELECT AccessId,TranslatedLabel AS 'EnglishAccess' FROM Translation WHERE Languages='English'
   )a FULL OUTER JOIN
   (
    SELECT AccessId,TranslatedLabel AS 'FrenchAccess' FROM Translation WHERE Languages='French'
   )b ON (a.AccessId = b.AccessId)
) a FULL OUTER JOIN
(
  SELECT a.AccessId, IT, Admin
  FROM
   (
    SELECT AccessId, Rights AS 'IT' FROM @profilRights WHERE ProfilName = 'IT'
   )a FULL OUTER JOIN
   (
    SELECT AccessId, Rights AS 'Admin' FROM @profilRights WHERE ProfilName = 'Admin'
   ) b ON (a.AccessId=b.AccessId)
) b ON (a.AccessId=b.AccessId)

But this query has hard-coded values, so your requirement is not completely fulfilled.

So we simply need to write code to make this query dynamic. To do that, I followed the below algorithm.

- Generate query for accessId and Language mapping temp resultSet.
- Generate query for accessId and profileName mapping temp resultSet.
- Construct final query.
- Create a temp table for profile, accessId and rights mapping.
- Execute the final query.

DDL Script

CREATE TABLE ProfilRights(ProfilRightId INT,ProfilId INT,AccessId INT, RIGHTS VARCHAR(10));

INSERT INTO ProfilRights(ProfilRightId, ProfilId ,AccessId , RIGHTS)
VALUES (1, 1,1,'full'),
(2, 1, 2,'r-o'),
(3, 2, 1,'none'),
(4, 2, 2,'full')


CREATE TABLE Profil(ProfilId INT, ProfilName VARCHAR(10));

INSERT INTO Profil(ProfilId , ProfilName)
VALUES (1, 'IT'),
(2, 'Admin')



CREATE TABLE Access(AccessId INT, AccessName VARCHAR(10));

INSERT INTO Access(AccessId , AccessName)
VALUES (1, 'Employess'),
(2, 'Clients')



CREATE TABLE Translation(AccessTransId INT, AccessId INT, TranslatedLabel VARCHAR(20), Languages VARCHAR(20));

INSERT INTO Translation(AccessTransId , AccessId , TranslatedLabel , Languages)
VALUES (1, 1,'Employess','English'),
(2, 2,'Clients','English'),
(3, 1,'EmployeesFR','French'),
(4, 2,'ClientsFR','French')
  • Related