Home > Blockchain >  Matrix table SQL
Matrix table SQL

Time:01-20

I have three tables:

  • Table tCity

    (Id int, City nvarchar(50))
    
  • Table tLocation

    (Id int, Location nvarchar(50))
    
  • Table tCityLocation

    (Id int, CityId int, LocationId int)
    

I would like to generate matrix table like in image below.

If City belongs to location-> in appropriate cell in table, char X will be written down.

I would like to about any sophisticated approach how to reach it. I had issue in similar logic and processed by iteration in cursors with dynamically added column to result set table.

Exists any sophisticated approach instead of cursor and dynamically added column?

Thank you.

ResultSet

CodePudding user response:

It should be something like this:

DECLARE @DymanimcTSQLSatement NVARCHAR(MAX)
       ,@DynamicColumns NVARCHAR(MAX);

SET @DynamicColumns = STUFF
                      (
                        (
                            SELECT ','   QUOTENAME([Location])
                            FROM tLocation
                            GROUP BY [Location] 
                            ORDER BY [Location] 
                            FOR XML PATH(''), TYPE
                        ).value('.', 'NVARCHAR(MAX)')
                        ,1
                        ,1
                        ,''
                     );

SET @DymanimcTSQLSatement = N'
SELECT *
FROM
(
    SELECT L.Location
          ,C.City
          ,1
    FROM tCityLocation CL
    INNER JOIN tLocation L
        ON CL.[LocationId] = L.[id]
    INNER JOIN tCity C
        ON CL.[CityId] = C.[id]
) DS (country, city, value)
PIVOT
(
    MAX([value]) FOR [country] IN ('   @DynamicColumns  ')
) PVT;';

EXECUTE sp_executesql @DymanimcTSQLSatement;

enter image description here

and here is the data I have used:

-- Creating Table tCity
CREATE TABLE tCity (
    Id int,
    City nvarchar(50)
);

-- Populating Table tCity with 10 records
INSERT INTO tCity (Id, City) VALUES (1, 'New York');
INSERT INTO tCity (Id, City) VALUES (2, 'Los Angeles');
INSERT INTO tCity (Id, City) VALUES (3, 'Paris');
INSERT INTO tCity (Id, City) VALUES (4, 'Tokyo');
INSERT INTO tCity (Id, City) VALUES (5, 'Sydney');
INSERT INTO tCity (Id, City) VALUES (6, 'Philadelphia');
INSERT INTO tCity (Id, City) VALUES (7, 'Rio de Janeiro');
INSERT INTO tCity (Id, City) VALUES (8, 'Cape Town');
INSERT INTO tCity (Id, City) VALUES (9, 'Beijing');
INSERT INTO tCity (Id, City) VALUES (10, 'Singapore');

-- Creating Table tLocation
CREATE TABLE tLocation (
    Id int,
    Location nvarchar(50)
);

-- Populating Table tLocation with 10 records
INSERT INTO tLocation (Id, Location) VALUES (1, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (2, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (3, 'France');
INSERT INTO tLocation (Id, Location) VALUES (4, 'Japan');
INSERT INTO tLocation (Id, Location) VALUES (5, 'Australia');
INSERT INTO tLocation (Id, Location) VALUES (6, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (7, 'Brazil');
INSERT INTO tLocation (Id, Location) VALUES (8, 'South Africa');
INSERT INTO tLocation (Id, Location) VALUES (9, 'China');
INSERT INTO tLocation (Id, Location) VALUES (10, 'Singapore');

-- Creating Table tCityLocation
CREATE TABLE tCityLocation (
    Id int,
    CityId int,
    LocationId int
);


INSERT INTO tCityLocation (Id, CityId, LocationId)
SELECT 
    ROW_NUMBER() OVER (ORDER BY tCity.Id), 
    tCity.Id, tLocation.Id
FROM tCity 
JOIN tLocation 
ON tCity.Id = tLocation.Id;

The idea is to built a dynamic PIVOT, where the PIVOT columns are the unique countries (in your case locations). If your SQL Server supports STRING_AGG, you can do this in different way:

SELECT @DynamicColumns = STRING_AGG(CAST(QUOTENAME([Location]) AS VARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY [Location])
FROM
(
    SELECT DISTINCT [Location]
    FROM tLocation
) DS ([Location]);

CodePudding user response:

Better way for answer to this question use pivot

--Get List Columns for Pivot

DECLARE @cols AS NVARCHAR(MAX),@scols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ','   QUOTENAME(Location)  from tLocation
                 FOR XML PATH(''), TYPE  ).value('.', 'NVARCHAR(MAX)')   ,1,1,'' )
 
--can to use below Code for Replace null with space

select @scols = STUFF((SELECT distinct ',ISNULL('   QUOTENAME(Location)  ','' '') as '   QUOTENAME(Location)
                 from tLocation  FOR XML PATH(''), TYPE  ).value('.', 'NVARCHAR(MAX)')  ,1,1,'')
 
 set @query = 'SELECT  city ,' @scols ' from 
             (
                select cl.CityId,c.City,l.Location,''X'' as Value from tCityLocation cl
                inner join tCity c on cl.CityId=c.Id
                inner join tLocation l on cl.LocationId=l.Id
            ) x
            pivot 
            (
               max( value)  for Location in ('   @cols   ')
            ) p 
            order by CityId
            '

execute(@query)

You can to insert the basic data with the following codes


create table tCity(Id int, City nvarchar(50))

create table tLocation(Id int, Location nvarchar(50))

create table tCityLocation(Id int, CityId int, LocationId int)

insert into tCity
(id,City)
select 1 as id,'NY' union all select 2 as id,'Prague'
union all select 3 as id,'London' union all select 4 as id,'Tokio'

insert into tLocation (id,Location)
select 1,'USA' union all select 2,'CRezah Rep.' union all select 3,'France' union all select 4,'GB'
union all select 5,'Germany' union all select 6,'Europe' union all select 7,'N.America'
union all select 9,'Asia' union all select 10,'Japan'

insert into tCityLocation
(id,CityId,LocationId)
select 1,1,1 union all select 2,1,7 union all select 3,2,2 union all select 4,2,6 union all select 5,3,4 union all
select 6,3,6 union all select 7,4,9 union all select 8,4,10 

  • Related