how to get a table with columns named after key in mapping table - sql

I'd like a "flat" representation of a table and a corresponding mapping table with key-value pairs, where the keys are used as columns in the result.
E.g. given I have a table with a one to manny relation like this:
Table Items(ID, Name)
| ID | Name |
|----|-------|
| 1 | Item1 |
| 2 | Other |
Table KeyValue(ID, Key, Value, ItemID[FK->Items])
| ID | Key | Value | ItemID[FK->Items] |
|----|------|--------|-------------------|
| 1 | key1 | value1 | 1 |
| 2 | key2 | value2 | 1 |
| 3 | key3 | value3 | 1 |
| 4 | key1 | valueX | 2 |
| 5 | key3 | valueY | 2 |
| 6 | key4 | valueZ | 2 |
how would I join/process the tables to get a table like this:
| ID | Name | key1 | key2 | key3 | key4 |
|----|-------|--------|--------|--------|--------|
| 1 | Item1 | value1 | value2 | value3 | NULL |
| 2 | Other | valueX | NULL | valueY | valueZ |
EDIT:
The keys would be variable irl.
In the real world I'm trying to implement a garden-database.
There I have a mapping table "dates" with important dates for the plants - e.g.
Table Dates(ID, Event, Date, PlantID[FK->Plants])
so if I add a Date with an event to a plant, and another plant has a date with the same event-name the event should be one column.
| Plant | planted | harvested | cut |
| Tree | 01/01/2018 | 01/09/2019| 02/09/2019 |
| Tree2 | 01/01/2017 | - | 02/09/2019 |
if I'd add a "harvested"-Date to Tree2 it should appear in column "harvested".
If I'd add a "newEvent"-Date to Tree2 there should be an additional column "newEvent" with NULL entry for Tree1.
…but I do not know yet, what possible events will be stored.

use conditional aggregation
select t1.id,name
max(case when t2.key='key1' then value end),
max(case when t2.key='key4' then value end),
max(case when t2.key='key2' then value end),
max(case when t2.key='key3' then value end)
from Items t1 join KeyValue t2 on t1.id=t2.ItemID
group by t1.id,name

select t1.ID, t1.Name,
max(case when t2.Kee = "key1" then t2.Value end) as key1,
max(case when t2.Kee = "key2" then t2.Value end) as key2,
max(case when t2.Kee = "key3" then t2.Value end) as key3,
max(case when t2.Kee = "key4" then t2.Value end) as key4
from Items t1
join KeyValue t2 on t1.ID = t2.ItemID
group by t1.ID, t1.Name

Related

How to get duplicate columns in multiple columns and single row in SQL

Below is the original table
+----------+---------+
| Trade | Key |
+----------+---------+
| A | 1 |
| A | 2 |
| A | 3 |
| B | 1 |
| B | 2 |
| B | 3 |
+----------+---------+
Below is the results i need
+----------+---------+---------+---------+
| Trade | Key1 | Key2 | Key3 |
+----------+---------+---------+---------+
| A | 1 | 2 | 3 |
| B | 1 | 2 | 3 |
+----------+---------+---------+---------+
Any pointers to the SQL code is appreciated.
Thanks in advance
Sarge
Look for PIVOT.
It is sql function that allows you to do exactly what you need.
The downside with PIVOT is that not all DBs support it. Make sure your does.
Look at the following answer for further explanation: https://stackoverflow.com/a/15931734/16462128
I think you want conditional aggregation like this:
select trade,
max(case when seqnum = 1 then key end) as key_1,
max(case when seqnum = 2 then key end) as key_2,
max(case when seqnum = 3 then key end) as key_3
from (select t.*,
row_number() over (partition by trade order by key) as seqnum
from t
) t
group by trade;
You will need to explicit list the number of columns to be sure you get all keys for all trades (your example data has three).

SQL transform distinct rows into one row and multiple columns

I have a table and rows with the same Name can occur like up to 5 times. Example:
| Name | Value |
|--------|---------|
| Test | Value1 |
| Test | Value2 |
| Test | Value3 |
| FooBar | Value11 |
| FooBar | Value12 |
I am trying to create a query to compress the rows to have a unique Name and transfer the values to columns. If there are less than 5 values per name the remaining columns should have NULL.
| Name | Col1 | Col2 | Col3 | Col4 | Col5 |
|--------|---------|---------|--------|------|------|
| Test | Value1 | Value2 | Value3 | NULL | NULL |
| FooBar | Value11 | Value12 | NULL | NULL | NULL |
I looked at Pivot but I don't have a column to aggregate.
I need this format for csv file.
Using SQL Server 2016.
You can construct a column using row_number():
select name,
max(case when seqnum = 1 then value end) as value_1,
max(case when seqnum = 2 then value end) as value_2,
max(case when seqnum = 3 then value end) as value_3,
max(case when seqnum = 4 then value end) as value_4,
max(case when seqnum = 5 then value end) as value_5
from (select t.*,
row_number() over (partition by name order by value) as seqnum
from t
) t
group by name;

Creating a view to transpose a column with special conditions

I have a table like this:
key | attr | value
----+---------+-------------
a | status1 | complete
a | status2 | incomplete
a | zipcode | 12345
b | status1 | complete
b | status2 | complete
b | zipcode | 54321
and I want to pivot the chart to look like this
key | status | status_value | zipcode
----+---------+--------------+----------
a | status1 | complete | 12345
a | status2 | incomplete | 12345
b | status1 | complete | 54321
b | status2 | incomplete | 54321
but with an arbitrary number of statuses (i.e I can have status3, status4, and each key can have unique number of statuses).
Is there a way to create a view in this manner?
You can use window functions:
select key, value, attr, zipcode
from (select t.*,
max(case when attr = 'zipcode' then value end) over (partition by key) as zipcode
from t
) t
where attr <> 'zipcode'
Yes, there is.
select t1.key,
t1.attr,
t1.value,
t2.value as zipcode
from tableName t1
join tableName t2 on t1.key = t2.key
and t2.attr = 'zipcode'
where t1.attr <> 'zipcode';

How to make 2 columns from one in one select in sqlite?

I've got one database with two columns (id and value). There are two types of values and each id has both of this values. How can I make a select to this database to have three columns in result (id, value1 and value2)
I've tried CASE and GROUP BY, but it shows only one result of each id
Example of a db:
| id | value |
| 0 | a |
| 0 | b |
| 1 | a |
| 1 | b |
Example of the result I am looking for is:
| id | value_a | value_b |
| 0 | a | b |
| 1 | a | b |
UPDATE:
As it was noted in comments, there is too simple data in the example.
The problem is more complicated
An example that would better describe it:
DB:
| id | value | value2 | value3 |
| 0 | a | a2 | a3 |
| 0 | b | b2 | b3 |
| 1 | a | c2 | c3 |
| 1 | b | d2 | d3 |
RESULT:
| id | value_a | value_b | value2_a | value2_b | value3_a | value3_b |
| 0 | a | b | a2 | b2 | a3 | b3 |
| 1 | a | b | c2 | d2 | c3 | d3 |
The output should be sorted by id an have all info from the both rows of each id.
If there are always two values per ID, you can try an aggregation using min() and max().
SELECT id,
min(value) value_a,
max(value) value_b
FROM elbat
GROUP BY id;
select t0.id,t0.Value as Value_A, t1.Value as Value_B
from test t0
inner join test t1 on t0.id = t1.id
where t0.Value = 'a' and t1.value = 'b';
I have used this method to turn "rows" into "columns". Depending on the number of unique values that exist in the table, you may or may not want to use this :)
SELECT id, SUM(CASE WHEN value = "a" then 1 else 0 END) value_a,
SUM(CASE WHEN value = "b" then 1 else 0 END) value_b,
SUM(CASE WHEN value = "c" then 1 else 0 END) value_c,
SUM(CASE WHEN value ="a2" then 1 else 0 END) value_a2,
.
.
.
FROM table
GROUP BY id;
Thanks all for the answers! This is the way how I did this:
WITH a_table AS
(
SELECT id, value, value2, value3 FROM table1 WHERE table1.value = 0
),
b_table AS
(
SELECT id, value, value2, value3 FROM table1 WHERE table1.value = 1
)
SELECT DISTINCT
a_table.id AS id,
a_table.value AS value_a,
a_table.value2 AS value2_a,
a_table.value3 AS value3_a,
b_table.value AS value_b,
b_table.value2 AS value2_b,
b_table.value3 AS value3_b
FROM a_table
JOIN b_table ON a_table.id = b_table.id
GROUP BY id;

Oracle SQL group data by value

I have this tables:
TABLE_A
ID | ATTR_NAME | ATTR_ID | START_DATE
-------------------------------------
1 | XPTA | 12 | 01-10-2013
1 | XPTO | 167 | 01-10-2013
1 | XPTA | 13 | 04-12-2013
2 | XPTA | 12 | 03-09-2015
2 | XPTO | 6 | 05-08-2012
TABLE_B (just to help understand)
ATTR_NAME | ATTR_ID | DESCRIPTION
---------------------------------
XPTA | 12 | ASM5
XPTO | 167 | weird attr
XPTA | 13 | DBSO12
XPTO | 6 | gosh...
Is there a way to make a select return this?
ID | XPTA | XPTO | START_DATE
-----------------------------
1 | 12 | 167 | 01-10-2013
1 | 13 | | 04-12-2013
2 | 12 | | 03-09-2015
2 | | 6 | 05-08-2012
Basically, the COL1 represents an attribute and COL2 the id value of COL1 associated to the ID starting from START_DATE
Example: Resource Mike (ID) has a cost profile (COL1) of ASM5 (COL2) starting from 01-10-2013 (START_DATE)
The ideia is to group the attributes by date for each ID.
The only way I see I can do it e first do a select start_date distinct from TABLE_A and then for each date fetched do a select ID, COL1, COL2, :date_start from TABLE_A where START_DATE = :date_start and then somehow make COL1 values my columns and COL2 values my values.
Assuming I'm understanding your question correctly, you're looking for conditional aggregation, something you can do with max and case:
select id,
max(case when attr_name = 'XPTA' then attr_id end) xpta,
max(case when attr_name = 'XPTO' then attr_id end) xpto,
start_date
from table_a
group by id, start_date