COUNT in a subquery, then SUM - mysql

This should be relatively simple, but I haven't been able to figure it out.
I have a query that currently creates a table that looks like:
Teacher, course, # students in course
John Doe, Algebra 1, 3
John Doe, Algebra 2, 1
Jeff Doh, Geometry, 2
I want to also count the number of students being taught by each teacher, giving results like:
Teacher, course, # students in course, # students with teacher
John Doe, Algebra 1, 3, 4
John Doe, Algebra 2, 1, 4
Jeff Doh, Geometry, 2, 2
But I can't figure out how to produce the last column that sums all of the students being taught by a teacher across all courses.
Here's my current query (I'd also be interested in a better way to write this existing query)
SELECT
u.username AS 'teacher',
c.fullname AS 'course',
(SELECT COUNT(u1.username)
FROM prefix_user u1
JOIN prefix_user_enrolments ue1 on ue1.userid=u1.id
JOIN prefix_enrol e1 ON e1.id=ue1.enrolid
JOIN prefix_course c1 on c1.id = e1.courseid
JOIN prefix_context AS ctx1 ON ctx1.instanceid = c1.id
JOIN prefix_role_assignments AS ra1 ON ra1.contextid = ctx1.id
JOIN prefix_course_categories AS cc1 ON cc1.id=c1.category
WHERE ra1.roleid="5" ### "5" = student
AND ra1.userid=u1.id
AND e1.courseid=c1.id
AND c1.id=c.id) AS '# students in course'
FROM prefix_user u
JOIN prefix_user_enrolments ue on ue.userid=u.id
JOIN prefix_enrol e ON e.id=ue.enrolid
JOIN prefix_course c on c.id = e.courseid
JOIN prefix_context AS ctx ON ctx.instanceid = c.id
JOIN prefix_role_assignments AS ra ON ra.contextid = ctx.id
JOIN prefix_course_categories AS cc ON cc.id=c.category
WHERE ra.roleid="3" ### "3" = Teacher
GROUP BY c.id
ORDER BY cc.name, c.fullname
I wish I could just add a SUM(# students in course) column, but that doesn't work. And the interface I'm using won't let me use WITH ROLLUP.
My Schema:
CREATE TABLE prefix_user
(`id` varchar(2), `username` varchar(11))
;
INSERT INTO prefix_user
(`id`, `username`)
VALUES
('1', 'JohnDoe'),
('2', 'JaneDuh'),
('3', 'JeffDoh'),
('4', 'JackSprat'),
('5', 'WillieWinky'),
('6', 'DonaldDuck'),
('7', 'MickeyMouse')
;
CREATE TABLE prefix_user_enrolments
(`id` varchar(2), `enrolid` varchar (4), `userid` varchar(1), `status` varchar(1))
;
INSERT INTO prefix_user_enrolments
(`id`, `enrolid`, `userid`, `status`)
VALUES
('10', '1000', '1', '0'),
('11', '1001', '2', '0'),
('12', '2000', '3', '0'),
('13', '1002', '4', '0'),
('14', '2001', '5', '0'),
('15', '1003', '6', '0'),
('16', '2002', '7', '0'),
('17', '3000', '1', '0'),
('18', '3001', '7', '0')
;
CREATE TABLE prefix_enrol
(`id` varchar(4), `status` varchar (1), `courseid` varchar(3), `roleid` varchar(1))
;
INSERT INTO prefix_enrol
(`id`, `status`, `courseid`, `roleid`)
VALUES
('1000', '0', '100', '5'),
('1001', '0', '100', '5'),
('2000', '0', '200', '5'),
('1002', '0', '100', '5'),
('2001', '0', '200', '5'),
('1003', '0', '100', '3'),
('2002', '0', '200', '3'),
('3000', '0', '300', '3'),
('3001', '0', '300', '5')
;
CREATE TABLE prefix_course
(`id` varchar(3), `fullname` varchar(8), `category` varchar(2))
;
INSERT INTO prefix_course
(`id`, `fullname`, `category`)
VALUES
('100', 'Algebra1', '10'),
('200', 'Geometry', '10'),
('300', 'Algebra2', '10')
;
CREATE TABLE prefix_context
(`id` varchar(5), `instanceid` varchar(8))
;
INSERT INTO prefix_context
(`id`, `instanceid`)
VALUES
('10000', '100'),
('10001', '100'),
('20000', '200'),
('10002', '100'),
('20001', '200'),
('10003', '100'),
('20002', '200'),
('30000', '300'),
('30001', '300')
;
CREATE TABLE prefix_role_assignments
(`id` varchar(6), `roleid` varchar(1), `contextid` varchar(5), `userid` varchar(1))
;
INSERT INTO prefix_role_assignments
(`id`, `roleid`, `contextid`, `userid`)
VALUES
('100000', '5', '10000', '1'),
('100001', '5', '10001', '2'),
('200000', '5', '20000', '3'),
('100002', '5', '10002', '4'),
('200001', '5', '20001', '5'),
('100003', '3', '10003', '6'),
('200002', '3', '20002', '7'),
('300000', '3', '30000', '1'),
('300001', '5', '30001', '7')
;
CREATE TABLE prefix_role
(`id` varchar(1), `shortname` varchar(7))
;
INSERT INTO prefix_role
(`id`, `shortname`)
VALUES
('5', 'student'),
('3', 'teacher')
;
CREATE TABLE prefix_course_categories
(`id` varchar(2), `name` varchar(4))
;
INSERT INTO prefix_course_categories
(`id`, `name`)
VALUES
('10', 'math')
;

It looks like your original query may be in error. I believe JohnDoe is enrolled in Algebra 1 as a student not a teacher. I also noticed that the role is held on two tables: prefix_enrol and prefix_role_assignments - not sure if I am misinterpreting something or this is redundant data.
In my attempt below I used sub-queries to count the enrollments by courseID where the role of the enrolled is a student and count the students per teacher. There may be a more efficient way to do this, but this is what I cam up with on my first attempt:
SELECT
t.username AS 'teacher'
,c.fullname AS 'course'
,studentCountCourse.numStudents AS '# students in course'
,studentCountTeach.numStudents AS '# students with teacher'
FROM prefix_enrol AS e
INNER JOIN prefix_course AS c ON c.id = e.courseid
INNER JOIN prefix_user_enrolments AS ue ON ue.enrolid = e.id
INNER JOIN prefix_user AS t ON t.id = ue.userID AND e.roleid = 3
INNER JOIN (
SELECT
e1.courseid
,count(e1.courseid) AS numStudents
FROM prefix_enrol AS e1
WHERE e1.roleid = 5
GROUP BY e1.courseid) AS studentCountCourse ON studentCountCourse.courseid = c.id
INNER JOIN (
SELECT
t1.id
,count(t1.id) AS numStudents
FROM prefix_user AS t1
INNER JOIN prefix_user_enrolments AS ue ON ue.userid = t1.id
INNER JOIN prefix_enrol AS e ON e.id = ue.enrolid
INNER JOIN prefix_enrol AS e2 ON e2.courseid = e.courseid AND e2.roleid = 5
INNER JOIN prefix_course AS c ON c.id = e.courseid
WHERE e.roleid = 3
GROUP BY t1.id) AS studentCountTeach ON studentCountTeach.id = t.id
ORDER BY e.courseid;
If I am misunderstanding your schema please let me know by explaining what each of the tables is used for.

Related

sql query with join and FIND IN SET

I want to get count of post subscribed by user below is my table schema , please help me with query for the same, i trie many option but could not do it
I tried and was able to get post count of user below is my query , but here i have used static user id , i want single query to list count for all users
SELECT COUNT(*)
FROM CATMAPPING INNER JOIN
POST ON CATMAPPING.pid = POST.id
where FIND_IN_SET(CATMAPPING.cid,(select selectedcatid from subscribers where id='1'));
Desire OP
Desired Output
uemail Postcount
-----------------------------
a#s.com 4
b#s.com 8
c#s.com 10
d#s.com 4
SQL fiddel link : http://sqlfiddle.com/#!9/4fff8f/2
CREATE TABLE subscribers (
`id` int(10),
`uemail` varchar(255) DEFAULT NULL,
`selectedcatid` varchar(255) DEFAULT NULL
) ;
ALTER TABLE subscribers ADD PRIMARY KEY (`id`);
ALTER TABLE subscribers MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
INSERT INTO subscribers (`uemail`, `selectedcatid`) VALUES ('a#s.com', '1'),
('b#s.com', '1,3'),
('c#s.com', '1,2,3'),
('d#s.com', '3');
CREATE TABLE POST (
`id` int(10),
`title` varchar(255) DEFAULT NULL
) ;
INSERT INTO POST (`id`, `title`) VALUES ('1', 'ABC'),
('2', 'DEF'),
('3', 'GHI'),
('4', 'JKL'),
('5', 'MNO'),
('6', 'PQR'),
('7', 'STU'),
('8', 'VXZ'),
('9', 'ASO'),
('10', 'LMO');
CREATE TABLE CATMAPPING (
`cid` int(10),
`pid` int(10) DEFAULT NULL
) ;
INSERT INTO CATMAPPING (`pid`, `cid`) VALUES ('1', '1'),
('2', '2'),
('3', '3'),
('4', '1'),
('5', '2'),
('6', '3'),
('7', '3'),
('8', '3'),
('9', '1'),
('10', '1');
Here is the correct query I wrote in sql server may be some syntax is diffrent but it gives me correct result I creates a table valued function and then used it for the query .
declare #tempsub as table (subid int,selectcatId int )
insert into #tempsub
select id ,string
from subscribers
CROSS APPLY [dbo].[ufn_CSVToTable] (selectedcatid)
--select * from #tempsub
-- subid is the id of the subscribes table
SELECT subid , count(*) from post p inner join CATMAPPING c on c.pid = p.id
left join #tempsub t on t.selectcatId= c.cid
group by t.subid
-- below is the code for tabled valued function it return a table for comma seprated string
create FUNCTION dbo.[ufn_CSVToTable] ( #StringInput VARCHAR(8000) )
RETURNS #OutputTable TABLE ( [String] nVARCHAR(1000) )
AS
BEGIN
DECLARE #String nVARCHAR(1000)
WHILE LEN(#StringInput) > 0
BEGIN
SET #String = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(',', #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(',', #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [String] )
VALUES ( #String )
END
RETURN
END

mysql cant' get null data to show up in query

Here is what I need to achieve:
person_id last_name first_name region_id region name
1 barnum phineas 1 maricopa
2 loman willy 2 pima
2 loman willy 3 pinal
2 loman willy 4 santa cruz
3 kay mary 5 cochise
3 kay mary 6 gila
3 kay mary 7 graham
4 lillian vernon NULL NULL
Here are my tables:
Create database sales
use sales
create table if not exists
`Sales_People`
(`person_id` int primary key,
`last_name` char(16) not null,
`first_name` char(16) not null);
INSERT INTO Sales_people
(`person_id`, `last_name`, `first_name`)
values
('1', 'barnum', 'phineas'),
('2', 'loman', 'willy'),
('3', 'kay', 'mary'),
('4', 'lillian', 'vernon');
create table if not exists
`Sales_Region`
(`region_id` int primary key,
`name` char(16) not null);
INSERT INTO Sales_Region
(`region_id`, `name`)
Values
('1', 'maricopa'),
('2', 'pima'),
('3', 'pinal'),
('4', 'santa cruz'),
('5', 'cochise'),
('6', 'gila'),
('7', 'graham');
create table if not exists
`Sales_People_Region`
(`person_id` int not null,
`region_id` int not null,
constraint spr_pk primary key(person_id, region_id),
constraint spr_fk1 foreign key(person_id)
references Sales_People(person_id),
constraint spr_fk2 foreign key(region_id)
references Sales_Region(region_id));
INSERT INTO Sales_People_Region
(`person_id`, `region_id`)
Values
('1', '1'),
('2', '2'),
('2','3'),
('2', '4'),
('3', '5'),
('3', '6'),
('3','7');
create table if not exists
`Sales`
(`year` int not null,
`month` int not null,
`region_id` int not null,
`amount_sold` decimal(11,2),
constraint s_pk primary key(year, month, region_id),
constraint s_fk foreign key(region_id)
references Sales_Region(region_id));
INSERT INTO Sales
(`year`, `month`, `region_id`, `amount_sold`)
Values
('2016', '01', '1', '800000'),
('2016', '02', '1', '850000'),
('2016', '03', '1', '990000'),
('2016', '01', '2', '425000'),
('2016', '02', '2', '440000'),
('2016', '03', '2', '450000'),
('2016', '01', '3', '200000'),
('2016', '02', '3', '210000'),
('2016', '03', '3', '220000'),
('2016', '01', '4', '50000'),
('2016','02', '4', '52000'),
('2016', '03', '4', '55000'),
('2016', '01', '5', '40000'),
('2016', '02', '5', '41000'),
('2016', '03', '5', '42000'),
('2016', '01', '6', '3000'),
('2016', '02', '6', '31000'),
('2016','03', '6', '32000'),
('2016', '01', '7', '20000'),
('2016', '02', '7', '21000'),
('2016', '03', '7', '22000');
here is my code:
select sales_people.person_id, `last_name`, `first_name`, sales_region.Region_id,
trim(sales_region.`name`) AS 'Region Name'
from `sales_region`
inner join sales_people_region
on sales_people_region.region_id = sales_region.region_id
inner join sales_people
on sales_people_region.`person_id` = sales_people.`person_id`
group by sales_region.region_id, sales_people.person_id
having sales_people.person_id >= ''
order by sales_people.person_id, sales_region.region_id asc;
what I get:
person_id last_name first_name Region_id "Region Name"
1 barnum phineas 1 maricopa
2 loman willy 2 pima
2 loman willy 3 pinal
2 loman willy 4 "santa cruz"
3 kay mary 5 cochise
3 kay mary 6 gila
3 kay mary 7 graham
I can't get it to let me see the last person_id because they have no region_ids assigned to them. In the table sales_people_region region_id is a primary key and therefore not able to be null.
If I query sales_people and group by person_id she does come up.
I think an outer join would remedy this. An inner join requires there to be at least one row extant on each side of the join; a left outer join will select all rows from the left-hand table including those for which there is no matching row in the right-hand table. (A right outer join is the mirror image of this, and a full outer join selects unmatched rows from both sides.)
Try this query:
SELECT sales_people.person_id, last_name, first_name, sales_region.Region_id,
TRIM(sales_region.`name`) AS 'Region Name'
FROM sales_people
LEFT OUTER JOIN sales_people_region
on sales_people.person_id = sales_people_region.person_id
LEFT OUTER JOIN sales_region
ON sales_people_region.region_id = sales_region.region_id
GROUP BY sales_region.region_id, sales_people.person_id
HAVING sales_people.person_id != ''
ORDER BY sales_people.person_id, sales_region.region_id ASC;

rank users and join tables

How do I rank users based on points and join that user_sno with refno of another table?
I am not getting right ranking with the code below :
select *, (#rank := #rank + 1) as rank
from tblB uv
join tblC c on uv.sno=c.refno
join
(select #rank := 0) const
where uv.sno in (2, 4,5)
order by rank;
I really appreciate any help.Thanks in Advance.
http://sqlfiddle.com/#!2/9be59/12
CREATE TABLE if not exists tblB
(
id int(11) NOT NULL auto_increment ,
sno varchar(255),
name varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblC
(
id int(11) NOT NULL auto_increment ,
data varchar(255),
refno varchar(255),
points int(255),
PRIMARY KEY (id)
);
INSERT INTO tblB (sno, name ) VALUES
('1', 'Aa'),
('2', 'Bb'),
('3', 'Cc'),
('4', 'Dd'),
('5', 'Ee'),
('6', 'Ff'),
('7', 'Gg'),
('8', 'Hh');
INSERT INTO tblC (data,refno,points ) VALUES
('data1', '1', '101'),
('data2', '2', '102'),
('data3', '3', '103'),
('data4', '4', '101'),
('data5', '5', '102'),
('data6', '6', '103'),
('data7', '7', '101'),
('data8', '8', '101'),
('data9', '9', '101');
You ORDER BY rank, but you want to ORDER BY points, seems like a typo.

get latest entry even if 2 records have same timestamp

I am using the code below to retrieve the latest data w.r.t all users .But if the user had points added at the same time stamp then I would like to get the last entry not both like in the example below.How do I make sure that I get latest entry even if 2 records have same timestamp.
http://sqlfiddle.com/#!2/374db/1
I really appreciate any help.Thanks in Advance.
CREATE TABLE if not exists tblA
(
id int(11) NOT NULL auto_increment ,
sender varchar(255),
receiver varchar(255),
msg varchar(255),
date timestamp,
points varchar(255),
refno varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblB
(
id int(11) NOT NULL auto_increment ,
sno varchar(255),
name varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblC
(
id int(11) NOT NULL auto_increment ,
data varchar(255),
refno varchar(255),
extrarefno varchar(255),
PRIMARY KEY (id)
);
INSERT INTO tblA (sender, receiver,msg,date,points,refno ) VALUES
('1', '2', 'buzz ...','2011-08-21 14:11:09','10','001'),
('1', '2', 'test ...','2011-08-21 14:12:19','20','002'),
('4', '2', 'test ...','2011-08-21 14:13:19','30','003'),
('1', '3', 'buzz ...','2011-08-21 14:11:09','10','004'),
('1', '3', 'test ...','2011-08-21 14:12:19','20','005'),
('1', '4', 'buzz ...','2011-08-21 14:11:09','10','006'),
('1', '4', 'test ...','2011-08-21 14:12:19','20','007'),
('3', '4', 'test ...','2011-08-21 14:13:19','20','008'),
('2', '4', 'test ...','2011-08-21 14:13:19','20','009');
INSERT INTO tblB (sno, name ) VALUES
('1', 'Aa'),
('2', 'Bb'),
('3', 'Cc'),
('4', 'Dd'),
('5', 'Ee'),
('6', 'Ff'),
('7', 'Gg'),
('8', 'Hh');
INSERT INTO tblC (data,refno,extrarefno ) VALUES
('data1', '001', '101'),
('data2', '002', '102'),
('data3', '003', '103'),
('data4', '004', '101'),
('data5', '005', '102'),
('data6', '006', '103'),
('data7', '007', '101'),
('data8', '008', '101'),
('data9', '009', '101');
///
query:
SELECT *
FROM (
SELECT tblB.*, MAX(tblA.date) AS date
FROM tblB
JOIN tblA ON tblB.sno = tblA.receiver
GROUP BY tblB.sno
) AS subset
JOIN tblA ON subset.sno = tblA.receiver
AND subset.date = tblA.date JOIN tblC ON tblA.refno=tblC.refno
The key idea is to use the id column instead of the date column. It is auto-incremented, so the biggest id should be more recent.
However, your query has another problem which is the join to tblB in the subquery. Arbitrary ("indeterminate") values from tblB would be returned in the outer query. Instead, just aggregate on tblA and move the join to tblB to the outer level:
SELECT *
FROM (SELECT tblA.receiver, MAX(tblA.id) AS id
FROM tblA
GROUP BY tblA.receiver
) subset JOIN
tblA
ON subset.receiver = tblA.receiver AND subset.id = tblA.id JOIN
tblB
on tblA.receiver = tblB.sno join
tblC
ON tblA.refno=tblC.refno ;
Order by the date AND the id. The id is set to auto increment, if the date is the same, you can assume the higher id was created after.
ORDER BY date, id

rank users based on points

How can I rank the users below based on points .I really appreciate any help.
Thanks in Advance .
http://sqlfiddle.com/#!2/374db/11
CREATE TABLE if not exists tblA
(
id int(11) NOT NULL auto_increment ,
sender varchar(255),
receiver varchar(255),
msg varchar(255),
date timestamp,
points varchar(255),
refno varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblB
(
id int(11) NOT NULL auto_increment ,
sno varchar(255),
name varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblC
(
id int(11) NOT NULL auto_increment ,
data varchar(255),
refno varchar(255),
extrarefno varchar(255),
PRIMARY KEY (id)
);
INSERT INTO tblA (sender, receiver,msg,date,points,refno ) VALUES
('1', '2', 'buzz ...','2011-08-21 14:11:09','10','001'),
('1', '2', 'test ...','2011-08-21 14:12:19','20','002'),
('4', '2', 'test ...','2011-08-21 14:13:19','30','003'),
('1', '3', 'buzz ...','2011-08-21 14:11:09','10','004'),
('1', '3', 'test ...','2011-08-21 14:12:19','20','005'),
('1', '4', 'buzz ...','2011-08-21 14:11:09','10','006'),
('1', '4', 'test ...','2011-08-21 14:12:19','20','007'),
('3', '4', 'test ...','2011-08-21 14:13:19','20','008'),
('2', '4', 'test ...','2011-08-21 14:13:19','20','009');
INSERT INTO tblB (sno, name ) VALUES
('1', 'Aa'),
('2', 'Bb'),
('3', 'Cc'),
('4', 'Dd'),
('5', 'Ee'),
('6', 'Ff'),
('7', 'Gg'),
('8', 'Hh');
INSERT INTO tblC (data,refno,extrarefno ) VALUES
('data1', '001', '101'),
('data2', '002', '102'),
('data3', '003', '103'),
('data4', '004', '101'),
('data5', '005', '102'),
('data6', '006', '103'),
('data7', '007', '101'),
('data8', '008', '101'),
('data9', '009', '101');
sql
SELECT *
FROM (SELECT tblA.receiver, MAX(tblA.id) AS id
FROM tblA
GROUP BY tblA.receiver
) subset JOIN
tblA
ON subset.receiver = tblA.receiver AND subset.id = tblA.id JOIN
tblB
on tblA.receiver = tblB.sno join
tblC
ON tblA.refno=tblC.refno ;
You can order the results of a query using ORDER BY. So, you can rank the rows in tblA as follows:
SELECT *
FROM tblA
ORDER BY points
However, I notice you are using varchar for the points column. If this is to hold a numerical value you probably want int or float.