How to select from Table by group - mysql

I have table like this:
idjob category customer
1560 001 1
1560 0010 1
1560 002 1
1562 001 2
1562 0010 2
1563 001 2
1563 002 3
1563 0010 3
1563 004 3
One customer can have more idjobs.
Every single jobs contain a group of categories.
I would like to select the number of customer that have two or more specifics category by its jobs.
Probably it is a simple query.
How can i do that?
Thank you

You could count the number of different idjob per customer:
SELECT customer, GROUP_CONCAT(idjob) AS jobs
FROM mytable
GROUP BY customer
HAVING COUNT(DISTINCT idjob) > 1

You can get the customers meeting the condition using:
select customer
from t
group by customer
having min(category) <> max(category);
To get the number, use a subquery:
select count(*)
from (select customer
from t
group by customer
having min(category) <> max(category)
) c;
Note that "2" is a special case. It can be handled by comparing the max() and min(). The more general solution would use count(distinct category) >= 2. However, count(distinct) is more resource intensive than simpler aggregation functions.
EDIT:
Perhaps I misread the original question.
If you want customers with a particular set of categories, you can still use GROUP BY and HAVING:
select customer
from t
where category in (. . .)
group by customer
having count(*) = <n>;
Here . . . is the list of categories you want. <n> is the number of categories in that list.
If you can have duplicate customer/category pairs, then use this having:
having count(distinct category) = <n>

Related

SQL nested query under WHERE

One of the test questions came by with following schemas, to look for the best doctor in terms of:
Best scored;
The most times/attempts;
For each medical procedures (in terms of name)
[doctor] table
id
first_name
last_name
age
1
Phillip
Singleton
50
2
Heidi
Elliott
34
3
Beulah
Townsend
35
4
Gary
Pena
36
5
Doug
Lowe
45
[medical_procedure] table
id
doctor_id
name
score
1
3
colonoscopy
44
2
1
colonoscopy
37
3
4
ulcer surgery
98
4
2
angiography
79
5
3
angiography
84
6
3
embolization
87
and list goes on...
Given solution as follow:
WITH cte AS(
SELECT
name,
first_name,
last_name,
COUNT(*) AS procedure_count,
RANK() OVER(
PARTITION BY name
ORDER BY COUNT(*) DESC) AS place
FROM
medical_procedure p JOIN doctor d
ON p.doctor_id = d.id
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
GROUP BY
name,
first_name,
last_name
)
SELECT
name,
first_name,
last_name
FROM cte
WHERE place = 1;
It'll mean a lot to be clarified on/explain on how the WHERE clause worked out under the subquery:
How it worked out in general
Why must we match the two pp.name and p.name for it to reflect the correct rows...
...
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
...
Thanks a heap!
Above is join with doctor and medical procedure and group by procedure name and you need doctor names with most attempt and best scored.
Subquery will join by procedure avg score and those who have better score than avg will be filtered.
Now there can be multiple doctor better than avg so taken rank by procedure count so most attempted will come first and then you taken first to pick top one

Having the number of line having a specifc ID without group by SQL

I have a 'billing' table which represent all instances of billings from my subscribers. A subscriber can have multiple billings.
I have a simple SQL request which is :
SELECT count(billing_id),subscriber_id
FROM billing
group by subscriber_id
As a result I have a list of all my subscribers with the number of billings they've made.
I want to have a list of all the billings no grouped by subscribers, but I want the result of the previous request appearing in each lines.
Example:
Result of my previous request:
sub_id nb_billings
1 3
2 2
What I want :
sub_id nb_billings
1 3
1 3
1 3
2 2
2 2
Thanks
I'd do it like this;
SELECT
b.subscriber_id
,a.billing_count
FROM billing b
JOIN (SELECT subscriber_id, count(billing_id) billing_count FROM billing GROUP BY subscriber_id) a
ON b.subscriber_id = a.subscriber_id
The subquery works out the count of billing_id by subscriber, this is then joined to all rows of your original table (using subscriber_id). This should give the result you're after.
You can use a subquery to do that:
SELECT
(SELECT count(t2.billing_id) FROM billing t2 WHERE t2.subscriber_id = t1.subscriber_id),
t1.subscriber_id
FROM billing t1
I guess this should suffice :
SELECT s.subscriber_id,
s.billing_id,
s.TotalCount
FROM (
SELECT subscriber_id,
billing_id,
COUNT(billing_id) AS TotalCount
FROM BILLING
GROUP BY subscriber_id,
billing_id
) s
GROUP BY s.subscriber_id,
s.TotalCount,
s.billing_id
ORDER BY s.subscriber_id
This should give you the result as follows :
subscriber_id billing_id TotalCount
1 10a 2
1 10b 2
1 10c 1
2 10a 1
2 10b 1
2 10c 3
2 10d 1
You can see this here -> http://rextester.com/AVVS23801
Hope this helps!!
select subscriber_id,count(billing_id)over(partition by subscriber_id)
from billing
will do just that.

How can you decide which records of a JOINED table remains after a GROUP BY?

I'm trying to LEFT JOIN two tables and GROUP BY a field of the first table in MySQL.
If there are multiple rows in the second table for each record in the first one, a GROUP BY eliminates all records but one of the right table. Is there a way to determine which one it is?
To give you a specific example, I want to get a list of users, joined with the IDs of their (for example) most expensive purchases (or most recent purchases, or whatever..) It seems like an easy task, but I'm frustrated and have asolutely no idea how to do it!
Table 1: Users
userId, userName
1 Frank
2 Sarah
3 Tim
Table 2: Purchases
orderId, userId, value
1 3 14.99
2 2 9.99
3 3 79.99
4 1 2.99
5 2 14.99
SELECT * FROM Users LEFT JOIN Purchases ON Users.userId = Purchases.userId
will return:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
2 Sarah 5 14.99
3 Tim 1 14.99
3 Tim 3 79.99
Now if I GROUP BY userId the result will be:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
3 Tim 1 14.99
Is there a way to decide in this case which orderId is kept or is there a completely other and better way to do this?
I have tried some things like MAX() but this will always only return the highest value of the whole table, not individually for each user.
Thank you in advance, you awesome stackoverflow-community!
best Florian
In strict SQL this Query would not be valid as in a Group by context u should select only fields contained in the group by clause or aggregates.
Mysql however allows this syntax and handles it as "i dont care about this fields", you can not define which of the rows values is selected then.
But you can do it with a query like this:
SELECT u.*,p.* FROM Users u LEFT JOIN
( SELECT userId, max(value) as max_value FROM Purchases GROUP BY userId) p ON u.userId = p.userId
If you want to get the max() pusrchase per user preserving the order like orderid etc you may need to do this
select
u.userId,
u.userName,
p.orderId,
p.value
from Users u
inner join Purchases p on
p.userId = u.userId
inner join
(
select orderId,
max(value) as value,
userId
from Purchases
group by userId
)o
on o.userId = p.userId
AND o.value = p.value
group by u.userId
http://sqlfiddle.com/#!2/1afda/9

MYSQL sum of item purchases and its 'up'/'down' votes are multiplied by a factor of 22?

I'm trying to count the number of purchases and 'up'/'down' votes for all items that match a given search term. Unfortunately, as I've set up my query now, the purchases and vote counts are multiplied by a mysterious factor of 22 that I can not figure out where it comes from.
As an example for one of the items: the purchases, up, and down votes should be 7, 2, and 1 respectively but they are instead 154, 44, and 22.
here's my SQL code:
SELECT *
sum(purchaseyesno) as tots,
sum(rating=1) as yes,
sum(rating=0) as no
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by item_id
order by tots desc, yes desc
Here's some sample data:
subject tots yes no full_name
album by joe 154 44 22 joe smith
album by fred 88 44 0 fred jones
Here's how i'd like the data to look:
subject tots yes no full_name
album by joe 7 2 1 joe smith
album by fred 4 2 0 fred jones
Would someone be able to help me figure out what is going on here? I can't figure out this factor of 22 issue which persists despite changing the group by and other things (meaning, this 22 number is independent of the # of returned rows).
You don't show your schema but it might help to use a subquery:
SELECT subject, tots, yes, no, fullnane
FROM (
SELECT item_id, SUM(purchaseyesno) AS tots, SUM(rating=1) AS yes, SUM(rating=0) AS no
FROM items_purchased
GROUP BY item_id
) i
JOIN items ON i.item_id = items.item_id
JOIN accounts ON items.account_id=accounts.account_id
WHERE subject LIKE '%album by joe%' OR description LIKE '%album by joe%'
ORDER BY tots DESC, yes DESC
Firstly, don't do select * if you're grouping by. Remember you must group by all non-aggregated fields.
Secondly, the high amount of results must be comming from those joins. Remove the group by and the aggregated columns and inspect the results and you'll see why you're getting so many records.
Finally... you're missing a where clause there...
SELECT subject,
sum(purchaseyesno)/22 as tots,
sum(CASE WHEN rating=1 THEN 1 END)/22 as yes,
sum(CASE WHEN rating=0 THEN 1 END)/22 as no,
full_name
from items
join items_purchased
on items_purchased.item_id=items.item_id
join accounts
on items.account_id=accounts.account_id
like subject='%album by joe%' or description='%album by joe%'
group by subject,full_name
order by tots desc, yes desc
In this query,the SUM function basically works on the columns purchaseyesno,rating=1,rating=0.
For eg.
Subject purchaseyesno rating full_name
1 5 1 Mark
1 6 0 Mark
2 7 1 Robert
2 8 0 Robert
The above query will return as below.
Subject tots yes no full_name
1 11 1 1 Mark
2 15 1 1 Robert

complex query in mysql

i have three tables in mysql like this,
triz_sti
stu_id name
-----------------
1 x1
2 x2
triz_sub
sub_id sub_name
------------------
1 english
2 maths
3 science
triz
stu_id sub_id marks
-------------------------
1 1 23
1 2 56
1 3 83
2 1 78
2 2 23
2 3 50
i want the result like
display all subject with higest mark in perticular subject with student name,
max_marks sub_name student_name
--------------------------------------
78 english x2
56 maths x1
83 science x2
so please help for this output that i want, i have tried but i m not get it desire output.
How about something like this?
SELECT
t.stu_id, t.sub_id, t.marks
FROM
triz t
JOIN (SELECT sub_id, MAX(marks) max_mark FROM triz GROUP BY sub_id) a ON (a.sub_id = t.sub_id AND a.max_mark = t.marks)
Of course you'll need to join it with lookup tables for names.
Have to say, it's early here so I might have missed something.
BR
The general, simplified syntax in this case is
SELECT stuff FROM joined tables ORDER BY whatever
The easiest is the ORDER BY: you want to sort descending by marks, so you ORDER BY marks DESC.
Where do the data come from? From triz, joined to the others. So
triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
And you want to display the marks.
So you get
SELECT marks, sub_name, name AS student_name
FROM triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
ORDER BY marks DESC
.
The rest I leave to you. :-)