Joining Tables and Pivoting Data with MySQL - mysql

Objective 1:
Your sales data is stored in the Purchases table.
Your sales staff wants to see the sales data in a pivoted form, broken down by quarter.
If your Purchases table doesn't have sales data, create some. Be sure the data spans four quarters.
Next, write a query to pivot the data as follows:
Album Q1 Q2 Q3 Q4
OK Computer 2 5 3 7
Sea Change 8 6 2 1
Do not create a separate table or a view. Do not alter any tables.
Save your query as dba1lesson10project1.sql and hand in the project.
This is What I need to do. But, the table it wants me to work with looks like this. And it states in the assignment I cannot alter it at all.
CustomerID DateOfPurchase SongID
1 2007-03-31 3
3 2007-06-30 4
4 2007-09-30 4
5 2007-12-31 5
I know I need to join three tables together so I can group by the title. Which are my Songs, Albums, and Purchases tables.
SELECT Albums.Title FROM Albums
LEFT JOIN Songs
INNER JOIN Purchases
ON Songs.SongID = Purchases.SongID
ON Albums.Title = Purchases.SongID,
SELECT Title,
SUM(CASE WHEN QUARTER(DateOfPurchase) = 1 THEN 1 ELSE 0 END) AS 'Q1',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 2 THEN 1 ELSE 0 END) AS 'Q2',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 3 THEN 1 ELSE 0 END) AS 'Q3',
SUM(CASE WHEN QUARTER(DateOfPurchase) = 4 THEN 1 ELSE 0 END) AS 'Q4'
From Purchases
GROUP BY Title;
I'm kind of at a loss here when it comes to Joining three separate tables then pivoting the data
I've tried the code above in multiple other variants which has failed me past the table joining portion.
Any help would be much appreciated.

My suggestion before attempting to PIVOT the data would be to first, write the query to return the columns that you need, this will involve joining your tables. You didn't provide your table definitions so I am making a few guesses on the structure. If your tables are structured similar to the following:
CREATE TABLE Purchases
(`CustomerID` int, `DateOfPurchase` datetime, `SongID` int)
;
CREATE TABLE Albums
(`AlbumId` int, `Title` varchar(11))
;
CREATE TABLE Songs
(`SongID` int, `AlbumID` int)
;
Then you would SELECT from the tables using a JOIN similar to this code:
select a.title,
p.DateOfPurchase
from albums a
inner join songs s
on a.albumid = s.albumId
inner join purchases p
on s.songid = p.songid
This query will return to you the album Title as well as the DateOfPurchase which you need to pivot the data. Once you have the JOINS worked out, then you can replace the p.DateOfPurchase with your aggregate function and CASE expression and add the GROUP BY clause to get the final result:
select a.title,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 1 THEN 1 ElSE 0 END) AS Q1,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 2 THEN 1 ELSE 0 END) AS Q2,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 3 THEN 1 ELSE 0 END) AS Q3,
SUM(CASE WHEN Quarter(p.DateOfPurchase) = 4 THEN 1 ELSE 0 END) AS Q4
from albums a
inner join songs s
on a.albumid = s.albumId
inner join purchases p
on s.songid = p.songid
group by a.title;
See SQL Fiddle with Demo. This gives a result:
| TITLE | Q1 | Q2 | Q3 | Q4 |
| OK Computer | 1 | 0 | 0 | 0 |
| Sea Change | 0 | 1 | 1 | 0 |

Related

Inserting Rows that don't exist and use previous value

I am trying to insert rows that do not exist in the table that I am pulling from, it is sequenced by the days_to_cancel column and starts back at zero with a new Year/Month entry. I am using the following query:
CREATE TABLE reporting.tbl_exec_retention_curve_cumulative (days_to_cancel int, cancels int, cumulative_cancels int, enroll_dt varchar(50));
SELECT rc.days_to_cancel,
rc.cancels,
(#run_total := CASE WHEN rc.days_to_cancel <> 0 THEN #run_total +
rc.cancels ELSE rc.cancels END) AS cumulative_cancels,
LEFT(rc.client_enroll_dt,7) AS client_enroll_dt
FROM (SELECT rc.days_to_cancel,
SUM(CASE WHEN rc.client_status = 'CAN' THEN 1 ELSE 0 END) AS cancels,
LEFT(rc.client_enroll_dt,7) AS client_enroll_dt
FROM t1.table rc
GROUP BY 1,3
ORDER BY 3,1
) rc
JOIN (SELECT #run_total := 0) r
GROUP BY 1,4
ORDER BY 4,1;
I get a sample result of the following
days_to_cancel | cancels | cumulative_cancels | client_enroll_dt
---------------+---------+--------------------+------------------
42 | 2 | 376 | 2019-02
47 | 0 | 376 | 2019-02
0 | 0 | 0 | 2019-03
10 | 4 | 4 | 2019-03
11 | 9 | 13 | 2019-03
So my goal is to input the missing days and use the previous cumulative_cancels and enroll_dt values and 0 for the cancels column when I input that new row for those missing days. I've tried multiple ways including using variables, but I'm at a loss of what to do. I don't know if it's possible considering the rows never existed in the first place. If it matters I am on version 10.0.35 of MariaDB.
I just imagine that you fetched data from t1.table and want to insert your result into the same table.
My solution is putting your result into a temporary table then find the missing days by left join.
SELECT rc.days_to_cancel,
rc.cancels,
(#run_total := CASE WHEN rc.days_to_cancel <> 0 THEN #run_total +
rc.cancels ELSE rc.cancels END) AS cumulative_cancels,
LEFT(rc.client_enroll_dt,7) AS client_enroll_dt
--put your result into a temp table
into #temp
FROM (SELECT rc.days_to_cancel,
SUM(CASE WHEN rc.client_status = 'CAN' THEN 1 ELSE 0 END) AS cancels,
LEFT(rc.client_enroll_dt,7) AS client_enroll_dt
FROM t1.table rc
GROUP BY 1,3
ORDER BY 3,1
) rc
JOIN (SELECT #run_total := 0) r
GROUP BY 1,4
ORDER BY 4,1;
insert into t1.table (days_to_cancel, cancels,cumulative_cancels, client_enroll_dt)
select tm.days_to_cancel, tm.cancels, tm.cumulative_cancels, tm.client_enroll_d from #temp tm
left join t1.table rc on rc.days_to_cancel = tm.days_to_cancel
where rc.days_to_cancel is null --find out missing days
drop table #temp

Joining two tables and creating PIVOT for varchar with boolean value in SQL

Working with SQL Server 2008 tables where A user can have one or multiple roles
UserDetails:
username level country role
=============================================
john A USA SystemAdmin
john B Brazil Co-ordinator
Smith G Spain Doctor
Anne D USA Nurse
.... ... .... ....
RoleDetails:
role function
============================
SystemAdmin full time
Doctor part time
Co-ordinator consultant
Nurse On call
.... ...
I am trying to create a VIEW where data would look like
username level country SystemAdmin Co-ordinator Doctor Nurse
=============================================================================
john A USA 1 0 0 0
john B Brazil 0 1 0 0
Smith G Spain 0 0 1 0
Anne D USA 0 0 0 1
.... ... .... .... .... .... ...
What I am trying to do is join two tables and generate columns from the rows of the second table where both of them are joined on the basis of UserDetails.role = RoleDetails.role. And most of the columns are varchar in the database. I am trying to generate the Columns from RoleDetails rows with boolean value dynamically. Since RoleDetails table will continue growing, I could not select the individual row like PIVOT ( MAX(role) FOR role IN (Doctor, Nurse...))
Not sure if this is feasible or how to do it. Any direction would be appreciated.
This is a very common question and here is the typical way to do this. If you need to handle the list of roles dynamically then you'll find many solutions out there using dynamic SQL along with XML features to accomplish string concatenation when building the column list.
select
u.username,
min(u.level) as level,
min(u.country) as country,
min(case when role = 'SystemAdmin' then 1 else 0 end) as SystemAdmin,
min(case when role = 'Co-ordinator' then 1 else 0 end) as "Co-ordinator",
min(case when role = 'Doctor' then 1 else 0 end) as Doctor,
min(case when role = 'Nurse' then 1 else 0 end) as Nurse
from UserDetails u left outer join RoleDetails r
on r.role = u.role
group by u.username
Your application may be able to get away with something like this if you can part a hard limit on the number of roles. Depending on how you intend to use this it may be preferable to have static column names anyway.
with NumberedRoles as (
select rolename, row_number() over (order by role) as rn
from RoleDetails
)
select
u.username,
min(u.level) as level,
min(u.country) as country,
min(case when r.rn = 1 then 1 else 0 end) as RoleIsMember01,
min(case when r.rn = 1 then r."role" else null end) as RoleName01,
min(case when r.rn = 1 then 1 else 0 end) as RoleIsMember02,
min(case when r.rn = 1 then r."role" else null end) as RoleName02,
min(case when r.rn = 1 then 1 else 0 end) as RoleIsMember03,
min(case when r.rn = 1 then r."role" else null end) as RoleName03,
...
min(case when r.rn = 1 then 1 else 0 end) as RoleIsMember32,
min(case when r.rn = 1 then r."role" else null end) as RoleName32
from
UserDetails u inner join
NumberedRoles r
on r."role" = u."role"
group by u.username

Show all patients with insurance and without insurance

I have been trying to do this for over 2 hours but simply cannot figure it out.
I have 2 tables - 'patient' with 'PatientNum' column, and 'insurance_cover' with 'insuranceCode', 'patientNum' columns.
I want to show all patients with insurance, with their patient number and the amount of different insurance companies they are covered by(this is the part I've been having trouble with).
This is what I want the output to look like as the explaination may be confusing
Insurance Cover | Patient Number | Number of insurances
-------------------------------------------------------
With Insurance| 1 | 3
With Insurance| 2 | 1
With Insurance| 3 | 1
No Insurance | 4 | N/A
No Insurance | 5 | N/A
Also I realise I need to use UNION, but I haven't been able to get the first part working yet so haven't attempted that yet
Here is my current attempt
SELECT CONCAT('With Insurance ', pat.`PatientNum`)
AS `Insurance cover + Patient Number`,
CONCAT(pat.`PatientFirstname`, ' ', pat.`PatientSurname`)
AS `Patient Name`,
COUNT(`patientNum`) GROUP BY `patientNum`
FROM `patient` AS pat,
`insurance_cover` AS ins
WHERE ins.`PatientNum` = pat.`PatientNum`
AND ins.PatientNum IN (SELECT ins.`PatientNum`
FROM `insurance_cover`)
GROUP BY pat.`PatientNum`;
Any help is appreciated
Table definitions as requested are at http://imgur.com/a/7k22r (I cannot insert pictures with low rep)
You should use a query like:
SELECT patientNum,
number_of_insurances,
(CASE number_of_insurances WHEN 0 THEN 'Not covered' ELSE 'Covered' END)
FROM (
SELECT patient.patientNum,
count(*) as number_of_insurances,
FROM patient
LEFT JOIN insurance_cover ON patient.patientNum = insurance_cover.patientNum
GROUP BY patient.patientNum
) AS S
Edit: According to comments below, you cannot use JOIN. So here is another (less efficient) answer:
SELECT (CASE (SELECT COUNT(*)
FROM insurance_cover AS i1
WHERE i1.patientNum = p.patientNum
)
WHEN 0 THEN 'Not covered'
ELSE 'Covered'
END) AS covered,
p.patientNum,
(SELECT COUNT(*)
FROM insurance_cover AS i2
WHERE i2.patientNum = p.patientNum
) AS number_of_insurances
FROM patient p

Join tables and get the same column to more columns based on the value

Lets say I join two tables and get a result like
id vendor vendor_id quantity
1 Sony 1 25
1 Apple 2 12
1 HTC 3 5
And I want the result to be like
id Quantity_Sony Quantity_Apple Quantity_HTC
1 25 12 5
How can I do that, I use Left joins to join the tables. I use mySql
SELECT ID,
MAX(CASE WHEN vendor = 'Sony' THEN Quantity END) Quantity_Sony,
MAX(CASE WHEN vendor = 'Apple' THEN Quantity END) Quantity_Apple,
MAX(CASE WHEN vendor = 'HTC' THEN Quantity END) Quantity_ATC
FROM
(
-- add your existing query here
) x
GROUP BY ID

Table join to return results in one row.

I have two tables and need to create a mysql view that gives the results in one row.
Currently I use a join but that gives me records as rows rather than columns. I tried the pivot but cannot get it to work. I need the hours for paint, hours for plumb and Other (everything else is in other) per job in one row.
The table structure is here:
This is basically a PIVOT, unfortunately MySQL does not have a PIVOT function, but you can use an aggregate function with a CASE statement:
select jobnum,
sum(case when tasktype = 'paint' then hrs else 0 end) Paint,
sum(case when tasktype = 'plumb' then hrs else 0 end) plumb,
sum(case when tasktype not in ('paint', 'Plumb') then hrs else 0 end) Other
from tablea a
left join tableb b
on a.id = b.tbla_id
group by jobnum
See SQL Fiddle with Demo
Result:
| JOBNUM | PAINT | PLUMB | OTHER |
----------------------------------
| 1 | 10 | 10 | 20 |
| 2 | 25 | 0 | 0 |
SELECT
a.`JobNum`,
SUM(IF(a.`TaskType`='Paint',b.`Hrs`,0)) AS 'Paint',
SUM(IF(a.`TaskType`='Plumb',b.`Hrs`,0)) AS 'Plumb',
SUM(IF(a.`TaskType` IN('Paint','Plumb'),0,b.`Hrs`)) AS 'Other'
FROM `tableA` a
INNER JOIN `tableB` b
ON b.`tblAid`=a.`id`
GROUP BY a.`JobNum`