MYSQL: transpose the result of a SQL query - mysql

please I have problem with transposing an sql query result.
My sql query is:
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK
FROM atop INNER JOIN (aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2));
the result is like this:
----------------------------------------------------------------
top | abei | CI | pac | FRK |
----------------------------------------------------------------
A | b | e | 1 | 12 |
A | b | e | 1 | 13 |
A | b | e | 1 | 14 |
A | c | t | 2 | 45 |
A | c | t | 2 | 56 |
A | c | t | 2 | 23 |
A | c | t | 2 | 29 |
A | c | t | 2 | 50 |
b | c | t | 1 | 11 |
b | c | t | 1 | 56 |
b | c | t | 1 | 78 |
----------------------------------------------------------------
I want to transpose this result and I want it like this :
----------------------------------------------------------------------------------
top | abei | CI | pac | FRK | frk1 | frk2 | frk3 | frk4 |...
---------------------------------------------------------------- -----------------
A | b | e | 1 | 12 | 13 | 14 | | |...
A | c | t | 2 | 45 | 56 | 23 | 29 | 50 |...
b | c | t | 1 | 11 | 56 | 56 | 78 | |...
----------------------------------------------------------------------------------
I tried this code but doesn't work!
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK,frk1,frk2,frk3,frk4,frk5,frk6,frk7,frk8,frk9,frk10,frk11,frk12,
max(case when row=1 then data end) frk1,
max(case when row=2 then data end) frk2,
max(case when row=3 then data end) frk3,
max(case when row=4 then data end) frk4,
max(case when row=5 then data end) frk5,
max(case when row=6 then data end) frk6,
max(case when row=7 then data end) frk7,
max(case when row=8 then data end) frk8,
max(case when row=9 then data end) frk9,
max(case when row=10 then data end) frk10,
max(case when row=11 then data end) frk11,
max(case when row=12 then data end) frk12
FROM atop INNER JOIN (aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2));
Thanks for your help.

You need a group by and you don't need the extra columns in the select. Try this:
SELECT atop.name AS top, aseco.name AS abei, aseco.abeiId AS CI, aseco.location AS pac, axrt.frk AS FRK,
max(case when row=1 then data end) frk1,
max(case when row=2 then data end) frk2,
max(case when row=3 then data end) frk3,
max(case when row=4 then data end) frk4,
max(case when row=5 then data end) frk5,
max(case when row=6 then data end) frk6,
max(case when row=7 then data end) frk7,
max(case when row=8 then data end) frk8,
max(case when row=9 then data end) frk9,
max(case when row=10 then data end) frk10,
max(case when row=11 then data end) frk11,
max(case when row=12 then data end) frk12
FROM atop INNER JOIN
(aseco INNER JOIN axrt ON (aseco.abeiId = axrt.abeiId) AND (aseco.BCFId = axrt.BCFId) AND (aseco.topId = axrt.topId)) ON atop.topId = aseco.topId
WHERE (((axrt.ctp)<>8) AND ((aseco.abeiish)=5)) OR (((axrt.ctp)<>3) AND ((aseco.abeiish)=2) AND ((aseco.hpnmd)=2))
GROUP BY atop.name, aseco.name, aseco.abeiId, aseco.location ;

Related

MySQL - Join and conditional select columns

Don't know how to excatly name the problem. Therefore my example tables:
People:
id_people | peopleName |
------------------------
1 | John |
2 | Jane |
3 | Frank |
4 | Emma |
Courses:
id | id_course | id_people | date |
-----------------------------------------
1 | 1 | 1 | 2011-02-14 |
2 | 2 | 1 | 2013-05-01 |
3 | 3 | 1 | 2015-08-25 |
4 | 1 | 2 | 2012-01-05 |
5 | 1 | 4 | 2017-10-10 |
Now I want to generate a table where I have one row per person and for every course a column with the date like:
id_people | peopleName | Course1 | Course2 | Course3 |
---------------------------------------------------------------
1 | John | 2011-02-14 | 2013-05-01 | 2015-08-25 |
2 | Jane | 2012-01-05 | | |
3 | Frank | | | |
4 | Emma | 2017-10-10 | | |
I tried following query:
SELECT *,
max(case when courses.id_course = 1 then cource.date end) as Course1,
max(case when courses.id_course = 2 then cource.date end) as Course2,
max(case when courses.id_course = 3 then cource.date end) as Course3
FROM people
JOIN courses ON people.id_people = courses.id_people
GROUP BY people.id_people
But I got the error #1055 ([..] this is incompatible with sql_mode=only_full_group_by).
So, it forces me to add the column course.id, course.id_course and course.date to the group by statemant. This results in a row per per people and course.
How can I get my desired resulting table with a sql statment?
I do not have the privilege to change the sql_mode variable.
So it forces me to add the column course.id, course.id_course and
course.date to the group by statemant...
No it does not.
You only need to change the join to a LEFT JOIN and group by people.id_people, people.peopleName and you will get the results that you want:
SELECT people.id_people, people.peopleName,
max(case when courses.id_course = 1 then courses.date end) as Course1,
max(case when courses.id_course = 2 then courses.date end) as Course2,
max(case when courses.id_course = 3 then courses.date end) as Course3
FROM people
LEFT JOIN courses ON people.id_people = courses.id_people
GROUP BY people.id_people, people.peopleName
See the demo.
Results:
| id_people | peopleName | Course1 | Course2 | Course3 |
| --------- | ---------- | ----------- | ----------- | ----------- |
| 1 | John | 2011-02-14 | 2013-05-01 | 2015-08-25 |
| 2 | Jane | 2012-01-05 | | |
| 3 | Frank | | | |
| 4 | Emma | 2017-10-10 | | |
You haven't define id_people to be a primary key or at least unique. You should probably do that.
In the meantime, you can aggregate first and then join:
SELECT *
FROM people p JOIN
(SELECT c.id_people
max(case when c.id_course = 1 then c.date end) as Course1,
max(case when c.id_course = 2 then c.date end) as Course2,
max(case when c.id_course = 3 then c.date end) as Course3
FROM courses c
GROUP BY c.id_people
) c
USING (id_people);

Convert Row with duplicate values to Column - MySQL

I have a table 'A' that looks something like:
_______________________________________________________________
|query_id | query | response |user_response_count |
|---------------------------------------------------------------
| 1 | acne | BothBad | 2 |
| 1 | acne | BothGood | 1 |
| 2 | asthma | BothBad | 1 |
| 2 | asthma | product 1 | 1 |
| 2 | asthma | BothGood | 1 |
| 3 | bell palsy | product 2 | 2 |
| 3 | bell palsy | BothGood | 1 |
---------------------------------------------------------------
I want to write a query to get something that looks like:
__________________________________________________________________________________
| query_id | query | BothGood | BothBad | Product 1 | Product 2 |
-----------------------------------------------------------------------------------
| 1 | acne | 1 | 2 | 0 | 0 |
| 2 | asthma | 1 | 1 | 1 | 0 |
| 3 | bell palsy| 1 | 0 | 0 | 2 |
-----------------------------------------------------------------------------------
That "user_response_count" column actually says, 2 users selected "BothBad" option for "acne" query.
I know, by using max, I can change my rows to the column, but here it would be difficult to the max. Any Thoughts?
Conditional aggregation:
select query_id, query,
sum(case when response = 'BothGood' then cnt else 0 end) as BothGood,
sum(case when response = 'BothBad' then cnt else 0 end) as BothBad,
sum(case when response = 'product 1' then cnt else 0 end) as product1,
sum(case when response = 'product 2' then cnt else 0 end) as product2
from a
group by query_id, query;
You can use a conditional aggregation as
select query_id, query,
max( coalesce(case when response = 'BothGood' then user_response_count end,0) )
as BothGood,
max( coalesce(case when response = 'BothBad' then user_response_count end,0) )
as BothBad,
max( coalesce(case when response = 'product 1' then user_response_count end,0) )
as Product_1,
max( coalesce(case when response = 'product 2' then user_response_count end,0) )
as Product_2
from tableA
group by query_id, query
Demo

MYSQL Select the Sum of Multiple Distinct Values

My table looks like this:
| id | Vendor | Issue |
|----|--------|-----------|
| 1 | Acme | Defective |
| 2 | Best | Returned |
| 3 | Ace | Other |
| 4 | Best | Returned |
| 5 | Acme | Other |
| 6 | Ace | Other |
| 7 | Best | Defective |
I need a Select statement to sum the amount of each distinct issue each vendor has had.
Output of select statement would look like this in a table:
| Vendor | Defective | Returned | Other |
|--------|-----------|----------|-------|
| Acme | 1 | 0 | 1 |
| Best | 1 | 2 | 0 |
| Ace | 0 | 0 | 2 |
Any help would be greatly appreciated.
You can use the CASE clause to separate the sums, as in:
select
vendor,
sum(case when issue = 'Defective' then 1 end) as defective,
sum(case when issue = 'Returned' then 1 end) as returned,
sum(case when issue = 'Other' then 1 end) as other
from my_table
group by vendor
Final Statement:
$sql = "select
vendor,
sum(case when issue = 'Item Defective' THEN 1 ELSE 0 END) as 'defective',
sum(case when issue = 'Incorrect Item Received' THEN 1 ELSE 0 END) as 'received',
sum(case when issue = 'Incorrect Item Ordered' THEN 1 ELSE 0 END) as 'ordered',
sum(case when issue = 'Item Not Made to Drawing' THEN 1 ELSE 0 END) as 'drawing',
sum(case when issue = 'Other' THEN 1 ELSE 0 END) as 'other'
FROM record GROUP BY vendor";

LEFT JOIN returns a wrong total

I have the following data in my database:
scu_banks
----------------------------
| id | name | type |
| 1 | One | 1 |
| 2 | Two | 2 |
| 3 | Three | 1 |
| 4 | Four | 1 |
----------------------------
scu_bankstatement
--------------------------------------
| type | code | mutations | status |
| 1 | 1 | 100 | 1 |
| 1 | 1 | 100 | 0 |
| 1 | 1 | -50 | 1 |
--------------------------------------
I want to show the following data:
------------------------------------------------------
| type | name | status1 | status2 | total | id |
| 1 | One | 1 | 2 | 150 | 1 |
| 2 | Two | 0 | 0 | 0 | 2 |
| 1 | Three | 0 | 0 | 0 | 3 |
| 1 | Four | 0 | 0 | 0 | 4 |
------------------------------------------------------
Status1 should represent the total of the rows where the status = 0 and Status2 should represent the total of the rows where the status = 1.
I am using the following statement:
SELECT b.type 'scu_banks.type', b.name 'scu_banks.name', count(l.status) 'status1', count(s.status) 'status2', concat('€ ', format(coalesce(x.mutations, 0), 2)) 'total', b.id 'scu_banks.id'
FROM scu_banks b
LEFT JOIN scu_bankstatement l
ON l.code = b.id AND l.status = 0
LEFT JOIN scu_bankstatement s
ON s.code = b.id AND s.status = 1
LEFT JOIN (SELECT s.code, sum(s.mutations) mutations
FROM scu_bankstatement s
GROUP BY s.code) x ON x.code = b.id
GROUP BY b.id, b.name, b.type
When I execute the statement I get total "2" in the columns 'status1' and 'status2':
------------------------------------------------------
| type | name | status1 | status2 | total | id |
| 1 | One | 2 | 2 | 150 | 1 |
| 2 | Two | 0 | 0 | 0 | 2 |
| 1 | Three | 0 | 0 | 0 | 3 |
| 1 | Four | 0 | 0 | 0 | 4 |
------------------------------------------------------
Does someone know why I am getting a incorrect response?
You are joining the scu_bankstatement twice, so you will get double number of rows for the matching rows. You do not need to join the table twice.
Also note that the x.mutations should be included into the GROUP BY:
SELECT
b.type 'scu_banks.type',
b.name 'scu_banks.name',
sum(if(l.status=0, 1, 0)) 'status1',
sum(if(l.status=1, 1, 0)) 'status2',
concat('€ ', format(coalesce(x.mutations, 0), 2)) 'total',
b.id 'scu_banks.id'
FROM scu_banks b
LEFT JOIN scu_bankstatement l
ON l.code = b.id
LEFT JOIN (SELECT s.code, sum(s.mutations) mutations
FROM scu_bankstatement s
GROUP BY s.code) x ON x.code = b.id
GROUP BY b.id, b.name, b.type, x.mutations

Mysql table joins using a where clause

I am trying to run a MySQL query against a database and join 3 tables together.
I have 3 tables in the database;
Table containing the important data
+----+-------+----------------------+
| id | name | value |
+----+-------+----------------------+
| 1 | data1 | First piece of data |
| 2 | data2 | Second piece of data |
| 3 | data3 | Third piece of data |
+----+-------+----------------------+
Table containing flag_id to flag_name
+----+-------+-------------+
| id | name | description |
+----+-------+-------------+
| 1 | flag1 | NULL |
| 2 | flag2 | NULL |
| 3 | flag3 | NULL |
| 4 | flag4 | NULL |
+----+-------+-------------+
Table containing the one-to-many data to flag mapping
+----+---------+--------+---------+
| id | type_id | status | data_id |
+----+---------+--------+---------+
| 1 | 1 | 0 | 1 |
| 2 | 2 | 0 | 1 |
| 3 | 4 | 1 | 1 |
| 4 | 2 | 0 | 2 |
| 5 | 3 | 0 | 2 |
| 6 | 4 | 1 | 2 |
| 7 | 3 | 0 | 3 |
| 8 | 4 | 1 | 3 |
+----+---------+--------+---------+
I want to be able to have a single query that will show me each named flag as a column for each piece of data. In searching the web, I found an example doing it using this query:
select d.id, d.name, d.value,
MAX(CASE WHEN f.type_id = 1 THEN f.status ELSE NULL END) as flag1,
MAX(CASE WHEN f.type_id = 2 THEN f.status ELSE NULL END) as flag2,
MAX(CASE WHEN f.type_id = 3 THEN f.status ELSE NULL END) as flag3,
MAX(CASE WHEN f.type_id = 4 THEN f.status ELSE NULL END) as flag4
from data d
inner join flags f on f.data_id = d.id
group by d.id
Which gives the result I want.
+----+-------+----------------------+-------+-------+-------+-------+
| id | name | value | flag1 | flag2 | flag3 | flag4 |
+----+-------+----------------------+-------+-------+-------+-------+
| 1 | data1 | First piece of data | 0 | 0 | NULL | 1 |
| 2 | data2 | Second piece of data | NULL | 0 | 0 | 1 |
| 3 | data3 | Third piece of data | NULL | NULL | 0 | 1 |
+----+-------+----------------------+-------+-------+-------+-------+
The problem is that I need to add a where clause to filter on specific flags, but I get the error that 'flag4' is an Unknown column, which is because an alias cannot be used in a where clause.
How do I accomplish this, preferably in a single query that I can use with a 'where' clause?
Instead of use where, use having.
The flag columns are the result of an aggregating operation, so, if you want to apply a selection criteria on them, you have to use having. Example:
select d.id, d.name, d.value,
MAX(CASE WHEN f.type_id = 1 THEN f.status ELSE NULL END) as flag1,
MAX(CASE WHEN f.type_id = 2 THEN f.status ELSE NULL END) as flag2,
MAX(CASE WHEN f.type_id = 3 THEN f.status ELSE NULL END) as flag3,
MAX(CASE WHEN f.type_id = 4 THEN f.status ELSE NULL END) as flag4
from data d
inner join flags f on f.data_id = d.id
group by d.id
havig flag1 = 0