count occurence of rows with specific status without using subquery - mysql

Here is my table
loan_id bid_id lender_id borrower_id amount interest duration loan_status
1 1 60 63 300.00 12.00 3 'completed'
2 2 61 63 300.00 12.00 3 'completed'
3 3 62 63 300.00 12.00 3 'pending',
4 1 62 63 300.00 12.00 3 'pending'
7 4 60 63 300.00 12.00 3 'completed'
I want to pull only those bid_id whose loan_status of all records is completed. It means if there is any record of bid_id with status pending then it will not pull that record.
I am using the followin query that is working fine:
SELECT bid_id
FROM loan
WHERE bid_id NOT IN (
SELECT l.bid_id
FROM loan l
WHERE l.`loan_status` = 'pending'
AND l.bid_id = bid_id
GROUP BY l.`bid_id`
HAVING COUNT(l.`bid_id`)>= 1
)
GROUP BY bid_id
Is there any other way in which we can get desired result without using sub query.

You can readily do this with group by and having:
select bid_id
from loan
group by bid_id
having sum(loand_status = 'pending') = 0

Related

Cumulative sum of counts is not working for distinct values of users

Table "users":
id
name
email
created_at
46
FSDSD2
FSDSD2#thebluedot.co
2022-05-29 14:19:21
47
Fxz3
Fxz3#gmail.com
2022-05-30 20:12:15
48
Fgh3
Fgh3#gmail.com
2022-05-31 20:12:15
49
Fghxc3
Fghxc3#gmail.com
2022-06-01 20:12:15
50
Fdx3
Fdx3#gmail.com
2022-06-02 20:12:15
51
Fg3q3
Fg3q3#gmail.com
2022-06-03 20:12:15
88
Fbhgt
Fbhgt#gmail.co
2022-05-23 16:38:41
112
Fht
Fht#gmail.com
2022-05-24 16:19:23
113
Y14gss
Y14gss#gmail.com
2022-05-25 16:42:44
114
sfhf
sfhf#gmail.com
2022-05-26 12:10:40
115
A2czu
A2czu#thebluedot.co
2022-05-27 14:00:31
116
Cc1sn
Cc1sn#gmail.com
2022-05-28 12:04:56
Table "oxygen_point_earns":
id
user_id
oxygen_point
created_at
2
116
50.00
2022-05-23 17:49:30
3
113
10.00
2022-05-24 07:49:46
4
114
10.00
2022-05-25 07:50:42
5
46
50.00
2022-05-26 07:55:19
6
47
40.00
2022-05-27 13:28:17
7
48
30.00
2022-05-28 13:32:19
8
49
10.00
2022-05-29 13:32:19
9
50
5.00
2022-05-30 13:32:19
10
51
10.00
2022-05-31 13:32:19
11
88
20.00
2022-06-01 13:32:19
12
112
50.00
2022-06-02 13:32:19
13
115
10.00
2022-06-03 13:32:19
14
112
20.00
2022-06-03 16:32:19
I have two tables:
"users", which stores users basic information
"oxygen_point_earns", which stores oxygen points earned by specific users
The "users" table has 12 rows, though the "oxygen_point_earns" table contains 13 records, which means that one user can win points even more than once.
I was trying to made some calculation between those tables (e.g. dividing the total of weekly gained points by the weekly users cumulative sum, for each user). The problem occurs when I attempt to get the users cumulative sum.
SELECT STR_TO_DATE(CONCAT(YEARWEEK(op.created_at), ' Sunday'), '%X%V %W') AS week,
SUM(COUNT(*)) OVER(ORDER BY MIN(op.created_at)) AS user_count,
SUM(op.oxygen_point) AS op_weekly
FROM users us
LEFT JOIN oxygen_point_earns op
ON us.id = op.user_id
GROUP BY week
ORDER BY week
This query gets me the following output:
As you can see, even though the points are correctly computed, the total user count is wrong at the second row: it should be 12 instead of 13 (First week I got 6 users then next week 6 more users registered. So my total user count is 12. On second row I should get 12.)
I tried DISTINCT, GROUP_CONCAT but didn't work. How can I fix this query to get true result of users counts?
One straightforward option is to separate the two operations (aggregation and windowing) using a subquery/cte:
WITH cte AS (
SELECT STR_TO_DATE(CONCAT(YEARWEEK(op.created_at), ' Sunday'), '%X%V %W') AS week,
COUNT(DISTINCT user_id) AS cnt,
SUM(op.oxygen_point) AS op_weekly
FROM users us
LEFT JOIN oxygen_point_earns op ON us.id = op.user_id
GROUP BY week
)
SELECT week,
SUM(cnt) OVER(ORDER BY week) AS user_count,
op_weekly
FROM cte
ORDER BY week

how to generate student attendance percentage per course, when they have specific day in a week

hi guys i really newbie in sql, i need help to generate percentage of attendance, here is the table:
Table Schedule
Schedule_ID Course_ID Lecture_ID Start_Date End_Date Course_Days
1 1 11 2019-09-09 2019-12-08 2,4,6
2 3 4 2019-09-09 2019-12-08 3,4
3 4 13 2019-09-09 2019-12-08 2,5
4 5 28 2019-09-09 2019-12-08 3
5 2 56 2020-01-27 2020-04-26 2,4
6 7 1 2020-01-27 2020-04-26 4,5
7 1 11 2020-01-27 2020-04-26 2,4,6
8 7 22 2020-01-27 2020-04-26 2,3
9 8 56 2020-01-27 2020-04-26 5
10 3 37 2020-01-27 2020-04-26 5,6
Reference of days of week used in this data.
1: Sunday, 2:Monday, 3:Tuesday, 4:Wednesday, 5:Thursday, 6:Friday, 7:Saturday
Table course_attendance
ID STUDENT_ID SCHEDULE_ID ATTEND_DT
1 1 2 2019-09-10
2 1 2 2019-09-11
3 1 2 2019-09-17
4 1 2 2019-09-18
......
46 2 1 2019-12-02
47 2 1 2019-09-11
48 2 1 2019-09-18
49 2 1 2019-09-25
50 2 1 2019-10-09
51 2 1 2019-10-16
....
111 6 1 2019-09-23
112 6 1 2019-09-30
113 6 1 2019-10-07
114 6 1 2019-10-14
table student
ID NAME
1 Jonny
2 Cecilia
3 Frank
4 Jones
5 Don
6 Harry
i need to show up like this :
STUDENT_ID NAME Course_ID Attendance rate
1 Jonny 1 82%
2 Cecilia 1 30%
3 Frank 3 100%
4 Jones 2 100%
5 Don 2 25%
6 Harry 4 40%
EDIT this my last step to get percentage:
result:
with main as (
select ca.STUDENT_ID,
ca.SCHEDULE_ID,
s.COURSE_ID,
co.NAME as course_name,
st.NAME,
count(ca.ID) as total_attendance,
((CHAR_LENGTH(s.COURSE_DAYS) - CHAR_LENGTH(REPLACE(s.COURSE_DAYS , ',', '')) + 1) * 13) as attendance_needed
from univ.course_attendance ca
left join univ.schedule s on ca.SCHEDULE_ID = s.ID
left join univ.student st on ca.SCHEDULE_ID = st.ID
left join univ.course co on ca.SCHEDULE_ID = co.ID
group by ca.STUDENT_ID, ca.SCHEDULE_ID
)
select *,total_attendance/attendance_needed as attendance_percentage
from main
order by 1,2;
This can be done following three steps.
Step 1: Calculate the total number of days a particular course of a schedule has. It's a good thing the start_date is always on Monday and the end_date is always on Sunday, which makes the week complete and saves some trouble. By calculating the total number of weeks a course go through and the number of days a week has for that course, we can get the total number of days a particular course of a schedule has.
Step 2:Calculate the total number of days a student for a schedule. This is done fairly easily. Note: As the majority part of the table has been skipped and the OP has yet to provide the complete data set, I could only have 14 existing rows provided.
Step 3: Calculate the percentage for the attendance using the result from the above two steps and get other required columns.
Here is the complete statement I wrote and tested in workbench:
select t2.student_id as student_id,`name`,course_id, (t2.total_attendance/t1.total_course_days)*100 as attendance_rate
from (select schedule_id,course_id,
length(replace(course_days,',',''))*(week(end_date)-week(start_date)) as total_course_days
from Schedule) t1
JOIN
(select count(attend_dt) as total_attendance,student_id,schedule_id
from course_attendance group by student_id, schedule_id) t2
ON t1.schedule_id=t2.schedule_id
JOIN
student s
ON t2.student_id=s.id;
Here is the result set ( the attendance_rate is not nice due to the abridged course_attendance table):
student_id, name, course_id, attendance_rate
2, Cecilia, 1, 15.3846
6, Harry, 1, 10.2564
1, Jonny, 3, 15.3846

How to get total appointment durations from this table using sql

I have 3 mysql tables:
appointments
id slot_id patient_name doctor_id deleted_at
1 11 Tasin 23 2019-10-10
2 12 Nawaz 22 null
3 13 Rakib 23 null
4 14 Hossen 23 null
5 15 Aritra 24 null
6 16 Anik 22 null
7 17 Manik 22 null
doctors
id status doctor_name
22 1 Khaled
23 1 Hasan
24 0 Rumi
slots
id date duration time
11 2019-10-10 2900 01:01
12 2019-10-11 1200 02:01
13 2019-10-18 1100 03:01
14 2019-09-08 200 11:01
15 2019-08-01 500 01:31
16 2019-10-07 300 02:31
17 2019-10-02 1200 03:31
Now, I want to show a list of doctors with their total appointment durations in decreasing order using SQL query.
Unfortunately, I don't have any idea about this SQL query. Can you assist me?
SELECT DOCTOR_NAME, SUM(DURATION) FROM APPOINTMENTS A
JOIN DOCTORS D ON D.ID = A.DOCTOR_ID
JOIN SLOTS S ON S.ID = A.SLOT_ID
GROUP BY D.ID, DOCTOR_NAME
ORDER BY SUM(DURATION) DESC;
select d.doctor_id, d.doctor_name, sum(apt.duration) as total_duration from
doctors as d
join appointments as apt on apt.doctor_id = d.doctor_id
join slots as s on s.id = apt.slot_id
group by d.doctor_id, d.doctor_name
The above query should work fine.
There might be some typo as I didn't write it in the SQL management studio.

Processing values in a column for each group

I have a MySQL table of customers and the shop branches they have purchased from, similar to the following:
customer_id | branch_id | is_major_branch
-----------------------------------------------
5 24 1
5 83 0
5 241 0
8 66 0
8 72 0
9 15 1
16 31 1
16 61 1
is_major_branch is 1 if that branch is a particularly large store.
How can I delete all rows where a customer has shopped in a minor branch (is_major_branch = 0), except if a customer has only ever shopped in a minor branch? Example result set:
customer_id | branch_id | is_major_branch
-----------------------------------------------
5 241 1
8 66 0
8 72 0
9 15 1
16 31 1
16 61 1
Notice how customer 8 has only ever shopped in a minor branches, so we ignore them from the deletion.
You can delete the rows doing:
delete t
from t join
(select customer_id, max(is_major_branch) as max_is_major_branch
from t
group by customer_id
) tt
on t.customer_id = tt.customer_id
where t.is_major_branch = 0 and tt.max_is_major_branch = 1;
If you just want a select query, then use exists:
select t.*
from t
where not (t.is_major_branch = 0 and
exists (select 1 from t t2 where t2.customer_id = t.customer_id and t2.is_major_branch = 1)
);

MySQL Group By not producing expected result

This is my table structure:
rec_id product_id quantity quantity_in quantity_out balance stock_date status
1 2 342 NULL 17 325 2009-10-23 1
2 2 325 NULL 124 201 2009-10-23 1
3 1 156 NULL 45 111 2009-10-23 1
4 2 201 NULL 200 1 2009-10-23 1
5 2 1 NULL 1 0 2009-10-23 1
6 1 111 NULL 35 76 2009-10-23 1
All I want is the last transaction done for a given product: product_id, quantity, quantity_out and balance from this table.
Example, there are 2 transaction done for product 2 (ids 1 & 2):
final balance for product_id 2 is 0 -> stored in rec_id 5
final balance for product_id 1 is 76 -> stored in rec_id 6
Final result/output should be like this:
recid productid quantity quantityin quantityout balance stock_date status
5 2 1 NULL 1 0 2009-10-23 1
6 1 111 NULL 35 76 2009-10-23 1
You can find the latest record for each product like:
select max(rec_id) as MaxRec
from YourTable
group by product_id
Using a subquery, you can retrieve the latest rows for their product:
select *
from YourTable
where rec_id in (
select max(rec_id) as MaxRec
from YourTable
group by product_id
)
Here's a single query with no subqueries:
SELECT main.*
FROM YourTable main
LEFT JOIN YourTable newer
ON newer.product_id = main.product_id AND newer.rec_id > main.rec_id
WHERE newer.rec_id IS NULL;
You can tweak the field list however you want--make sure you select fields from main, not newer, which should be all null.