Count only if field is filled and same as other field value - mysql

I have a question about an sql query i want to make. Supose i have an column with the follow values in table: school with column: grades.
SUI grades | Score
2 9 2
2 9
5 4 1
5 4 1
5 4
6 1 1
6 1
And Table Grade_scores
id score_1 score_2
1 4 1
Now i wan't an output where it groups the SUI that counts grades only if Score is filled and school.grades is same as Grade_scores.score_1 OR Grade_scores.score_2 . So my output will be:
SUI Count
5 2
6 1
The code so far...
SELECT SUI, Count(Grades)
FROM mytable
WHERE Score <> ''
GROUP BY SUI

You need to JOIN your school and grade_scores tables on grades being IN the two values in grade_scores:
SELECT s.SUI, COUNT(s.grades) AS `Count`
FROM grade_scores gs
JOIN school s ON s.Score IS NOT NULL AND s.grades IN (gs.score_1, gs.score_2)
GROUP BY s.SUI
Output:
SUI Count
5 2
6 1
Demo on dbfiddle
Note This query assumes the empty values in the Score column are NULL. If they are an empty string, replace s.Score IS NOT NULL with s.Score != ''

I prefer to do this using exists, because a join approach would double count if there are duplicate values in score_1/score_2:
select s.sui, count(*)
from scores s
where s.score is not null and
s.score in (select 1
from grade_scores gs
where s.grades in (gs.score_1, gs.score_2)
)
group by s.sui;

(untested):
SELECT SUI, count(GRADES) As Count
FROM school
INNER JOIN Gradescores ON s.grades = score_1 OR s.grades = score_2
WHERE Score>'' AND NOT Score IS NULL
GROUP BY SUI

Related

Select multiple tables with only unique users and ordered by latest id

I have 2 tables, first one is called members:
id name show
1 John 1
2 Wil 1
3 George 1
4 Chris 1
Second is called score:
id user_id score
1 1 90
2 1 70
3 2 55
4 3 30
5 3 40
6 3 100
7 4 30
user_id from score is the id of members.
What I want is to show a scorelist with unique members.id, ordered by score.score and order by the latest score.id.
I use the following code:
SELECT members.id, members.show, score.id, score.user_id, score.score FROM members
INNER JOIN score ON score.user_id = members.id
WHERE members.show = '1'
GROUP BY score.user_id
ORDER BY score.score DESC, score.id DESC
The output is not ordered by the latest score.id, but it does show only unique user_id's:
id user_id score
1 1 90
3 2 55
4 3 30
7 4 30
It should be like:
id user_id score
6 3 100
2 1 70
3 2 55
7 4 30
I hope you can help me
You could use:
with cte as (
select id,
user_id,
score,
row_number() over(partition by user_id order by id desc) as row_num
from score
) select cte.id,user_id,score
from cte
inner join members m on cte.user_id=m.id
where row_num=1
order by score desc;
Demo
If your MySQL server doesn't support windows function, use:
select s.id,s.user_id,s.score
from score s
inner join members m on s.user_id=m.id
where s.id in (select max(id) as id
from score
group by user_id
)
order by score desc;
Demo

SQL Query to return rows where a column value appears multiple time

I'm creating a simple database which will allow me to track snooker results, producing head to head results between players. Currently I have 3 tables: (Player, Fixture, Result)
PlayerID PlayerName
1 Michael Abraham
2 Ben Mullen
3 Mark Crozier
FixtureID Date TableNo Group
1 07/12/2015 19:00:00 12 0
2 08/12/2015 12:00:00 9 0
ResultID FixtureID PlayerID FramesWon
1 1 1 3
2 1 3 1
3 2 1 5
4 2 2 1
I would like a query which returns all rows in the result table for fixtures which took place between players 1 and 3. Currently my query is:
SELECT *
FROM Result
WHERE PlayerID IN (1,3);
This returns the first 3 rows of the result table - when I'm only looking for the top 2 rows because they share the same FixtureID. Is there an easy way to remove the third row from this query result, or should I reconsider my database design? Any help would be appreciated.
One solution is to use a GROUP BY query, grouping by FixtureID and counting the rows for each FixtureID. This query will select all FixtureIDs with both players 1 and 3:
select
FixtureID
from
Results
where
PlayerID IN (1,3)
group by
FixtureID
having
count(*)=2
then to get the record from the Results table you can use this query:
select *
from Results
where FixtureID IN (
select FixtureID
from Results
where PlayerID IN (1,3)
group by FixtureID
having count(*)=2
)
You could join your fixtures table twice, like this:
select
*
from
Result as R1
join Result as R2 on R1.FixtureID = R2.FixtureID
where
R1.PlayerID in (1,3)
AND R2.PlayerID in (1,3)
AND R1.PlayerID != R2.PlayerID
group by
R1.FixtureID
;
Or, since it's a bit messy now, show it like a snooker score display often is shown:
select
R1.FixtureID, R1.PlayerID as player1, R1.FramesWon as player1_frames, R1.FramesWon+R2.FramesWon as total_frames, R2.FramesWon as player2_frames, R2.PlayerID as player2
from
Result as R1
join Result as R2 on R1.FixtureID = R2.FixtureID
where
R1.PlayerID in (1,3)
AND R2.PlayerID in (1,3)
AND R1.PlayerID != R2.PlayerID
group by
R1.FixtureID
;

MySQL - add column to table and insert "tag" if order is from new customer

I have simple table:
Order_ID Client_ID Date Order_Status
1 1 01/01/2015 3
2 2 05/01/2015 3
3 1 06/01/2015 3
4 2 10/01/2015 3
5 1 12/01/2015 4
6 1 05/02/2015 3
I want to identify orders from new customers which are orders in same month in which that customer made first order with Order_Status = 3
So the output table should look like this:
Order_ID Client_ID Date Order_Status Order_from_new_customer
1 1 01/01/2015 3 yes
2 2 05/01/2015 3 yes
3 1 06/01/2015 3 yes
4 2 10/01/2015 3 yes
5 1 12/01/2015 4 NULL
6 1 05/02/2015 3 no
I wasn't able to successfully figure out the query. Thanks a lot for any help.
Join with a subquery that gets the date of the first order by each customer.
SELECT o.*, IF(MONTH(o.date) = MONTH(f.date) AND YEAR(o.date) = YEAR(f.date),
'yes', 'no') AS order_from_new_customer
FROM orders AS o
JOIN (SELECT Client_ID, MIN(date) AS date
FROM orders
WHERE Order_Status = 3
GROUP BY Client_ID) AS f
ON o.Client_ID = f.Client_ID
Use a CASE statement along with a SELF JOIN like below
select t1.*,
case when t1.Order_Status = 3 and MONTH(t1.`date`) = 1 then 'yes'
when t1.Order_Status = 3 and MONTH(t1.`date`) <> 1 then 'no'
else null end as Order_from_new_customer
from order_table t1 join order_table t2
on t1.Order_ID < t2.Order_ID
and t1.Client_ID = t2.Client_ID;
If your order table gets big, the solutions from Rahul and Barmar will tend to get slow.
I would hope your shop will get many orders and you will run into performance trouble ;-). So I would suggest marking the very first order of a new customer with a tinyint column, and when you have the comfort of a tinyint, you could code it like:
0 : unknown
1 : very first order
2 : order in first month
3 : order in "grown-up" mode.
The very first order you could probably mark easily, everyone loves a bright new customer enough to store this event somehow during first ordering. The other orders you can identify in a background job / cronjob by there "0" for unknown, or you mark your old customers and store the "3" on their orders.
The result-set can be achieved without any table-join or subquery:
select
if(Order_Status<>3,null,if(#first_date:=if(#prev_client_id!=Client_ID,month(date),#first_date)=month(date),"yes","no")) as Order_from_new_customer
,Order_ID,Client_ID,date,Order_Status,#prev_client_id:=client_id
from
t1,
(select #prev_client_id:="",#first_date:="")t
order by Client_ID ,date
One extra column added for computation and order by clause is used.
Verify result at http://sqlfiddle.com/#!9/83c29f/24

Sum values in mysql table where userid is identical

I have read the different answers here on SO, but I am stuck on this question. Please help.
I have this mysql view named "activeuser":
userid COUNT(*) ACRONYM
1 23 admin
2 2 doe
3 4 tompa
12 4 Marre
13 1 Mia
1 2 admin
3 1 tompa
12 1 Marre
13 1 Mia
2 1 doe
3 1 tompa
12 1 Marre
How can I sum the COUNT column so that I get the following wanted result?
userid COUNT(*) ACRONYM
1 25 admin
2 3 doe
3 6 tompa
12 6 Marre
13 1 Mia
EDITED:
I used this query to create the view:
CREATE VIEW activeuser AS
(SELECT boats_comments.userid, COUNT(boats_comments.userid), boats_user.acronym, boats_user.email
FROM boats_comments
INNER JOIN boats_user
ON boats_comments.userid = boats_user.id
GROUP BY boats_comments.userid
ORDER BY COUNT(boats_comments.userid) DESC)
UNION ALL
(SELECT boats_answers.userid, COUNT(boats_answers.userid), boats_user.acronym, boats_user.email
FROM boats_answers
INNER JOIN boats_user
ON boats_answers.userid = boats_user.id
GROUP BY boats_answers.userid
ORDER BY COUNT(boats_answers.userid) DESC)
UNION ALL
(SELECT boats_questions.userid, COUNT(boats_questions.userid), boats_user.acronym, boats_user.email
FROM boats_questions
INNER JOIN boats_user
ON boats_questions.userid = boats_user.id
GROUP BY boats_questions.userid
ORDER BY COUNT(boats_questions.userid) DESC)
My goal is to see which users are the most active by checking the number of comments, questions and answers... but I got stuck...
As the results in your view has duplicates I guess the underlying code for the view is grouping on something it maybe shouldn't be grouping on.
You can get the results you want by applying SUM to it:
select userid, sum("whatever column2 is named") as "Count", Acronym
from activeuser group by userid, Acronym;
select userid, count(*) from activeuser group by userid;

MySQL join with a subquery

I have three tables and am trying to get info from two and then perform a calculation on the third and display all the results in one query.
The (simplified) tables are:
table: employee_work
employee_id name
1 Joe
2 Bob
3 Jane
4 Michelle
table: carryover
employee_id days
1 5
2 10
3 3
table: timeoff
employee_id time_off_type days
1 Carryover 2
1 Leave 3
1 Carryover 1
2 Sick 4
2 Carryover 4
3 Leave 1
4 Sickness 4
The results I would like are:
employee_id, carryover.days, timeoff.days
1 5 3
2 10 4
3 3 0
However when I run the query, whilst I get the correct values in columns 1 and 2, I get the same number repeated in the third column for all entries.
Here is my query:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days) FROM timeoff WHERE timeoff.time_off_type = 'Carryover'
AND timeoff.start_date>='2013-01-01') AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id Left Join
timeoff On employee_work.employee_id = timeoff.employee_id Left Join
Where
carryover.carryover > 0
Group By
employee_work.employee_id
I have tried to group by in the sub query but I then get told "Subquery returns more than one row" - how can I ensure that the sub query is respecting the join so it only looks at each employee at a time so I get my desired results?
The answer to your question is to use a correlated subquery. You don't need to mention the timeoff table twice in this case:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days)
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01' and
timeoff.employee_id = employee_work.employee_id
) AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id
Where
carryover.carryover > 0
Group By
employee_work.employee_id;
An alternative structure is to do the grouping for all employees in the from clause. You can also remove the employee_work table, because it does not seem to be being used. (You can use carryover.employee_id for the id.)
Select co.employee_id, co.carryover, et.taken
From carryover c Left Join
(SELECT employee_id, SUM(days) as taken
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01'
) et
on co.employee_id = et.employee_id
Where c.carryover > 0;
I don't think the group by is necessary. If it is, then you should probably have an aggregation function in the original query.