I am a very beginner to mysql and sql in general. None of my search results brought the solution I am looking for. I have a database and need to format the output like in the example below. Unfortunately I can't change the database anymore since it's an old project of another employee.
The source table:
CustomerID serviceID ratingID employeeID
1 1 A 1
1 2 B 5
1 3 B 2
1 4 A 3
2 1 A 1
2 2 C 5
2 3 D 2
2 4 C 3
3 1 T 1
3 2 O 5
3 3 T 2
3 4 O 3
Each Customer has four entries, one for each service. They have set ratings and employees.
The output that I need (employeeID can be ignored):
CustomerID ServiceID=1 ServiceID=2 ServiceID=3 ServiceID=4
1 A B B A
2 A C D C
3 T O T O
I found solutions with group_concat(if())... in the select statement and grouping by customerID, which only brought the values of "1" in each field and not the related values. I tried tying lose selects together with union or subselects but none gave the result I am looking for. Does anybody have a beginner friendly help?
For this sample data you can use conditional aggregation:
select CustomerID,
max(case when serviceID = 1 then ratingID end) `ServiceID=1`,
max(case when serviceID = 2 then ratingID end) `ServiceID=2`,
max(case when serviceID = 3 then ratingID end) `ServiceID=3`,
max(case when serviceID = 4 then ratingID end) `ServiceID=4`
from tablename
group by CustomerID
See the demo.
Results:
| CustomerID | ServiceID=1 | ServiceID=2 | ServiceID=3 | ServiceID=4 |
| ---------- | ----------- | ----------- | ----------- | ----------- |
| 1 | A | B | B | A |
| 2 | A | C | D | C |
| 3 | T | O | T | O |
Related
Here is the table structure for Clinic has many Categories:
Clinic Category
id company_id | clinic_id | type
--- -----------------------------
1 -> 1 | 1 | pre
2 | 1 | pre
2 -> 1 | 2 | ext
2 | 2 | ext
3 -> 1 | 3 | pre
4 -> 2 | 4 | ext
5 -> 2 | 5 | pre
Here, I want to get all clinics in the output ordered by first pre then ext. But, sometimes I have check a condition against company_id (eg: 1, in the following try) and I don't want the clinics to be filtered out.
My Try
Clinic.joins("LEFT JOIN categories c ON c.clinic_id = clinics.id").
order("
CASE c.type
WHEN 'pre' THEN 0
WHEN 'ext' THEN 1
ELSE 2
END
").
where("c.company_id = 1 OR c.company_id IS NULL")
The problem is, as can be seen in the table structure, if I don't have a clinic that matches a category with company id 1, it gets filtered out, but, still I want that clinic to be part of output with type other.
Expected Output For Company ID = 1
Clinic Category
id company_id | clinic_id | type
--- -----------------------------
1 -> 1 | 1 | pre
3 -> 1 | 3 | pre
2 -> 1 | 2 | ext
4 -> NULL | NULL | NULL(or other)
5 -> NULL | NULL | NULL(or other)
Expected Output For Company ID = 2
Clinic Category
id company_id | clinic_id | type
--- -----------------------------
1 -> 2 | 1 | pre
5 -> 2 | 5 | pre
2 -> 2 | 2 | ext
4 -> 2 | 4 | ext
3 -> NULL | NULL | NULL (or other)
In the left join itself, filter the company_id like below
LEFT JOIN categories c ON c.clinic_id = clinics.id AND c.company_id = 1
Hope this would help you out.
I am trying to show one semesters aggregates in one column, the next semester's aggregates in the second column, and the third semesters aggregates in the third column. Also the real tables, I don't know how many status codes there are...
I have a semester table:
Id Semester
+----+----------+
| 1 Current |
| 2 Next |
| 3 2 Ahead |
+----+----------+
I have a simple project table:
Id Title Status termId
+----+--------+---------+--------+
| 1 A OK 1 |
| 2 B Bad 1 |
| 3 C OK 1 |
| 4 D Bad 2 |
| 5 E OK 2 |
| 6 F Bad 3 |
| 7 G OK 2 |
+----+--------+---------+--------+
This is the desired Output:
Status CurrentCount NextCount 2AheadCount
+---------+--------------+-----------+-------------+
| OK 2 1 0 |
| Bad 1 1 1 |
+---------+--------------+-----------+-------------+
What would you recommend I do to be able to achieve this?
You can use conditional aggregation with group by:
select status,
sum(case when termId = 1 then 1 else 0 end) CurrentCount,
sum(case when termId = 2 then 1 else 0 end) NextCount,
sum(case when termId = 3 then 1 else 0 end) 2AheadCount
from project
group by status
I have a question about FULL JOIN at MySql. I know that alternative is UNION but I have dificulties to combine them all.
I guess it would be already enough to get answer on 4 tables as 5th and 6th are same as in first 4.
Tables: Bill, Service, BS, Item, BI, Buyer
BS connect more Services to Bill and BI connect more Items to Bill. Buyer is 1:1 relation with Bill.
Tables example:
Bill:
----------------------
id | number | Buyer_id
1 | 12014 | 3
2 | 22014 | 2
3 | 32014 | 5
Services:
----------------------
id | cost
1 | 2
2 | 7
3 | 1
4 | 12
BS:
----------------------
id | Bill_id | Services_id
1 | 1 | 3
2 | 1 | 4
3 | 2 | 2
4 | 3 | 1
5 | 3 | 2
6 | 3 | 3
7 | 3 | 4
Item:
----------------------
id | cost
1 | 34
2 | 77
3 | 2
4 | 15
5 | 13
BI:
----------------------
id | Bill_id | Items_id
1 | 1 | 5
2 | 2 | 3
3 | 3 | 2
Buyer:
----------------------
id | name
1 | John
2 | Mary
3 | Dave
4 | Carl
5 | Jack
So far the closest I got to was that I used following SQL:
SELECT *
FROM Bill b
LEFT JOIN BI ON BI.Bill_id = b.id
LEFT JOIN BS ON BS.Bill_id = b.id
LEFT JOIN Item i ON i.id = BI.Item_id
LEFT JOIN Services s ON s.id = BS.Services_id
LEFT JOIN Buyer ON Buyer.id = b.Buyer_id
WHERE b.number = '12014'
Result gives me 2 Services and 1 duplicated Item but I want one Item and one NULL item (as only one item is attached to that bill.
Result that I get (just ids as it's shorter):
b.id | s.id | BS.id | i.id | BI.id | Buyer.id
1 | 3 | 1 | 1 | 5 | 3
1 | 4 | 2 | 1 | 5 | 3
And desired result in table:
b.id | s.id | BS.id | i.id | BI.id | Buyer.id
1 | 3 | 1 | 1 | 5 | 3
1 | 4 | 2 | NULL | NULL | 3 (or NULL, doesn't really matter)
I tried with others as well but I got even more rows than 2 (note that two are expected or if Bill.number=32014 then 4 rows).
Thank you!
You are taking the wrong approach.
The result of a query to a relational database is a relation - you may treat it as a rectangular matrix with rows and columns. Every row represents something (at least it should), every column represents an attribute and every cell represents the thing's attribute's value.
| [attr 1] | [attr 2]
-----------+---------------+-------------------------
[thing 1] | value | some value
[thing 2] | value | other value
[thing 3] | a value | yeah, a value
Now here is what you are trying to produce:
| [attr 1] | [attr 2] | | [other attr 3]
-----------+---------------+------------------+---------------------+-------------------
[thing 1] | value | some value | [other thing 1] | a value
[thing 2] | value | other value |
See? Attempting to return two relations with a single query. Not rectangular anymore, huh? Items and services are independent here, but you are trying to put them into a single row. Don't go this way, here are three queries for you:
-- Get bill/buyer details (1 row)
SELECT b.id, Buyer.id
FROM Bill b
LEFT JOIN Buyer ON Buyer.id = b.Buyer_id
WHERE b.number = '12014';
-- Get billed items (1 row per item)
SELECT BI.id, i.id
FROM Bill b
JOIN BI ON BI.Bill_id = b.id
JOIN Item i ON i.id = BI.Item_id
WHERE b.number = '12014';
-- Get billed services (1 row per service)
SELECT BS.id, s.id
FROM Bill b
JOIN BS ON BS.Bill_id = b.id
JOIN Services s ON s.id = BS.Services_id
WHERE b.number = '12014';
Note that item and services queries don't use left joins. You would like to return 0 rows if there are no items/services on the bill.
Then handle the results of them one by one in your application.
Edit:
Sometimes two (or more) entities share some common characteristics, for example in your application you could treat services and items as bill lines. In this case, this could be valid to retrieve all of them in a single query, but only this way using union:
-- Get bill lines (items and services)
SELECT BI.id AS bill_item_id, i.id AS item_id, NULL as bill_service_id, NULL as service_id
FROM Bill b
JOIN BI ON BI.Bill_id = b.id
JOIN Item i ON i.id = BI.Item_id
WHERE b.number = '12014';
UNION ALL
SELECT NULL AS bill_item_id, NULL AS item_id, BS.id as bill_service_id, s.id as service_id
FROM Bill b
JOIN BS ON BS.Bill_id = b.id
JOIN Services s ON s.id = BS.Services_id
WHERE b.number = '12014';
Which will return a result similar to what you originally expected:
BI.id | i.id | BS.id | s.id
5 | 1 | NULL | NULL
NULL | NULL | 1 | 3
NULL | NULL | 2 | 4
Note that:
each item and service is an individual bill line represented by a record. Don't try to artificially "compress" data across rows or columns
it's not the case in your schema, but most often there are also some shared attributes, like line id, quantity ordered or amount paid.
Lets say, I have two tables - people and bonus
------------
people
------------
people_id | company_id | job_id
1 | 1 | 2
2 | 1 | 4
3 | 2 | 1
4 | 2 | 3
5 | 3 | 5
------------
bonus
------------
job_id | bonus_id
1 | 101
2 | 102
3 | 103
Now, I want to have a joined table like the following
-------------
JOINED TABLE
-------------
people_id | company_id | job_id | bonus_id | no_of_bonus_for_company
1 | 1 | 2 | 102 | 1
2 | 1 | 4 | NULL | 1
3 | 2 | 1 | 101 | 2
4 | 2 | 3 | 103 | 2
5 | 3 | 5 | NULL | 0
I need to have the main search term in people_id as in -
SELECT p.people_id,
p.company_id,
p.job_id,
b.bonus_id
FROM people p
LEFT JOIN bonus b
ON p.job_id = b.job_id
WHERE p.people_id IN (1,2,3,4,5)
ORDER BY p.people_id ASC;
But how do I get the fifth column of the joined table? It actually counts the no. of bonus id's for each company id in the joined table itself.
I can only assume that you are telling us part of the picture so I will reserve judgement about the DB schema, normalization etc.
Given the presented facts you can retrieve the information in the following manner.
NOTE : This is TSQL syntax but I don't think that the mySQL syntax should be very different i.e. ISNULL -> IFNULL
SELECT p.people_id
, p.company_id
, p.job_id
, b.bonus_id
, ISNULL((
SELECT COUNT(pt.Job_Id)
FROM Bonus bt
INNER JOIN People pt
ON pt.job_Id = bt.job_Id
WHERE pt.company_Id = p.company_Id
GROUP BY pt.company_Id
), 0) AS no_of_bonus_for_company
FROM People p
LEFT JOIN Bonus b
ON p.job_Id = b.job_Id
I have three tables like follows:
products
id | name
1 | name1
2 | name2
3 | name3
attributes
id | name
1 | attr1
2 | attr2
3 | attr3
4 | attr4
5 | attr5
6 | attr6
7 | attr7
attr_rel Makes the relationship between prodcuts and attributes
attr_id | prod_id
1 | 1
1 | 2
2 | 1
2 | 2
3 | 3
4 | 2
4 | 3
5 | 1
5 | 2
5 | 3
What I want is to perform a query that returns all products ordered by the number of attributes they have in common with a given product.
Example: For product 3 query should return something like
id | name | num_attr_in_common
2 | product2 | 2
1 | product1 | 1
(Because product 3 shares attributes 4 and 5 with product 2 but only share attribute 5 with product 1)
Can anyone give me an help please?
select a1.prod_id,p.name,count(*) as num_attr_in_common
from attr_rel as a1
inner join (
select attr_id
from attr_rel
where prod_id = 3) as a2
on a1.attr_id = a2.attr_id and a1.prod_id <> 3
inner join products as p on p.id = a1.prod_id
group by a1.prod_id
order by num_attr_in_common desc