This is indeed a pivot, also called a crosstab, or sometimes transpose
Some databases have dedicated facilities for doing it, others you have to use a grouping syntax. I prefer the latter because it works universally
If it's any consolation, you were really close!
SELECT
DIM_KEY,
MAX(CASE WHEN ATTR_NAME = 'UPC' THEN VALUE END) as UPC,
MAX(CASE WHEN ATTR_NAME = 'DAIRY_CLM' THEN VALUE END) as DAIRY_CLM,
MAX(CASE WHEN ATTR_NAME = 'KOSHER_CLM' THEN VALUE END) as KOSHER_CLM,
MAX(CASE WHEN ATTR_NAME = 'FAT' THEN VALUE END) as FAT,
MAX(CASE WHEN ATTR_NAME = 'CALORIES' THEN VALUE END) as CALORIES
FROM demo
GROUP BY DIM_KEY
How does it work?
Well, if you run the non-grouped, no-max-functions version that you already had:
SELECT
DIM_KEY,
(CASE WHEN ATTR_NAME = 'UPC' THEN VALUE END),
(CASE WHEN ATTR_NAME = 'DAIRY_CLM' THEN VALUE END),
(CASE WHEN ATTR_NAME = 'KOSHER_CLM' THEN VALUE END),
(CASE WHEN ATTR_NAME = 'FAT' THEN VALUE END),
(CASE WHEN ATTR_NAME = 'CALORIES' THEN VALUE END)
FROM
demo
Then you'll see your data become "diagonal":
3005, 123423, null, null...
3005, null, N, null...
3005, null, null, Y ...
In each column (per dim_key) there is only one value, the rest are NULL
Adding in the GROUP BY and MAX causes these to collapse into a single row because MAX() will return just the value from the column and make all the nulls disappear. It's an intrinsic property of a grouping, that the row data doesnt "stay together" - within the group of a particular DIM_KEY, the MAX(DAIRY_CLM) can come from any row, the MAX(KOSHER_CLM) can come from any other row.. In practice this means that the single values are picked, the nulls are discarded, they all appear on the same row..
..and thus your vertical data went horizontal, after going through the diagonal