How can we pivot in this scenario in MySQL - mysql

I have this table:
+----------+--------+-----+--------+--------+
| personId | Name | Age | height | weight |
+----------+--------+-----+--------+--------+
| 1 | Aritra | 20 | 5.6 | 56 |
| 1 | Aritra | 30 | 5.6 | 76 |
+----------+--------+-----+--------+--------+
But require the output to be like this:
+---------------+----------+--------------+----------+
| AttributeName | personId | Presentvalue | OldValue |
+---------------+----------+--------------+----------+
| Height | 1 | 5.6 | 5.6 |
| weight | 1 | 76 | 56 |
+---------------+----------+--------------+----------+
I tried to get this using pivot but am facing a problem. How can I do this, with or without using pivot?
Thank you.

union all is probably the simplest method:
select 'Height' as attributeName, personId,
(case when age = 30 then height end) as newValue,
(case when age = 20 then height end) as oldValue
from t
union all
select 'Weight' as attributeName, personId,
(case when age = 30 then weight end) as newValue,
(case when age = 20 then weight end) as oldValue;

Related

Leetcode Rearrange Products Table Medium Level

I saw one answer in discussion as below
SELECT A.product_id, B.COLUMN_NAME as store,
CASE
WHEN B.COLUMN_NAME = "store1" THEN store1
WHEN B.COLUMN_NAME = "store2" THEN store2
WHEN B.COLUMN_NAME = "store3" THEN store3
END AS price
FROM Products A, INFORMATION_SCHEMA.COLUMNS B
WHERE table_name = 'Products' AND column_name != "product_id"
HAVING price IS NOT NULL
Can someone please explain how can we write this INFORMATION_SCHEMA and could still get the output. This is in MYSQL
A couple of techniques are used to pull this off:
Cross Join: Instead of joining the two tables on a key, instead the OOP is joining every record of Products with every records of INFORMATION_SCHEMA.COLUMNS (where the column_name isn't product_id). Essentially they are making an intermediate result set that looks like:
+------------+--------+--------+--------+------------+-------------+
| product_id | store1 | store2 | store3 | table_name | column_name |
+------------+--------+--------+--------+------------+-------------+
| 0 | 95 | 100 | 105 | Products | store3 |
| 0 | 95 | 100 | 105 | Products | store2 |
| 0 | 95 | 100 | 105 | Products | store1 |
| 1 | 70 | | 80 | Products | store3 |
| 1 | 70 | | 80 | Products | store2 |
| 1 | 70 | | 80 | Products | store1 |
+------------+--------+--------+--------+------------+-------------+
You can see each row of products is triplicated as there are three rows of column metadata in INFORMATION_SCHEMA.COLUMNS.
A Case Expression to generate a new column on this intermediate result set that pulls the correct store column's int value based on the INFORMATION_SCHEMA.COLUMNS.column_name for that row:
SELECT A.*, B.COLUMN_NAME as store,
CASE
WHEN B.COLUMN_NAME = "store1" THEN store1
WHEN B.COLUMN_NAME = "store2" THEN store2
WHEN B.COLUMN_NAME = "store3" THEN store3
END AS price
FROM Products A, INFO_SCHEMA B
WHERE table_name = 'Products' AND column_name != "product_id"
HAVING price IS NOT NULL;
+------------+--------+--------+--------+--------+-------+
| product_id | store1 | store2 | store3 | store | price |
+------------+--------+--------+--------+--------+-------+
| 0 | 95 | 100 | 105 | store3 | 105 |
| 0 | 95 | 100 | 105 | store2 | 100 |
| 0 | 95 | 100 | 105 | store1 | 95 |
| 1 | 70 | | 80 | store3 | 80 |
| 1 | 70 | | 80 | store1 | 70 |
+------------+--------+--------+--------+--------+-------+
I included the original columns from Products table to show what the case expression is doing.
The sneaky HAVING clause tossed on at the end. Because the price columns derived by the case expression doesn't exist at the point of query execution when the WHERE clause is executed, OOP snuck in this HAVING clause which evaluates near the end of the sql execution steps when the price column was made into existence. The reason I say it's sneaky is because HAVING without a GROUP BY or any aggregation at all is an oddball and may fail in other RDBMS (but I'd have to test).

SQL query to return specific labels if exist (0 if it does not exist)

There are two tables given, tag and media.
mysql> select * from media;
+----+---------+----------+
| id | name | duration |
+----+---------+----------+
| 1 | cat.mp4 | 3.4 |
| 2 | dog.mp4 | 8 |
+----+---------+----------+
mysql> select * from tag;
+----+----------+-------+--------+------------+
| id | media_id | type | value | confidence |
+----+----------+-------+--------+------------+
| 1 | 1 | LABEL | cat | 0.9 |
| 2 | 1 | LABEL | person | 0.6 |
| 3 | 1 | TEXT | kitty | 0.95 |
| 4 | 2 | LABEL | dog | 0.8 |
| 5 | 2 | LABEL | person | 0.75 |
| 6 | 2 | TEXT | food | 0.7 |
+----+----------+-------+--------+------------+
I need to get the output table by joining two tables that gives media_id, name, duration and label of the value from tag such that if the value is cat, the confidence of cat will be inserted into label_cat column otherwise 0 will be inserted.
Something like this:
+----------+---------+----------+-----------+-----------+--------------+
| media_id | name | duration | label_cat | label_dog | label_person |
+----------+---------+----------+-----------+-----------+--------------+
| 1 | cat.mp4 | 3.4 | 0.9 | 0 | 0.6 |
| 2 | dog.mp4 | 8 | 0 | 0.8 | 0.75 |
+----------+---------+----------+-----------+-----------+--------------+
If I understand correctly, you want conditional aggregation:
select m.id, m.name, m.duration,
max(case when t.value = 'cat' then t.confidence end) as label_cat,
max(case when t.value = 'dog' then t.confidence end) as label_dog,
max(case when t.value = 'person' then t.confidence end) as label_person
from media m left join
tag t
on m.id = t.media_it
group by m.id, m.name, m.duration
Select t.media_id,m.name,m.duration,
case "label_cat " when t.value ='cat' then
t.confidence else 0 end case,
case "label_dog" when t.value ='dog' then
t.confidence else 0 end case,
case "label_person" when t.value ='person' then
t.confidence else 0 end case
from
tag t right join media m on t.id=m.id
group by t.media_id

efficient query to group by one field and put all other fields in the row SQL

Imagine the result of a query is something like the following:
+----+---------+--------+
| id | count | type |
+----+---------+--------+
| 1 | 20 | a |
| 1 | 30 | b |
| 1 | 10 | c |
| 2 | 05 | a |
| 2 | 20 | b |
| 2 | 40 | c |
+----+---------+--------+
and the expected result:
+----+---------+--------+------+
| id | a | b | c |
+----+---------+--------+------+
| 1 | 20 | 30 | 10 |
| 2 | 05 | 20 | 40 |
+----+---------+--------+------+
I know some solutions which are complex using Cursor, Variables, Join and etc. I would like to find the most efficient one, otherwise I will handle it from the application layer.
One method uses conditional aggregation:
select id,
sum(case when type = 'a' then count else 0 end) as a,
sum(case when type = 'b' then count else 0 end) as b,
sum(case when type = 'c' then count else 0 end) as c
from t
group by id;

MySQL select calculate 2 fields based on other field

Let's say i have a table like this
| user | symbol | status | value |
----------------------------------
| 101 | A | 1 | 20 |
| 102 | A | 1 | 20 |
| 103 | A | 1 | 20 |
| 101 | A | 0 | 20 |
| 102 | B | 1 | 20 |
| 103 | A | 1 | 20 |
| 101 | A | 0 | 20 |
| 102 | A | 1 | 20 |
| 103 | A | 0 | 20 |
| 101 | B | 1 | 20 |
| 102 | A | 0 | 20 |
and i want the result like this (all value change to minus that have status = 0)
| user | symbol | differences |
----------------------------------
| 101 | A | -20 |
| 101 | B | 20 |
| 102 | A | 20 |
| 102 | B | 20 |
| 103 | A | 20 |
Please help, any help would be appreciated!
Use a CASE expression for changing the value to minus if status = 0.
Query
select `user`, `symbol`,
sum(case when `status` = 0 then `value` * -1 else `value` end) as differences
from `your_table_name`
group by `user`, `symbol`
order by `user`, `symbol`;
SQL Fiddle demo
A generic solution using CASE..WHEN below:
SELECT user,
symbol,
CASE WHEN status > 0 THEN value
ELSE -value END as differences
FROM yourTable;
Though in this particular case, you can also use math:
SELECT user,
symbol,
(value * (2*status-1)) as differences
FROM yourTable;
If you also want to aggregate the results, then you can change the queries above to use a GROUP BY with SUM as the aggregation:
SELECT user,
symbol,
SUM(CASE WHEN status > 0 THEN value
ELSE -value END) as differences
FROM yourTable
GROUP BY user, symbol;
And the equivalent aggregated query, using the arithmetic:
SELECT user,
symbol,
SUM(value * (2*status-1)) as differences
FROM yourTable
GROUP BY user, symbol;

How can i query table within table?

How can i make a query that takes everything from one table then joins another table and put the values from the second table in a certain column in the result
What i am asking can be better explained:
clients:
id | name | age | ...
---------------------
15 | something | 30 |
17 | somethiaa | 30 |
13 | ggggthing | 30 |
clients_meta:
id | client_id | meta_key | meta_value |
-----------------------------------------
1 | 15 | location | NY |
2 | 15 | height | 195 |
3 | 15 | job | student |
4 | 13 | location | TN |
This is my current query:
SELECT
`clients`.*,
`clients_meta`.*
FROM `clients`
JOIN clients_meta ON ( clients_meta.client_id = clients.id )
WHERE
`clients_age` = '30'
how can instead of a ugly table like that:
15 | something | 30 | 1 | 15 | location | NY |
15 | something | 30 | 2 | 15 | height | 195 |
15 | something | 30 | 3 | 15 | job | student |
change it to something like:
15 | something | 30 | 1 | 15 | location | NY |
| 2 | 15 | height | 195 |
| 3 | 15 | job | student |
thanks
You can use a variable to check whether the last id is equal to the current id, and in that case output null or '' instead.
select
case when c.ClientId <> #clientid then c.Name else '' end as ClientName,
case when c.ClientId <> #clientid then #ClientId := c.ClientId else '' end as ClientId,
p.ContactId,
p.Name as ContactName
from
Clients c
inner join Contacts p on p.ClientId = c.Clientid
, (select #clientid := -1) x
order by
c.ClientId, p.ContactId
Example: http://sqlfiddle.com/#!2/658e4c/6
Note, this is a bit hacky. I deliberately made ClientId the second field, so I could change and return the clientId variable in the same field. In other, more elaborate cases, you may have to do that in a separate field. But to eliminate that placeholder field from the result, you'll have to embed the whole query in a subselect, and define the wanted fields in the right order on the top level query.
You could choose one value in clients_meta.meta_key to always be first like 'location'. Then you can sort by clients.id, then by whether or not meta_key = 'location'. Any rows where meta_key != 'location', you can hide, like this:
select
case when clients_meta.meta_key = 'location'
then clients.id else '' end as id
, case when clients_meta.meta_key = 'location'
then clients.name else '' end as name
, case when clients_meta.meta_key = 'location'
then clients.age else '' end as age
, clients_meta.*
from clients join clients_meta on (clients_meta.client_id = clients.id)
where clients.age = '30'
order by clients.id, clients_meta.meta_key = 'location' desc;
You will obtain the results that you wanted:
+------+-----------+------+----+-----------+----------+------------+
| id | name | age | id | client_id | meta_key | meta_value |
+------+-----------+------+----+-----------+----------+------------+
| 13 | ggggthing | 30 | 4 | 13 | location | TN |
| 15 | something | 30 | 1 | 15 | location | NY |
| | | | 3 | 15 | job | student |
| | | | 2 | 15 | height | 195 |
+------+-----------+------+----+-----------+----------+------------+