SQL Select with Conditional Fields (Subquerys maybe?) - mysql

I'm trying to set up an SQL View that returns all details of a contact. Name and Last name are saved in a Table "Person", the contact info is saved in "contact" and the type of contact info (email, phone 1, phone2) is saved in "contact_types".
I want to return all the information in 1 row but I can't really figure it out. So far my best result is with:
SELECT
Person.ID, Person.Title, Person.Firstname, Person.Lastname,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '1' AND ContactInfo.PersonID = Person.ID ) AS Phone_Business,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '2' AND ContactInfo.PersonID = Person.ID ) AS Phone_Private,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '3' AND ContactInfo.PersonID = Person.ID ) AS Phone_Mobile,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '5' AND ContactInfo.PersonID = Person.ID ) AS Email
FROM Person
This statement results in duplicate outputs - 4 identical rows, even with MAX(). It is apparently one row per subquery. How can I only receive 1 row per ID?
I'm quite new to SQL, any suggestions would be helpful!
Edit:
Sample Data:
Table Person:
ID
Title
Firstname
Lastname
1
Mr.
Tom
Selleck
2
Mr.
Fred
Miller
Table ContactInfo
PersonID
InfoText
ContactTypeID
1
tom.selleck#gmail.com
5
2
+1 12345 678
1
1
+1 98765 432
2
Table ContactTypeID
ID
InfoText
1
phone_business
2
phone_private
5
email
Expected Result:
ID
Title
Firstname
Lastname
Phone_Business
Phone_Private
Phone_Mobile
Email
1
Mr.
Tom
Selleck
NULL
+1 98765 432
NULL
tom.selleck#gmail.com
2
Mr.
Fred
Miller
+1 12345 678
NULL
NULL
NULL
It works so far, but I'd get each row 4 times.

You can do it like this:
SELECT P.ID, P.Title, P.Firstname, P.Lastname,
MAX(CASE WHEN C.ContactTypeID = '1' THEN C.InfoText END) AS Phone_Business,
MAX(CASE WHEN C.ContactTypeID = '2' THEN C.InfoText END) AS Phone_Private,
MAX(CASE WHEN C.ContactTypeID = '3' THEN C.InfoText END) AS Phone_Mobile,
MAX(CASE WHEN C.ContactTypeID = '5' THEN C.InfoText END) AS Email
FROM Person P
LEFT JOIN ContactInfo C
ON P.ID=C.PersonID
GROUP BY P.ID, P.Title, P.Firstname, P.Lastname;
Just a single LEFT JOIN between Person table and ContactInfo. The Person table here acts as a reference table. Then use MAX() with CASE expression (also possible with GROUP_CONCAT()) in SELECT.
Here's a demo fiddle

Related

Select records in Mysql if the other column is set to none

how can I get all the departments where is a ID is equal to manager column or if the manager is set to NULL it is equal to the supervisor.
table_department:
dept_id, dept_name, manager_id, supervisor_id
1 IT-admin 1 NULL
2 IT-hardware NULL 1
3 IT-system 4 1
4 Engineering 3 NULL
table_users:
user_id, username
1 username1
2 username2
3 username3
4 username4
5 username5
If in my condition the user is username1 and his id is 1, How to query and show IT-admin and IT-hardware department because username1 is the manager/supervisor of that departments.
Any help would be appreciated, Thank you.
Try this:
SELECT dept_id, dept_name, user_id, username
FROM table_department
INNER JOIN table_user ON COALESCE(manager_id, supervisor_id) = user_id;
I suggest aggregating by the coalesced value of the manager/supervisor ID value. Then, use conditional aggregation to pivot out the correct department. For users who are supervisors, choose the supervisor department, otherwise use the manager department.
SELECT COALESCE(manager_id, supervisor_id) AS user_id,
CASE WHEN COUNT(CASE WHEN supervisor_id IS NOT NULL THEN 1 END) > 0
THEN MAX(CASE WHEN supervisor_id IS NOT NULL THEN dept_name END)
ELSE MAX(CASE WHEN manager_id IS NOT NULL THEN dept_name END)
END AS dept_name
FROM table_department
GROUP BY 1;
Demo
By the way, it would probably help to better normalize your table design, and get a single column per user with possible role values. This would avoid the ugly CASE expressions in my answer.
You can either use OR in you query.
select * from table_department d
where exists (select 1 from table_users u
where u.username = 'username1'
and (u.id = d.manager_id or (d.manager_id is NULL and u.id = d.supervisor_id)))

Required SQL Query

I have been stuck on this for sometime now.
I have the following SQL Tables:
department table
Id Name
1 DeptA
2 DeptB
3 DeptC
users table
Id Name Dept
101 Alice 2
102 Bob 3
alpha table
Id Uid Title
501 101 HELLO
502 102 HEY
503 101 SQL
beta table
Id Uid Title
601 101 HELLO1
602 101 HEY1
603 102 SQL1
Explanation:
There's basically a users table which has all the users.
Each user has a department (Dept field)
Each user has some records, linked to it with Uid, in alpha and beta tables.
The result I want:
DeptA DeptB DeptC
0 4 2
I want the count of records in alpha and beta combined, grouped by Dept of the users whose records are there in these tables.
Can someone help me with the SQL query?
As per your table structure I've used dept id for retrieving result otherwise I used dept name. You can also use COALESCE function if you get NULL
-- MySQL
SELECT SUM(CASE WHEN d.id = 1 THEN COALESCE(total, 0) END) dept_A
, SUM(CASE WHEN d.id = 2 THEN COALESCE(total, 0) END) dept_B
, SUM(CASE WHEN d.id = 3 THEN COALESCE(total, 0) END) dept_C
FROM department d
LEFT JOIN (SELECT u.dept
, COUNT(1) total
FROM users u
INNER JOIN (SELECT uid
FROM alpha
UNION ALL
SELECT uid
FROM beta) t
ON u.id = t.uid
GROUP BY u.dept ) p
ON d.id = p.dept;
Please check url http://sqlfiddle.com/#!9/020b2/1

Mysql - how to sort results by values in columns?

I have two database tables customers which contains data about customers with the scheme like that:
mysql> SELECT * FROM customers;
customer_id created_at partner_id
1 "2019-08-20 09:17:58" cats
2 "2019-09-12 11:46:37" dogs
and customers_facts which keeps the customers facts in a form of fact_name and corresponding fact_value.
mysql> SELECT * FROM customers_facts;
customer_id fact_name fact_value
1 name Milton
1 city Milan
1 birthday "2019-08-20 09:17:58"
1 company Idaho
2 surname Bloom
2 name Orlando
3 name Milton
3 city Milan
3 birthday "2011-10-20 11:17:58"
3 company Chicago
I want to create a query to get all customer_id where name=Milton and city=Milan sorted by birthday and company. So in my example the results would be:
mysql> SELECT customer_id FROM ....
customer_id
1
3
I have a query which gets all the customers_id where name=Milton and city=Milan
SELECT cf.* FROM customers_facts cf
WHERE cf.customer_id IN (
SELECT cf.customer_id FROM customers_facts cf
WHERE (cf.fact_name,cf.fact_value) IN (('name','Milton'),('city','Milan'))
GROUP BY cf.customer_id
HAVING COUNT(*) = 2
)
But I have no idea on how to sort the results by fact_value How to do it ? Is it even possible with such scheme ?
This is a little tricky. You can't filter easily before aggregating. So, do the filtering in the having clause:
SELECT customer_id
FROM customers_facts
GROUP BY customer_id
HAVING SUM( fact_name = 'name' AND fact_value = 'Milton' ) > 0 AND
SUM( fact_name = 'city' AND fact_value = 'Milan' ) > 0
ORDER BY MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END) DESC,
MAX(CASE WHEN fact_name = 'company' THEN fact_value END)
Ues pivoting logic to turn out the birthday for each matching customer group:
SELECT customer_id
FROM customers_facts
WHERE (fact_name, fact_value) IN (('name', 'Milton'), ('city', 'Milan'))
GROUP BY customer_id
HAVING MIN(fact_name) <> MAX(fact_name)
ORDER BY MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END);
Other than the ORDER BY clause, I used a HAVING clause which ensures that both the matching name and city were present in each matching customer group.
Edit:
Here is your desired ORDER BY clause:
ORDER BY
MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END) DESC,
MAX(CASE WHEN fact_name = 'company' THEN fact_value END); -- ASC is default

How to get only one record instead of fetching 2 records when using IN

I am working on sql query in mysql for example I am inserting records like key and values pair
Student Table
StdId StuName phnNum
1 John 87678
I am storing detailed Info about that student in student_metadata table
S.NO FieldName FieldValue indexVal
1 college St.Anns 1
2 Address Arizona 1
3 IdNum 321 1
Now I want to fetch student_metadata
SELECT
student.stdId AS StdId
fieldValue AS fieldValue
FROM
Student
LEFT JOIN
Student_metadata ON Student.StdId = student_metadata.indexVal
Where
Student_metadata.fieldName IN ('College','IdNum')
Here my problem is If I use 'IN' it gives me 2 rows. like the below
StudentId fieldValue
1 St.Anns
1 321
I want to fetch like the below
StudentId fieldValue IdNum
1 St.Anns 321
Can any one suggest me in this
You can use conditional aggregation to pivot:
select
s.stdid,
max(case when m.fieldname = 'College' then fieldvalue end) as fieldvalue,
max(case when m.fieldname = 'IdNum' then fieldvalue end) as idnum
from student s
left join student_metadata m on s.stdid = m.indexval
where m.fieldname in ('College','IdNum')
group by s.stdid
You could use a pivot table style query:
SELECT
a.`StuName`,
COALESCE(b.`FieldVal`) as `college`,
COALESCE(c.`FieldVal`) as `IdNum`
FROM `Student` a
LEFT JOIN `student_metadata` b
ON b.`indexVal` = a.`StdId` AND b.`FieldName` = 'college'
LEFT JOIN `student_metadata` c
ON c.`indexVal` = a.`StdId` AND c.`FieldName` = 'IdNum'
WHERE a.`StdId` = 1
GROUP BY a.`StdId`

How display the count of associates at each rating?

I have a table called associate_ratings with the below structure:
id int(11) NO PRI auto_increment
associate varchar(10) NO
skill_id int(11) NO MUL
rating int(11) NO
updated_time datetime NO
This table holds the skills(skill_id) of the associate and their corresponding rating in that skill.
Rating column can take values (1,2,3)
I want to get the in each skill how many associates have got a particular rating, please find below output table structure:
Skill_id Rating1_count Rating2_count Rating3_count
Java 2 1 4
C# 3 2 2
This says in Java there are 2 associates with rating 1, 1 associates with rating 2 & 4 associates with rating 3
I tried the below query, but the output is not in the format I expect:
SELECT skill_id, rating, count(*) FROM associate_ratings a
WHERE updated_time = (
SELECT max(updated_time)
FROM skill_set.associate_ratings b
WHERE a.associate = b.associate
) GROUP BY a.skill_id, a.rating order by a.skill_id, a.rating;
Could you please let me know how to get the output in the format I want?
Use temporary table and case
SELECT skill_id, sum(rating_1), sum(rating_2), sum(rating_3)
FROM (
SELECT a.skill_id as skill_id,
case a.rating when '1' then 1 else 0 end as rating_1,
case a.rating when '2' then 1 else 0 end as rating_2,
case a.rating when '3' then 1 else 0 end as rating_3
FROM associate_ratings a
WHERE updated_time = (
SELECT max(updated_time)
FROM skill_set.associate_ratings b
WHERE a.associate = b.associate
) ) as t
GROUP BY skill_id
ORDER BY skill_id;
select Skill_id ,
count(case when rating = 1 then 1 else null end) as Rating1_count ,
count(case when rating = 2 then 1 else null end) as Rating2_count ,
count(case when rating = 3 then 1 else null end) as Rating3_count
from associate_ratings b
left join associate_ratings a
on b.Skill_id = a.Skill_id
group by Skill_id
That would be something like this:
SELECT
skill_id,
sum(IF(rating=1,1,0)) as Rating1_count,
sum(IF(rating=2,1,0)) as Rating2_count,
sum(IF(rating=3,1,0)) as Rating3_count
FROM associate_ratings
GROUP BY skill_id
ORDER BY skill_id;
I think it's the most simple solution possible here.