Sql-server – Display single record for attributes values joining 3 tables in SQL Server

join;optimizationselectsql server

I have 3 tables

Table1 (a view) contains the record ID, Person's Name, Attribute ID and a new record for all attributes entered into the database pertaining to the person.

Table2 is a descriptive identifier name for the Attribute Type ID in Table1

Table3 (a view) contains the data entry timestamp for the record ID in Table1, used as selection criteria range

Table1 –SQL Server 2014 View–

ID [char(18)]
Name [nvarchar(50)]
AttributeTypeID [int]
AttributeValue [nvarchar(2000)]

Table2 –SQL Server 2014 Table–

AttrTypeID [int]
AttrName [nvarchar(50)]

Table3 –SQL Server 2014 View–

EntryID [char(18)]
EntryDateTime [datetime2(3)]

My current query brings them together (however I would like a different output):

SELECT DISTINCT
Table1.ID
Table1.Name
Table2.AttrTypeID
Table2.AttrName
Table1.AttributeValue

FROM 
Table1 RIGHT JOIN Table2 ON Table1.AttributeTypeID = Table2.AttrTypeID

WHERE
Table1.ID IN (SELECT DISTINCT
             EntryID
             FROM Table3
             WHERE
             EntryDateTime BETWEEN '2018-05-01 00:00:00.000' AND '2018-05-07 00:00:00.000')

ORDER BY
Table1.ID ASC

Returns the following records:

ID     | Name  | AttrTypeID  | AttrName   | AttributeValue
123456 | Bob   | 201         | Hair Color | Blonde
123456 | Bob   | 202         | Eye Color  | Blue
123456 | Bob   | 203         | Height CM  | 180   
123456 | Bob   | 204         | Weight KG  | 77.1   
123456 | Bob   | 205         | Email      | bob@domain.com   
123456 | Bob   | 206         | Profile    | Text...2000characters...  
789000 | Sarah | 201         | Hair Color | Brown
789000 | Sarah | 202         | Eye Color  | Brown
789000 | Sarah | 203         | Height CM  | 155   
789000 | Sarah | 204         | Weight KG  | 50.5  
789000 | Sarah | 206         | Profile    | Text...2000characters...  

Note Sarah does not have an entry for email (AttrTypeID = 205) in table 1

My desired output is create a query that returns:

ID     | Name  | Hair Color  | Eye Color  | Height CM  | Weight KG  | Email           | Profile
123456 | Bob   | Blonde      | Blue       | 180        | 77.1       | bob@domain.com  | Text...2000characters...
789000 | Sarah | Brown       | Brown      | 155        | 50.5       | NULL            | Text...2000characters...

Your assistance is very much appreciated!

PS I'm new here so if you need more info or require a different format for asking this question please let me know.

Best Answer

Here is a very simple way to solve this.

--Setup demo data
Declare @Table1 table
    (ID int, Name varchar(5), AttrTypeID int, AttrName varchar(10), AttributeValue varchar(24))
;

INSERT INTO @Table1
    (ID, Name, AttrTypeID, AttrName, AttributeValue)
VALUES
    (123456, 'Bob', 201, 'Hair Color', 'Blonde'),
    (123456, 'Bob', 202, 'Eye Color', 'Blue'),
    (123456, 'Bob', 203, 'Height CM', '180'),
    (123456, 'Bob', 204, 'Weight KG', '77.1'),
    (123456, 'Bob', 205, 'Email', 'bob@domain.com'),
    (123456, 'Bob', 206, 'Profile', 'Text...2000characters...'),
    (789000, 'Sarah', 201, 'Hair Color', 'Brown'),
    (789000, 'Sarah', 202, 'Eye Color', 'Brown'),
    (789000, 'Sarah', 203, 'Height CM', '155'),
    (789000, 'Sarah', 204, 'Weight KG', '50.5'),
    (789000, 'Sarah', 206, 'Profile', 'Text...2000characters...')
;

--The actual query
SELECT ID, max(name) as Name,
  MAX(CASE WHEN AttrName = 'Hair Color' THEN AttributeValue END) AS [Hair Color],
  MAX(CASE WHEN AttrName = 'Eye Color' THEN AttributeValue END) AS [Eye Color],
  MAX(CASE WHEN AttrName = 'Height CM' THEN AttributeValue END) AS [Height CM],
  MAX(CASE WHEN AttrName = 'Weight KG' THEN AttributeValue END) AS [Weight KG],
  MAX(CASE WHEN AttrName = 'Email' THEN AttributeValue END) AS [Email],
  MAX(CASE WHEN AttrName = 'Profile' THEN AttributeValue END) AS [Profile]
FROM @Table1
GROUP BY id, name;

| ID     | Name  | Hair Color | Eye Color | Height CM | Weight KG | Email          | Profile                  |
|--------|-------|------------|-----------|-----------|-----------|----------------|--------------------------|
| 123456 | Bob   | Blonde     | Blue      | 180       | 77.1      | bob@domain.com | Text...2000characters... |
| 789000 | Sarah | Brown      | Brown     | 155       | 50.5      | NULL           | Text...2000characters... |

You just need to wrap your original query inside a common table expression and use that in place of my @Table1. Something like:

;With Table1 as
(
SELECT DISTINCT Table1.ID Table1.NAME Table2.AttrTypeID Table2.AttrName Table1.AttributeValue
FROM Table1
RIGHT JOIN Table2 ON Table1.AttributeTypeID = Table2.AttrTypeID
WHERE Table1.ID IN (
        SELECT DISTINCT EntryID
        FROM Table3
        WHERE EntryDateTime BETWEEN '2018-05-01 00:00:00.000'
                AND '2018-05-07 00:00:00.000'
        )
) 

--The actual query
SELECT ID, max(name) as Name,
  MAX(CASE WHEN AttrName = 'Hair Color' THEN AttributeValue END) AS [Hair Color],
  MAX(CASE WHEN AttrName = 'Eye Color' THEN AttributeValue END) AS [Eye Color],
  MAX(CASE WHEN AttrName = 'Height CM' THEN AttributeValue END) AS [Height CM],
  MAX(CASE WHEN AttrName = 'Weight KG' THEN AttributeValue END) AS [Weight KG],
  MAX(CASE WHEN AttrName = 'Email' THEN AttributeValue END) AS [Email],
  MAX(CASE WHEN AttrName = 'Profile' THEN AttributeValue END) AS [Profile]
FROM Table1
GROUP BY id, name;