Converting rows to columns and keeping data pairs - sql

I have the following problem in MSSQL: I have a table, which contains 4 columns.
Example table:
JunctionId | type| color| value
1 | a | red | 5|
1 | b | green | 10|
2 | a | orange | 40|
2 | b | yellow | 35|
3 | a | blue | 6|
3 | b | cyan | 9|
Now, I'd like the following result:
1 | a | red | 5 | b | green | 10
2 | a | orange | 40 | b | yellow | 35
3 | a | blue | 6 | b | cyan | 9
I tried using PIVOT, but it was returning multiple rows because of the different values. I would use selfjoin, but I have 12 different 'type'. Any ideas would be very welcomed!
(note: I can't use this stackoverflow table thingy... sorry)

Self join time
select a1.junctionid,
a1.type as a_type,
a1.color as a_color,
a1.value as a_value,
a2.type as b_type,
a2.color as b_color,
a2.value as b_value
from MyTable a1
inner join MyTable a2
on a1.junctionid = a2.junctionid
where a1.type = 'a'
and a2.type = 'b'

Related

SQL to add position depending on multiple columns

I have a table that I am adding a position column in. I will need to add a numbered position to all rows already in the table. The numbering depends on 4 columns that would match each other between rows. For example
id| name| fax | cart| area |
1| jim | 1 | 4 | 1 |
2| jim | 1 | 4 | 1 |
3| jim | 2 | 4 | 1 |
4| jim | 2 | 4 | 1 |
5| bob | 1 | 4 | 1 |
6| bob | 1 | 4 | 1 |
7| bob | 2 | 5 | 1 |
8| bob | 2 | 5 | 2 |
9| bob | 2 | 5 | 2 |
10| bob | 2 | 5 | 2 |
would result with
id| name| fax | cart| area | position
1| jim | 1 | 4 | 1 | 1
2| jim | 1 | 4 | 1 | 2
3| jim | 2 | 4 | 1 | 1
4| jim | 2 | 4 | 1 | 2
5| bob | 1 | 4 | 1 | 1
6| bob | 1 | 4 | 1 | 2
7| bob | 2 | 5 | 1 | 1
8| bob | 2 | 5 | 2 | 1
9| bob | 2 | 5 | 2 | 2
10| bob | 2 | 5 | 2 | 3
I need an sql query that will iterate over the table and add the position.
Use row_number():
select
t.*,
row_number() over(partition by name, fax, cart, area order by id) position
from mytable t
If you wanted an update query:
update mytable as t
set position = rn
from (
select id, row_number() over(partition by name, fax, cart, area order by id) rn
from mytable
) x
where x.id = t.id

Is it possible to write a SQL for all matchs in a triple join with no unions?

I'd like to perform a SQL join for the grey painted region of the following Venn diagram, but using no UNIONs:
Please, is it possible?
For the sake of simplicity, think all tables with only two columns: id and value, id is the same for all of them.
Here's a sample data:
Set A
| id | value |
----------------
| 1 | red |
| 2 | magenta |
| 3 | white |
| 4 | yellow |
Set B
| id | value |
----------------
| 2 | magenta |
| 3 | white |
| 5 | blue |
| 6 | cyan |
Set C
| id | value |
----------------
| 7 | green |
| 3 | white |
| 4 | yellow |
| 6 | cyan |
Resulting set
| id | value |
----------------
| 2 | magenta |
| 3 | white |
| 4 | yellow |
| 6 | cyan |
You can full join twice and add conditional logic in the where clause:
select id
from a
full join b using(id)
full join c using(id)
where
case when a.id is not null then 1 else 0 end
+ case when b.id is not null then 1 else 0 end
+ case when c.id is not null then 1 else 0 end
> 1
Not all databases support that syntax. A more widely supported approach is indeed to use union all and aggregation:
select id
from (
select id from a
union all select id from b
union all select id from c
) t
group by id
having count(*) > 1

Set multiple column values from previous row using Postgres SQL

Assume you have a series of responses from people on what their favorite colors are. This information is stored in a SQL table:
| id | favorite_color | friend_recommendation_id |
|----|----------------|--------------------------|
| 1 | green | |
| 2 | blue | |
| 3 | yellow | |
| 4 | green | |
| 5 | yellow | |
| 6 | green | |
My goal is to write a Postgres SQL query that would fill the friend_recommendation column with the id of the most recent person to respond with the same color as the provided individual. This would result in the following table:
| id | favorite_color | friend_recommendation_id |
|----|----------------|--------------------------|
| 1 | green | |
| 2 | blue | |
| 3 | yellow | |
| 4 | green | 1 |
| 5 | yellow | 3 |
| 6 | green | 4 |
Note that id 6 is filled with 4 and not 1
I've tried using variables and subselects, but am struggling with how to apply the select for each result from the parent query.
Use a subquery to calculate the field
SQL Fiddle Demo
SELECT "id", "favorite_color", (SELECT MAX("id")
FROM colors c2
WHERE c2."favorite_color" = c1."favorite_color"
AND c2."id" < c1."id"
) as friend_recommendation_id
FROM colors c1
OUTPUT
| id | favorite_color | friend_recommendation_id |
|----|----------------|--------------------------|
| 1 | green | (null) |
| 2 | blue | (null) |
| 3 | yellow | (null) |
| 4 | green | 1 |
| 5 | yellow | 3 |
| 6 | green | 4 |
Can also be write like this:
SELECT c1."id", c1."favorite_color", MAX(c2."id") as friend_recommendation_id
FROM colors c1
LEFT JOIN colors c2
ON c2."favorite_color" = c1."favorite_color"
AND c2."id" < c1."id"
GROUP BY c1."id", c1."favorite_color"
ORDER BY c1."id";
UPDATE
UPDATE colors target
SET "friend_recomendation_id" = ( SELECT MAX("id")
FROM colors c2
WHERE c2."favorite_color" = target."favorite_color"
AND c2."id" < target."id")

SQL: Max of summed list from subgroup

I've been looking around for a solution for this, and found many questions about finding a max of a summed group, but none of them solving my problem, hence I decided to make a new question.
My data is grouped in 3 levels and data would be like this:
+--------+---------+----+
| Sektor | Sektion | n |
+--------+---------+----+
| 1 | a | 9 |
| 1 | b | 14 |
| 1 | a | 6 |
| 2 | d | 4 |
| 2 | d | 7 |
| 2 | f | 10 |
| 2 | e | 100|
| 3 | g | 59 |
| 4 | h | 200|
+--------+---------+----+
I would like to find the "sektion" with highest summed n for each "sektor".
I tried some different approaches, but none of them solved my problem. The closest i got was:
select
sektor, sektion, n
from
table
where
n = (select max(n) from table i where i.sektor = table.sektor)
GROUP BY sektor, sektion, n
ORDER BY n DESC
This would return
+--------+---------+----+
| Sektor | Sektion | n |
+--------+---------+----+
| 1 | b | 14 |
| 2 | e | 100|
| 3 | g | 59 |
| 4 | h | 200|
+--------+---------+----+
The problem is i don't get the max(n) for each group, but single instance. Sektor 1 should return sektion a with 15 instead.
Am i close to the answer, or far away? Seems like i just need to sum before taking the max, but not sure how.
Desired:
+--------+---------+----+
| Sektor | Sektion | n |
+--------+---------+----+
| 1 | a | 15 |
| 2 | e | 100|
| 3 | g | 59 |
| 4 | h | 200|
+--------+---------+----+
You could try the below, this works for me
Select Distinct
sektor, Sektion, n
from
table t
where
n = (select max(n) from table where Sektor = t.Sektor)
Looking at your question again, it looks like all you were missing was a group by in your subquery
Also as your comment suggests that you want the max based on the sum, you may find this works for that scenario:
Select Sektor, Sektion, SUM(n) n
from Table t
Group by Sektor, Sektion
Having Sum(n) = (Select max(n) from (Select SUM(n) n from Table where Sektor = t.Sektor group by Sektion) a)

Relational Algebra - Divide

If I have the following tables and I perform R1/R2 in relational algebra, would the result be a table with A values 1 and 3? I am a bit confused as I know 3 would be a result as it contains both 5 and 1, but the result 1 has additional values for B aside from the matching ones so would this also be included and why?
R1 R2
+---+---+ +---+
| A | B | | B |
|---|---| |---|
| 1 | 1 | | 5 |
| 1 | 2 | | 1 |
| 1 | 3 | +---+
| 1 | 4 |
| 2 | 3 |
| 2 | 4 |
| 3 | 5 |
| 3 | 1 |
| 1 | 5 |
| 5 | 7 |
| 5 | 8 |
+---+---+
In relational databases Divide is defined as:
R1(Y,X) DIVIDE R2(X) = R1[Y] MINUS ((R1[Y] TIMES R2) MINUS R1)[Y]
remember that R1[Y] is another form of "PROJECT R1 over Y".
so the result is {1,3}