How to make one column in to more colums - mysql

I have a really complicated SQL question for a mySQL database.
I will first introduce all needed tables for this question:
User Table:
Id date_created
------------------------------
1 2015-09-19T14:18:07.000Z
2 2015-09-20T01:16:34.000Z
3 2015-09-21T15:10:21.000Z
…
Setting table:
Id User_id setting_key setting_value
----------------------------------------------
1 1 city 1
2 3 city 2
3 2 city 1
…
City names Table:
Id name
------------------
1 New York
2 Los Angeles
3 Boston
…
With a select-query I Would like to achieve this:
date New York Los Angeles Boston …
------------------------------------------------------
2015-09-19 1 0 0
2015-09-20 2 0 0
2015-09-21 2 1 0
…
For every date in certain range how many users has as setting New york, Los, Angeles....
The only thing I can achieve is this query:
select date(u.date_created), n.name, count(u.id)
from user u inner join setting s
on u.id = s.user_id
and setting_key = 'city'
inner join name n
on s.setting_value = n.id
where u.date_created > '2015-09-19T14:18:07.000Z'
group by 1, 2
but then I get this result:
date name count
-------------------------------
2015-09-19 New York 1
2015-09-19 Los Angeles 0
2015-09-19 Boston 0
2015-09-20 New York 1
2015-09-20 Los Angeles 0
2015-09-20 Boston 0
2015-09-21 New York 0
2015-09-21 Los Angeles 1
2015-09-21 Boston 0
…
It is not cummulative en there is row for each city instead of a column for each city. Does somebody know (the complicated) answer? If something is not clear just ask, In real live these are tables of 50.000 rows and I can't change the structure, I need this query for analytics purposes

If you know the names of the cities, just use conditional aggregation:
select date(u.date_created),
sum(name = 'New York') as NewYork,
sum(name = 'Los Angeles' as LosAngeles,
sum(name = 'Boston') as Boston
from user u inner join
setting s
on u.id = s.user_id and
setting_key = 'institution' inner join
education_niveau en
on s.setting_value = en.id
where u.date_created > '2015-09-19T14:18:07.000Z'
group by 1;
If you don't know the names of the cities or there are an unknown number, then you will need dynamic SQL -- that is, construct the SQL, prepare a statement, and then execute it.
EDIT:
It wasn't obvious at first that the question is about cumulative counts. For that, use variables:
select dte,
(#ny := #ny + NewYork) as NewYork,
(#la := #la + LosAngeles) as LosAngeles,
(#b := #b + Boston) as NewYork
from (select date(u.date_created) as dte,
sum(name = 'New York') as NewYork,
sum(name = 'Los Angeles' as LosAngeles,
sum(name = 'Boston') as Boston
from user u inner join
setting s
on u.id = s.user_id and
setting_key = 'institution' inner join
education_niveau en
on s.setting_value = en.id
where u.date_created > '2015-09-19T14:18:07.000Z'
group by 1
order by 1
) us cross join
(select #ny := 0, #la := 0, #b := 0) params
order by 1;

You want a pivot, which is not supported natively by mysql. However, this may work for you:
select date, group_concat(concat(name, '=', uid_count)) cities
from (
select date(u.date_created) date, en.name, count(u.id) uid_count
from user u
join setting s on u.id = s.user_id
join education_niveau en on s.setting_value = en.id
where u.date_created > '2015-09-19T14:18:07.000Z'
and setting_key = 'institution'
group by 1, 2) x
group by 1
which will produce a result like:
date Cities
------------------------------------------------------
2015-09-19 New York=1
2015-09-20 New York=2
2015-09-21 New York=2,Los Angeles=1
The main advantage with this approach is the range of cities returned is totally based on the data.

Related

show who resit and passed and what course was it?

i need help find who fail in exam & resit and pass the exam only,
heres the code:
select STUDENT_ID,EXAM_ID,SCORE,PASS_THRESHOLD,s.NAME , c.NAME as Course_name, EXAM_DT,
case
when SCORE>=PASS_THRESHOLD then 'PASS'
else 'Fail'
end as Flag
from exam_submission es
left join student s on es.STUDENT_ID = s.ID
left join exam e on es.EXAM_ID = e.ID
left join course c on e.COURSE_ID = c.ID
heres the result:
STUDENT_ID EXAM_ID SCORE PASS_THRESHOLD NAME Course_name EXAM_DT Flag
1 3 88 65 Anthony Data Mining 2019-12-17 PASS
1 5 71 70 Anthony Statistic 2019-12-19 PASS
2 1 53 55 Sisca Machine Learning2019-12-17 Fail
2 3 77 65 Sisca Data Mining 2019-12-17 PASS
2 4 85 63 Sisca Data Science 2019-12-18 PASS
2 1 60 55 Sisca Machine Learning2020-01-08 PASS
I need find like this:
2 1 53 55 Sisca Machine Learning2019-12-17 Fail
2 1 60 55 Sisca Machine Learning2020-01-08 PASS
Possibly using a query like below.
this is using your query as input.
Also we have assumed that it is not possible to have a student have (PASS, FAIL) for a student on same exam on two years chronologically.
; with inputdata as
(
select STUDENT_ID,EXAM_ID,SCORE,PASS_THRESHOLD,s.NAME , c.NAME as Course_name, EXAM_DT,
case
when SCORE>=PASS_THRESHOLD then 'PASS'
else 'Fail'
end as Flag
from exam_submission es
left join student s on es.STUDENT_ID = s.ID
left join exam e on es.EXAM_ID = e.ID
left join course c on e.COURSE_ID = c.ID
)
select * from Inputdata I
join
( select student_id, exam_id from
inputdata
group by student_id, exam_id
having count(distinct flag)=2
)T on I.student_id=T.student_id and I.exam_id=T.exam_id
order by exam_dt asc

I want to check if the value was repeated above. How I can do it?

So I have the next code:
SELECT distinct NVL( l.city, ' '), e.last_name || ' ' || e.first_name
FROM locations l
FULL JOIN departments d
ON d.location_id = l.location_id
FULL JOIN employees e
ON e.department_id = d.department_id
OUTPUT:
NVL(L.CITY,'') E.LAST_NAME||''||E.FIRST_NAME
------------------- ----------------------------------------------
1) Seattle
2) Seattle Kochhar Neena
3) Oxford Zlotkey Eleni
4) Oxford Abel Ellen
5) Oxford Vargas Jonathon
6) Oxford Grovlin Gus
I want it to not display the line where is only City and no Lastname if there is a line where this city is mentioned. For example, I have row
Seattle Kochhar Neena
because Seattle is already displayed, I don't want to see the first line. However, if there is no row with city and last_name, there should be line with just the city.
Any idea how to do it?
A little bit of analytic sql should do the trick.
SQL> with your_data as
2 (
3 SELECT distinct
4 l.city city,
5 e.last_name || ' ' || e.first_name name
6 FROM hr.locations l FULL JOIN
7 hr.departments d ON d.location_id = l.location_id FULL JOIN
8 hr.employees e ON e.department_id = d.department_id
9 )
10 select *
11 from
12 (
13 select
14 y.*,
15 count(*) over ( partition by city ) as seq
16 from your_data y
17 )
18 where seq = 1 or ( seq > 1 and name != ' ' )
19 order by 1,2;
CITY NAME SEQ
------------------------------ ---------------------------------------------- ----------
Beijing 1
Bern 1
Bombay 1
Geneva 1
Hiroshima 1
London Mavris Susan 1
Mexico City 1
Munich Baer Hermann 1
Oxford Abel Ellen 34
Oxford Ande Sundar 34
Oxford Banda Amit 34
Oxford Bates Elizabeth 34
Oxford Bernstein David 34
Full tutorial on these techniques here https://www.youtube.com/watch?v=0cjxYMxa1e4&list=PLJMaoEWvHwFIUwMrF4HLnRksF0H8DHGtt
Use row_number analytical function based on pks.
Select city, ename from
(SELECT distinct NVL( l.city, ' ') as city,
trim(e.last_name || ' ' || e.first_name) as ename,
Row_number() over
(partiotion by l.location_id order by e.department_id desc nulls last) as rn
FROM locations l
FULL JOIN departments d
ON d.location_id = l.location_id
FULL JOIN employees e
ON e.department_id = d.department_id)
Where case when ename is null and rn != 1
Then 0 Else 1
End = 1
Cheers!!

I have a join that is returning too many rows

I am doing a MySQL join where I only want to grab the records that have a LMSCDonationLetterNeeded value of 1. What is happening, however, is that I am getting 7 rows returned instead of 3. Also, when the query is executed the column that called LMSCDonationLetterNeeded all has values of 1 when that is not accurate.
I have a 3 tables which are below:
Registristration
-------------------
SwimmerID RegNumber TransactionID LMSCDonationLetterNeeded RegistrationDate
HYDR0 3881-HYDR0 0123-0001 0 11/25/2015
HYDR0 3882-HYDR0 0123-0048 1 04/15/2018
97SSN 3880-97SSN 0124-0022 0 01/01/2016
97SSN 3881-97SSN 0124-0068 0 03/20/2017
97SSN 3882-97SSN 0124-0084 1 04/02/2018
06HM5 388K-06HM5 0126-0011 0 02/02/2015
06HM5 388J-06HM5 0126-0056 1 03/02/2018
People
-------------------
SwimmerID FirstName LastName
HYDR0 John Smith
97SSN Jim Johnson
06HM5 Susan Korver
FinTransactions
--------------------
SwimmerID RegistrationNum TransactionID LMSCDonateAmt FinTransactions
HYDR0 3881-HYDR0 0123-0001 10.00 11/25/2015
HYDR0 3882-HYDR0 0123-0048 15.00 04/15/2018
97SSN 3880-97SSN 0124-0022 05.00 01/01/2016
97SSN 3881-97SSN 0124-0068 25.00 03/20/2017
97SSN 3881-97SSN 0124-0084 10.00 04/02/2018
06HM5 388K-06HM5 0126-0011 05.00 02/02/2015
06HM5 388J-06HM5 0126-0056 35.00 03/02/2018
My query that is returning too many rows is below:
SELECT Registration.LMSCDonationLetterNeeded,
FinTransactions.LMSCDonateAmt,
Registration.RegNumber,
Registration.SwimmerID,
People.FirstName,
People.MI,
People.LastName,
People.Suffix,
People.Address1,
People.City,
People.StateAbbr,
People.Zip,
People.Country AS CountryCode,
Countries.Country,
DATE_FORMAT(FinTransactions.FinTrxDateTime, '%m/%d/%Y') AS DonationDate,
LMSCs.Name AS LMSCName,
Registration.LMSCID,
LMSCOfficers.FirstName AS RegistrarFirstName,
LMSCOfficers.LastName AS RegistrarLastName,
Aliases.EMailAlias AS RegistrarEMail
FROM
Registration
LEFT JOIN People USING (SwimmerID)
LEFT JOIN FinTransactions ON FinTransactions.SwimmerID = Registration.SwimmerID
LEFT JOIN LMSCs ON LMSCs.LMSCID = Registration.LMSCID
LEFT JOIN LMSCOfficers ON LMSCOfficers.LMSCID = Registration.LMSCID AND LMSCOfficers.OfficeID = 5
LEFT JOIN Aliases ON LMSCOfficers.AliasID = Aliases.AliasID
LEFT JOIN Countries ON People.Country = Countries.CountryCode
WHERE
Registration.LMSCDonationLetterNeeded = 1
AND Registration.LMSCID = 38
AND Registration.RegNumber IN ("3881-HYDR0", "3880-97SSN", "388K-06HM5")
ORDER BY
People.LastName,
People.FirstName,
People.MI
Any help on this would be GREATLY appreciated.
Try this solution:
SELECT Registration.LMSCDonationLetterNeeded,
FinTransactions.LMSCDonateAmt,
Registration.RegNumber,
Registration.SwimmerID,
People.FirstName,
People.MI,
People.LastName,
People.Suffix,
People.Address1,
People.City,
People.StateAbbr,
People.Zip,
People.Country AS CountryCode,
Countries.Country,
DATE_FORMAT(FinTransactions.FinTrxDateTime, '%m/%d/%Y') AS DonationDate,
LMSCs.Name AS LMSCName,
Registration.LMSCID,
LMSCOfficers.FirstName AS RegistrarFirstName,
LMSCOfficers.LastName AS RegistrarLastName,
Aliases.EMailAlias AS RegistrarEMail
FROM
Registration
LEFT JOIN People
USING (SwimmerID)
LEFT JOIN FinTransactions
ON FinTransactions.SwimmerID = Registration.SwimmerID
AND FinTransactions.RegistrationNum = Registration.RegNumber -- Added this condition
LEFT JOIN LMSCs
ON LMSCs.LMSCID = Registration.LMSCID
LEFT JOIN LMSCOfficers
ON LMSCOfficers.LMSCID = Registration.LMSCID
AND LMSCOfficers.OfficeID = 5
LEFT JOIN Aliases
ON LMSCOfficers.AliasID = Aliases.AliasID
LEFT JOIN Countries
ON People.Country = Countries.CountryCode
WHERE Registration.LMSCDonationLetterNeeded = 1
AND Registration.LMSCID = 38
AND Registration.RegNumber IN ("3881-HYDR0", "3880-97SSN", "388K-06HM5")
ORDER BY People.LastName,
People.FirstName,
People.MI
Explanation:
You have not mentioned one condition while joining Table Registristration and FinTransactions in ON Condition i.e. FinTransactions.RegistrationNum = Registration.RegNumber.

DBMS SQL Database Query

How to view this?
Problem: The oldest student per sProgram.
Table name is Student.
Columns are:
sID, sLast, sFirst, sMI, sProgram, sGender, sAge
Sample data:
sID sLast sFirst sMI sProgram sGender sAge
-------------------------------------------------------
001 Right Mc D BSIT M 26
002 Michael John G BSIT M 22
002 Franco James D BSCPE M 20
003 Step Ren D BSECE M 22
I want to display the oldest student of BSIT
The desired output is :
001 , Right, Mc, D, BSIT, M, 26
If you just want oldest student of 'BSID', you can try this:
SELECT *
FROM Student
WHERE sProgram = 'BSIT'
ORDER BY sAge DESC
LIMIT 1
Or if you want oldest student of each sProgram, you can do it like this:
SELECT t1.*
FROM Student t1
JOIN (
SELECT sProgram, MAX(sAge) AS sAge FROM Student GROUP BY sProgram
) t2 ON t1.sProgram = t2.sProgram AND t1.sAge = t2.sAge
-- WHERE t1.sProgram = 'BSIT'

Mysql Query use of AND

Contest
contest_id Launched name
-------------------------------------------------------------------------
1 0 Cont1
2 1 cont2
participants
participants_id group_id contest_id puser_id
-------------------------------------------------------------------------
1 1 1 555
2 1 1 666
fusers
fuser_id name
-------------------
555 Alex
666 sabrina
I need to write up a query that will select all users(name,groupid,contestname) who are participants of all contests
Details of all users are in fusers, where as contest has all contest hosted Launched should be 1 and participants is the table that contains id of users who have decided to participate in a contest.
fuser_id = puser_id
Want to find who is participating in contest_id=1 there group_id,contestname and name
Your description is not particularly legible, but I think this is what you want.
SELECT fusers.fuser_id, fusers.name, Contest.contest_id, Contest.name
FROM fusers
LEFT JOIN participants ON fusers.fuser_id = participants.participants_id
LEFT JOIN Contest ON Contest.contest_id = participants.contest_id
WHERE (Contest.Launched = 1)
From what I understand:
SELECT f.name, p.group_id, c.name contestName
FROM
participants p
JOIN fusers u on p.puser_id=u.fuser_id
JOIN contest c on p.contest_id = c.contest_id
WHERE c.launched = 1
AND c.contest_id = 1 /*or any other contest_id*/