SQL: Creating an aggregated table from a 2-column data source - mysql

I have a table within a database, and I want to aggregate the questions and answers within them into a more readable data table. The source table 👇
+-----+----------------+----------------------------+----------------+
| QID | Date Submitted | Question | Answer |
+-----+----------------+----------------------------+----------------+
| 0 | 08/04/2021 | companyName | Yaneev |
| 1 | 08/04/2021 | humanRightPolicy | George Micheal |
| 2 | 08/04/2021 | accountManagerMobileNumber | 7474236843 |
| 3 | 08/04/2021 | swiftCode | AOEU326 |
| 4 | 03/04/2021 | companyName | Eimbee |
| 5 | 03/04/2021 | humanRightPolicy | Revvie George |
| 6 | 03/04/2021 | accountManagerMobileNumber | 7475123843 |
| 7 | 03/04/2021 | swiftCode | AOEU324 |
| 8 | 04/04/2021 | companyName | Yaneev |
| 9 | 04/04/2021 | humanRightPolicy | Mark Fox |
| 10 | 04/04/2021 | accountManagerMobileNumber | 74742121231 |
| 11 | 04/04/2021 | swiftCode | 124eoKK |
+-----+----------------+----------------------------+----------------+
And what I want the 'destination' table to look like 👇
+--------------+-------------------+----------------------------+-----------+
| Company Name | humanRightsPolicy | accountManagerMobileNumber | swiftCode |
+--------------+-------------------+----------------------------+-----------+
| Yaneev | George Micheal | 7474236843 | AOEU326 |
| Eimbee | Revvie George | 7475123843 | AOEU324 |
+--------------+-------------------+----------------------------+-----------+
I'd also like to know what kind of operation this is, as I feel that it's something that isn't new in any way.

SQL table represent unordered sets. Let me assume that you have an ordering column. With such a column, you can assign a group to each set of rows for the same company and then use conditional aggregation:
select max(case when question = 'Company Name' then answer end) as company_name,
max(case when question = 'Q1' then answer end) as q1,
max(case when question = 'Q2' then answer end) as q2,
max(case when question = 'Q3' then answer end) as q3
from (select t.*,
sum(case when question = 'Company Name' then 1 else 0 end) over (order by <ordering col>) as grp
from t
) t
group by grp;
Note that this answers the question that you asked, where all the questions are known -- so the result columns are all known. If you don't know the questions in advance, you need to use dynamic SQL (i.e. construct the query as a string and then execute it). How to do that depends entirely on the database you are using.
EDIT:
In older versions of MySQL, you can use a subquery:
select max(case when question = 'Company Name' then answer end) as company_name,
max(case when question = 'Q1' then answer end) as q1,
max(case when question = 'Q2' then answer end) as q2,
max(case when question = 'Q3' then answer end) as q3
from (select t.*,
(select count(*)
from t t2
where t2.question = 'Company Name' and
t2.<ordering col> <= t.<ordering col>
) as grp
from t
) t
group by grp;

Related

How Do Efficiently I Convert Dynamic Rows to Column in MySQL

I'm looking for an efficient way to convert rows to columns in the SQL server, I heard that PIVOT is not very fast, and I need to deal with a lot of records.
I tried following on this Efficiently convert rows to columns in sql server but still not solved with my below example
This is my example: (updated)
-----------------------------------------------
| Id | Value | ColumnName | Submission_Id |
-----------------------------------------------
| 1 | John | FirstName | 1 |
| 2 | 2.4 | Amount | 1 |
| 3 | ZH1E4A | PostalCode | 1 |
| 4 | Fork | LastName | 1 |
| 5 | 857685 | AccountNumber | 1 |
| 6 | Donny | FirstName | 2 |
| 7 | 2.7 | Amount | 2 |
| 8 | ZH1E4C | PostalCode | 2 |
| 9 | Yen | LastName | 2 |
| 10 | 857686 | AccountNumber | 2 |
-----------------------------------------------
This is my expected result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 2.4 | ZH1E4A | Fork | 857685 |
| Donny | 2.7 | ZH1E4C | Yen | 857686 |
---------------------------------------------------------------------
How can I build the result?
You need to group them by their associative key, which you informed:
SELECT
MAX(CASE ColumnName WHEN 'FirstName' THEN Value END) AS FirstName,
MAX(CASE ColumnName WHEN 'Amount' THEN Value END) AS Amount,
MAX(CASE ColumnName WHEN 'PostalCode' THEN Value END) AS PostalCode,
MAX(CASE ColumnName WHEN 'LastName' THEN Value END) AS LastName,
MAX(CASE ColumnName WHEN 'AccountNumber' THEN Value END) AS AccountNumber
FROM table
GROUP BY submission_id
;
GROUP BY enforces that there is single row for each unique submission_id, MAX selects the most descendant value of the expression for that particular group key (it is assumed to be singular so aggregate type should not matter), and finally CASE is filtering the Value by ColumnName.
WITH
indata(Id,Value,ColumnName) AS (
SELECT 1,'John' ,'FirstName'
UNION ALL SELECT 2,'2.4' ,'Amount'
UNION ALL SELECT 3,'ZH1E4A' ,'PostalCode'
UNION ALL SELECT 4,'Fork' ,'LastName'
UNION ALL SELECT 5,'857685' ,'AccountNumber'
UNION ALL SELECT 6,'Donny' ,'FirstName'
UNION ALL SELECT 7,'2.7' ,'Amount'
UNION ALL SELECT 8,'ZH1E4C' ,'PostalCode'
UNION ALL SELECT 9,'Yen' ,'LastName'
UNION ALL SELECT 10,'857686','AccountNumber'
)
,
-- need to get a grouping column, one that
-- changes every time we encounter a 'FirstName
-- add a counter that is at 1 for FirstName
-- otherwise at 0, and build a running sum...
w_session_id AS (
SELECT
SUM(CASE ColumnName WHEN 'FirstName' THEN 1 END)
OVER(ORDER BY id) AS sessid
, *
FROM indata
)
-- now un-pivot manually
SELECT
sessid AS id
, MAX(CASE ColumnName WHEN 'FirstName' THEN value END) AS FirstName
, MAX(CASE ColumnName WHEN 'Amount' THEN value END) AS Amount
, MAX(CASE ColumnName WHEN 'PostalCode' THEN value END) AS PostalCode
, MAX(CASE ColumnName WHEN 'LastName' THEN value END) AS LastName
, MAX(CASE ColumnName WHEN 'AccountNumber' THEN value END) AS AccountNumber
FROM w_session_id
GROUP BY sessid;
-- out id | FirstName | Amount | PostalCode | LastName | AccountNumber
-- out ----+-----------+--------+------------+----------+---------------
-- out 1 | John | 2.4 | ZH1E4A | Fork | 857685
-- out 2 | Donny | 2.7 | ZH1E4C | Yen | 857686

One row result with multiple join in 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

MySQL row into number of columns sum and payment option [duplicate]

This question already has answers here:
MySQL row into number of columns and sum
(3 answers)
Closed 5 years ago.
I have a table with records such as:
ID | Car_num | Service | Price | Payment
---+---------+---------+-------+-------+-
1 | 001 | shower | 10 | card
2 | 002 | TV | 5 | cash
3 | 001 | TV | 5 | cash
How to write an SQL query to get the following output?
ID |Car_num | shower | TV
---+--------+------------+---
1 | 001 | 10 (card) | 5 (cash)
2 | 002 | | 5 (cash)
Use conditional aggregation:
SELECT MIN(t.id) as id,
t.car_num,
MAX(CASE WHEN t.service = 'shower' THEN t.price END) as shower,
MAX(CASE WHEN t.service = 'TV' THEN t.price END) as TV
FROM YourTable t
GROUP BY t.car_num
If you want the columns to actually appear like 10 (card) and not 10 (which is not recommended at all), then change it to this:
MAX(CASE WHEN t.service = 'shower' THEN concat(t.price,'(',t.payment,')') END) as shower,
MAX(CASE WHEN t.service = 'TV' THEN concat(t.price,'(',t.payment,')') END) as TV

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 5.6 - Complex table update

I have two tables as follows:
Enclosure
+-------+--------------+-----------+-----------+
| name | serialnumber | VCenabled | BCenabled |
+-------+--------------+-----------+-----------+
| ENC01 | 12345 | | |
| ENC02 | 45678 | | |
| ENC03 | 11222 | | |
+-------+--------------+-----------+-----------+
Interconnect
+-------------+-----------+-----------------------+
| description | baynumber | enclosureserialnumber |
+-------------+-----------+-----------------------+
| VC | 1 | 12345 |
| VC | 2 | 12345 |
| BC | 3 | 12345 |
| VC | 1 | 45678 |
| BC | 3 | 45678 |
+-------------+-----------+-----------------------+
I need to update the VCenabled and BCenabled columns in the Enclosure table. VCenabled should contain a count of the corresponding rows in the Interconnect table. Likewise for BCenabled.
Here is what I need to end up with:
+-------+--------------+-----------+-----------+
| name | serialnumber | VCenabled | BCenabled |
+-------+--------------+-----------+-----------+
| ENC01 | 12345 | 2 | 1 |
| ENC02 | 45678 | 1 | 1 |
| ENC03 | 11222 | | |
+-------+--------------+-----------+-----------+
I was able to come up with this SQL query, but I'm not having much luck turning this into an update. Also, this query works if I run it in Flyspeed Query but if I run it in MySQL Workbench, I get a 1064 error: Error in SQL syntax.
Select
enclosure.name,
enclosure.vcenabled,
count(*)
From
enclosure Inner Join
interconnect On interconnect.enclosureserialnumber = enclosure.serialnumber
Where
interconnect.description like '%VC%'
Group By
enclosure.serialnumber
Any help would be appreciated.
try this :
update enclosure t1,
(select count(*) as x,enclosureserialnumber from interconnect where description='VC') t2,
(select count(*) as y,enclosureserialnumber from interconnect where description='BC') t3
set t1.VCenabled = t2.x,t1.BCenabled=t3.x
where t1.serialnumber=t2.enclosureserialnumber
and t2.serialnumber=t3.enclosureserialnumber
Sample
UPDATE
Enclosure e
INNER JOIN (
SELECT
enclosureserialnumber,
SUM(CASE WHEN description = 'VC' THEN 1 ELSE 0 END) AS VC,
SUM(CASE WHEN description = 'BC' THEN 1 ELSE 0 END) AS BC
FROM
Interconnect
GROUP BY
enclosureserialnumber
) q1
ON e.serialnumber = q1.enclosureserialnumber
SET
VCenabled = q1.VC,
BCenabled = q1.BC;
This should work using a subquery with the update:
update Enclosure e
join (
select enclosureserialnumber,
sum(case when i.description = 'VC' then 1 else 0 end) vcsum,
sum(case when i.description = 'BC' then 1 else 0 end) bcsum
from Interconnect i
group by i.enclosureserialnumber
) i
on e.serialnumber = i.enclosureserialnumber
set e.VCenabled = i.vcsum,
e.BCenabled = i.bcsum;
SQL Fiddle Demo
update Enclosure as E1 set E1.VCenabled = (select count(*) from Interconnect as I where I.enclosureserialnumber = E1.serialnumber and I.description = 'VC');
similar for BC
Tested... if any error occurs, please check for any spelling typos.
for output to my system....
http://pastebin.com/B0bUDYX6