One row result with multiple join in MySQL - mysql

I have 3 tables like the following.
Table "mansioni":
id_mansione | desc_mansione
1 | production
2 | office
3 | transport
Table "dipendente": store id, name and surname:
id_dip | nome_dip | cognome_dip
1 | piero | rossi
2 | marco | rossi
Table dipendenti_iddip: store the association between "dipendente" and table "mansioni"
iddip_mansione | num_mansione | id_mansione
1 | 1 | 1
1 | 2 | 2
2 | 1 | 2
2 | 2 | 3
Now I need a query that give me a result like this:
id_dip | nome_dip | cognome_dip | mansione1 | mansione2 | mansione3
1 | piero | rossi | production| office |
2 | marco | rossi | office | transport |
I arrived to the following query but with this I can only see the "id_mansione" and not the "desc mansione" field
select i.id_dip,
i.nome_dip,
i.cognome_dip,
max(case when t.num_mansione='1' then t.id_mansione end) Mansione1,
max(case when t.num_mansione='2' then t.id_mansione end) Mansione2,
max(case when t.num_mansione='3' then t.id_mansione end) Mansione3
from dipendente i
left join dipendenti_iddip t
on i.id_dip = t.iddip_mansione
group by i.id_dip, i.nome_dip, i.cognome_dip
How can I arrive to my result?
Thanks...

Add join on mansioni and replace t.id_mansione with m.desc_mansione
select i.id_dip,
i.nome_dip,
i.cognome_dip,
max(case when t.num_mansione = '1' then m.desc_mansione end) Mansione1,
max(case when t.num_mansione = '2' then m.desc_mansione end) Mansione2,
max(case when t.num_mansione = '3' then m.desc_mansione end) Mansione3
from dipendente i
join dipendenti_iddip t
on i.id_dip = t.iddip_mansione
join mansioni m on m.id_mansione = t.id_mansione
group by i.id_dip

Related

Flatten a response to an SQL query

I am trying to get a flat table for a question and answer table with mysql. This are my tables.
PollQuestion
PollID | Question| A | B | C | D | E | TimeStamp
PollReponse
PollReponseID | PollID | UserID | Answer
In the PollAnswer table I get Five answers as VARCHAR A, B, C, D, E.
I have written a query to group the answers by A, B, C, D, E.
select q.Question
, q.PollID
, r.Answer
, count(r.Answer)
from pollQuestions q
, pollResponse r
where q.PollID = r.PollID
group
by r.Answer
, q.Question
, q.PollID
order
by r.PollID;
Which gives me a response as follows.
Question | PollID | Answer | count
alpha | 1 | A | 2
alpha | 1 | B | 3
alpha | 1 | C | 4
alpha | 1 | D | 0
alpha | 1 | E | 0
betas | 2 | A | 3
betas | 2 | B | 4
betas | 2 | C | 4
betas | 2 | D | 6
betas | 2 | E | 0
I want to flatten the answer like this.
Question | PollID | countA | countB | countC | countD | countE
alpha | 1 | 2 | 2 | 4 | 0 | 0
betas | 2 | 3 | 4 | 4 | 6 | 0
Is there anyway I can achieve this without changing the table structure?
Any pointer would be appreciated.
You can try to use condition aggregate function.
select
pollQuestions.Question,
pollQuestions.PollID,
count(CASE WHEN pollResponse.Answer ='A' THEN 1 END) countA,
count(CASE WHEN pollResponse.Answer ='B' THEN 1 END) countB,
count(CASE WHEN pollResponse.Answer ='C' THEN 1 END) countC,
count(CASE WHEN pollResponse.Answer ='D' THEN 1 END) countD,
count(CASE WHEN pollResponse.Answer ='E' THEN 1 END) countE
from pollQuestions
JOIN pollResponse on pollQuestions.PollID = pollResponse.PollID
group by
pollQuestions.Question,
pollQuestions.PollID
order by
pollResponse.PollID;
NOTE
I would use JOIN instead of comma , because JOIN syntax is clearer than , about connect two tabls.
You can use group by with sum(case when...) like below:
select pq.Question, pq.PollID,
sum(case when pr.Answer = 'A' then 1 else 0 end) as 'countA',
sum(case when pr.Answer = 'B' then 1 else 0 end) as 'countB',
sum(case when pr.Answer = 'C' then 1 else 0 end) as 'countC',
sum(case when pr.Answer = 'D' then 1 else 0 end) as 'countD',
sum(case when pr.Answer = 'E' then 1 else 0 end) as 'countE'
from PollQuestion pq
left join PollResponse pr on pq.PollID = pr.PollID
group by pq.Question, pq.PollID

MySQL Select Case - Comparison Between Two Tables

I have two tables similar to these:
Table: case
---------------------------------------------------
| id | company | managed | time | client |
---------------------------------------------------
| 1 | apple | yes | 1412643785 | no |
---------------------------------------------------
| 2 | barilla | no | 1412643785 | no |
---------------------------------------------------
| 3 | google | no | 1412643785 | yes |
---------------------------------------------------
| 4 | google | yes | 1412643785 | yes |
---------------------------------------------------
| 5 | google | no | 1412643785 | yes |
---------------------------------------------------
Table: language
---------------------------
| id | company | lang |
---------------------------
| 1 | apple | EN |
---------------------------
| 2 | barilla | IT |
---------------------------
| 3 | google | EN |
---------------------------
I have create statistics/graphics from this tablet, the I extract the following information for each month:
* Number of cases of clients per month
* Number of cases managed per month
* Total number of cases per month
* Number of cases of Italian companies per month
For the first three points I don't have no problems, and I have made this query:
SELECT FROM_UNIXTIME(time, '%Y-%M') as 'Month',
COUNT(CASE WHEN client = 'yes' THEN 1 ELSE NULL END) as 'Reports of a customer,
COUNT(CASE WHEN managed = 'yes' THEN 1 ELSE NULL END) as 'Managed cases',
COUNT(id) as 'Total reports'
FROM case
GROUP BY FROM_UNIXTIME(time, '%Y-%M')
ORDER BY FROM_UNIXTIME(time, '%Y-%m')
But how do I extract the monthly number of cases managed of any Italian company?
I tried to add this portion of query but does not go...
COUNT(CASE WHEN case.company = language.company AND language.lang = 'IT' THEN 1 ELSE NULL END) as 'Italian Case',
Can you help me? Thank you
Your thought was right, just needed to join the additional table:
SELECT FROM_UNIXTIME(t.time, '%Y-%M') as 'Month',
COUNT(CASE WHEN t.client = 'yes' THEN 1 END) as 'Reports of a customer',
COUNT(CASE WHEN t.managed = 'yes' THEN 1 END) as 'Managed cases',
COUNT(t.id) as 'Total reports',
COUNT(CASE WHEN language.lang = 'IT' THEN 1 END) as 'Italian Case',
FROM case t
JOIN language ON language.company = t.company
GROUP BY FROM_UNIXTIME(t.time, '%Y-%M')
ORDER BY FROM_UNIXTIME(t.time, '%Y-%m')
Please note that I removed ELSE NULL from your cases as it is the default.

MySQL Dynamic inner join for combining multiple rows into one?

I am creating a database with the tables below, where shop_id in hours refers to a an id in shop.
Preferably I would have a query to return all data in one row, in stead of needing to post-process a lot of rows to "merge" the result from hours so the end result looks like this.
+-------------------+---------+------+---------+----------------+----------+---------------+---------------+-----+-----+-----+-----+-----+-----+-----+
| name | address | zip | city | municipal | phone | lat | lng | day | day | day | day | day | day | day |
+-------------------+---------+------+---------+----------------+----------+---------------+---------------+-----+-----+-----+-----+-----+-----+-----+
| Coop Marked Budal | false | 7298 | Budalen | Midtre gauldal | 72436410 | 62.8837013245 | 10.4836997986 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-------------------+---------+------+---------+----------------+----------+---------------+---------------+-----+-----+-----+-----+-----+-----+-----+
Right now I have come to the query below, wich I feel there must be a better alternative to. So my question is: Is there really another solution to this?
Also, I've seen concat, but I want to avoid having to split strings later on when processing the data.
select shop.name, shop.address, shop.zip, shop.city, shop.municipal, shop.phone, shop.lat, shop.lng,
h.day, hh.day, hhh.day, hhhh.day, hhhhh.day, hhhhhh.day, hhhhhhh.day
from shop
/**
As it requires a unique table name, this was the solution I found.
Could this be shortened?
**/
inner join hours h on shop.id = h.shop_id and h.day = 1
inner join hours hh on shop.id = hh.shop_id and hh.day = 2
inner join hours hhh on shop.id = hhh.shop_id and hhh.day = 3
inner join hours hhhh on shop.id = hhhh.shop_id and hhhh.day = 4
inner join hours hhhhh on shop.id = hhhhh.shop_id and hhhhh.day = 5
inner join hours hhhhhh on shop.id = hhhhhh.shop_id and hhhhhh.day = 6
inner join hours hhhhhhh on shop.id = hhhhhhh.shop_id and hhhhhhh.day = 7;
Tables
shop
+----+-------------------+---------+------+---------+----------------+----------+---------------+---------------+----------+-----------+
| id | name | address | zip | city | municipal | phone | lat | lng | chain_id | county_id |
+----+-------------------+---------+------+---------+----------------+----------+---------------+---------------+----------+-----------+
| 1 | Test | false | 1234 | Test | Test | 12341234| 0.0000 | 0.0000 | 3 | 16 |
+----+-------------------+---------+------+---------+----------------+----------+---------------+---------------+----------+-----------+
hours
+-----+----------+----------+---------+
| day | open | close | shop_id |
+-----+----------+----------+---------+
| 1 | 09:00:00 | 18:00:00 | 1 |
| 2 | 09:00:00 | 18:00:00 | 1 |
| 3 | 09:00:00 | 18:00:00 | 1 |
| 4 | 09:00:00 | 18:00:00 | 1 |
| 5 | 09:00:00 | 18:00:00 | 1 |
| 6 | 09:00:00 | 20:00:00 | 1 |
| 7 | 14:00:00 | 20:00:00 | 1 |
+-----+----------+----------+---------+
You can also use a case .. when to do the pivot, and then group by the shop fields and use an aggregate function to process the day.
select
shop.NAME, shop.address, shop.zip, shop.city, shop.municipal, shop.phone, shop.lat, shop.lng,
MAX(CASE WHEN h.DAY = 1 THEN h.DAY ELSE 0 END) AS Day1,
MAX(CASE WHEN h.DAY = 2 THEN h.DAY ELSE 0 END) AS Day2,
MAX(CASE WHEN h.DAY = 3 THEN h.DAY ELSE 0 END) AS Day3,
MAX(CASE WHEN h.DAY = 4 THEN h.DAY ELSE 0 END) AS Day4,
MAX(CASE WHEN h.DAY = 5 THEN h.DAY ELSE 0 END) AS Day5,
MAX(CASE WHEN h.DAY = 6 THEN h.DAY ELSE 0 END) AS Day6,
MAX(CASE WHEN h.DAY = 7 THEN h.DAY ELSE 0 END) AS Day7
from shop
INNER JOIN HOURS h ON shop.id = h.shop_id
group by
shop.NAME, shop.address, shop.zip, shop.city, shop.municipal, shop.phone, shop.lat, shop.lng;
Just a note about what you want displayed in the day columns:
AFAIK if any of the hours rows for a shop : day is missing, your current query will drop the whole row? If you want this behaviour repeated, you will need to also add in a where clause.

Don't return the lowest value if

The goal
Don't return the lowest price whose its markets are suspended.
The problem
I don't know the syntax.
The scenario
There is the following stored procedure to get the lowest and the biggest price of a specific product:
BEGIN
Select Min(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As minProductPrice,
Max(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As maxProductPrice
From products As product
Where product.Name = 'Playstation 3';
END
The context is: there are markets and products. Products belong to markets. If some market is suspended, then doesn't display its products and nor add them to max/min price comparison.
Can you all understand? I want to exclude the products whose its markets are suspended from the Min or Max statement of above's query.
The tables
Here is the markets table:
+----+------+-------------+
| Id | Name | SituationId |
+----+------+-------------+
| 1 | A | 1 |
+----+------+-------------+
| 2 | B | 2 |
+----+------+-------------+
| 3 | C | 3 |
+----+------+-------------+
Here is the markets_situations table:
+----+-----------+
| Id | Name |
+----+-----------+
| 1 | Neutral |
+----+-----------+
| 2 | Premium |
+----+-----------+
| 3 | Suspended |
+----+-----------+
And finally, here is the products table:
+----+---------------+--------+------------------+---------------+
| Id | Name | Market | PromotionalPrice | OriginalPrice |
+----+---------------+--------+------------------+---------------+
| 1 | Xbox 360 | 1 | 0 | 225,00 |
+----+---------------+--------+------------------+---------------+
| 2 | Xbox 360 | 2 | 99,00 | 175,00 |
+----+---------------+--------+------------------+---------------+
| 3 | Xbox 360 | 3 | 0 | 135,00 |
+----+---------------+--------+------------------+---------------+
| 4 | Playstation 3 | 1 | 0 | 189,00 |
+----+---------------+--------+------------------+---------------+
| 5 | Playstation 3 | 2 | 125,00 | 165,00 |
+----+---------------+--------+------------------+---------------+
| 6 | Playstation 3 | 3 | 110,00 | 185,00 |
+----+---------------+--------+------------------+---------------+
To enhance the comprehension
I don't want to display 110,00 as the Min price of the stored procedure's result because its market (C) is Suspended.
What I already did
I already tried the following, but without success:
BEGIN
[...]
Where product.Name = 'Playstation 3'
And marketSituation.Id <> 3;
END
What happens? The And condition does nothing. The query keeps returning me the price of the suspended market.
Select Min(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As minProductPrice,
Max(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As maxProductPrice
From products As product
Inner join markets on product.market = markets.id AND markets.SituationId <> 3
Where product.Name = 'Playstation 3';
How about something like
Select Min(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As minProductPrice,
Max(Case When product.PromotionalPrice = 0
Then product.OriginalPrice Else
Least(product.PromotionalPrice, product.OriginalPrice)
End) As maxProductPrice
From products As product INNER JOIN
Markets ON Product.Market = Markets.Id
Where product.Name = 'Playstation 3'
AND Markets.SituationID <> 3

Collecting multiple columns of aggregate data with a join

I'm trying to figure out if the query I'd like to do is at all doable or feasible in SQL or if I need to collect raw data and process it in my application.
My schema looks like this:
applications
================
id INT
application_steps
=================
id INT
application_id INT
step_id INT
activated_at DATE
completed_at DATE
steps
=====
id INT
step_type_id INT
Ideally, with this data in application_steps:
| id | application_id | step_id | activated_at | completed_at |
| 1 | 1 | 1 | 2013-01-01 | 2013-01-02 |
| 2 | 1 | 2 | 2013-01-02 | 2013-01-02 |
| 3 | 1 | 3 | 2013-01-02 | 2013-01-10 |
| 4 | 1 | 4 | 2013-01-10 | 2013-01-11 |
| 5 | 2 | 1 | 2013-02-02 | 2013-02-02 |
| 6 | 2 | 2 | 2013-02-02 | 2013-02-07 |
| 7 | 2 | 4 | 2013-02-09 | 2013-02-11 |
I want to get this result:
| application_id | step_1_days | step_2_days | step_3_days | step_4_days |
| 1 | 1 | 0 | 8 | 1 |
| 2 | 0 | 5 | NULL | 2 |
Note that in reality there are many more steps and many more applications that I would be looking at.
As you can see, there is a has-many relation between applications and application_steps. It is also possible for a given step to not be in use for a particular application. I'd like to get the amount of time each step takes (using DATEDIFF(completed_at, activated_at)), all in one row (the column names don't matter). Is this at all possible?
Secondary question: To complicate things a bit further, I will also need a secondary query which joins application_steps with steps and only gets data for steps with a particular step_type_id. Assuming part one is possible, how can I extend it to filter efficiently?
NOTE: Efficiency is key here - this is for a yearly report, which equates to about 2500 applications with 70 different steps and 44,000 application_steps in production (not a lot of data, but potentially a lot when joins are factored in).
This should be a basic "pivoting" aggregation:
select id,
max(case when step_id = 1 then datediff(completed_at, activated_at) end) as step_1_days,
max(case when step_id = 2 then datediff(completed_at, activated_at) end) as step_2_days,
max(case when step_id = 3 then datediff(completed_at, activated_at) end) as step_3_days,
max(case when step_id = 4 then datediff(completed_at, activated_at) end) as step_4_days
from application_steps s
group by id;
You would have to repeat this for all 70 steps.
To do this only for a particular type of step:
select application_id,
max(case when step_id = 1 then datediff(completed_at, activated_at) end) as step_1_days,
max(case when step_id = 2 then datediff(completed_at, activated_at) end) as step_2_days,
max(case when step_id = 3 then datediff(completed_at, activated_at) end) as step_3_days,
max(case when step_id = 4 then datediff(completed_at, activated_at) end) as step_4_days
from application_steps s join
steps
on s.step_id = steps.id and
steps.step_type_id = XXX
group by application_id;