0

Ok please excuse the slightly strange example, but I couldn't think of anything better and my real SQL query would overcomplicate the essence of the real question...

Lets imagine I have 3 tables: - Person (PersonID, PersonName, DoB, etc) - PersonItemKey (PersonItemKeyID, ItemName) - PersonItemValue (PersonItemValueID, PersonItemKeyID, PersonID, Value)

So we could have Item keys for "Socks", "Jumpers", "T-Shirts, and Item Values that say that a particular person has say 10 pairs of sock, 6 jumpers and 8 shirts.

I can get this information with a query something like this:

SELECT * 
FROM Person
LEFT JOIN PersonItemValue ON PersonItemValue.PersonID = Person.PersonID
INNER JOIN PersonItemKey ON PersonItemValue.PersonItemKeyID = PersonItemKey.PersonItemKeyID

The problem is that this would return 3 rows, each with the same PersonID, Name, DoB etc, but with different ItemName and Value fields (One for Socks, one for Jumpers and one for Shirts).

Is it possible to return a single row instead and promote the keys to field names and values to its value. i.e. make it so that "Socks" becomes a field name and 10 is its value, "Jumpers" is another field with 6 as its value, and "Shirts" as another field with a value of 8?

I've managed to do this in code in PHP like this:

$sql = "SELECT * 
    FROM Person
    LEFT JOIN PersonItemValue ON PersonItemValue.PersonID = Person.PersonID
    INNER JOIN PersonItemKey ON PersonItemValue.PersonItemKeyID = PersonItemKey.PersonItemKeyID";

// Run the query
$query = $db->query($sql);

// Convert it to a map
$allrows = array();
while($row = $query->fetch(PDO::FETCH_ASSOC))
{
    // Create the entry if it doesn't exist
    $key = $row['PersonID'];
    if (isset($allrows[$key]) == false)
    {
        $allrows[$key] = $row;
    }

    // Turn ItemName into a field, and set its value
    $field_name = $row['ItemName'];
    $field_value = $row['Value'];

    // Put the metric into the row
    $allrows[$key][$field_name] = $field_value;
}

But it'd be better to do it in the query itself.

Is this possible?

Thanks in advance.

P.S. I don't believe that this is a duplicate of MySQL pivot row into dynamic number of columns as that uses Aggregate functions (count). At least I couldn't get it to work in my case.

Community
  • 1
  • 1
BenS1
  • 183
  • 2
  • 11
  • This is called dynamic pivoting or dynamic cross tabulated (crosstab) query. See the SO topic I linekd in the previous comment for general solution to this question. – Shadow Feb 09 '16 at 16:20
  • Thanks Shadow, however I can't work out how to get that other SO Topic to apply in my case. It only seems to work because they are using aggregate function (count), but I'm not. Maybe I'm misunderstanding something, but I can't get it to work in my case. – BenS1 Feb 09 '16 at 20:36
  • Ah ha, I think I managed to get it to work using max instead of count. Actually, max or min should work as there's only one value for each field, but count obviously wont work in this case as I want the actual value itself. Thanks again. – BenS1 Feb 09 '16 at 21:00
  • Sum works too and is probably the most logical choice. – BenS1 Feb 09 '16 at 21:04

0 Answers0