Using count aggregate based on the current row on SQL Server - sql

I am using SQL server and trying to get the the course code , title and the number of students who may take that course ( current course) , Knowing that for a student to take the course, he should pay at least half the course price , below are the tables :
I tried the folowing code , and faced the issue of "Column 'courseStudent.paid' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.
"
here is the sql code I used :
select courses.code, courses.title , courses.courseId ,
(select count(*) from courseStudent inner join courses on courseStudent.courseId = courses.courseId
group by courseStudent.courseId having courseStudent.paid>courses.price/2)
from courses ;

This should work.
SELECT c.courseId,c.code,c.title, COUNT(1) AS NoOfStudentEnrolled FROM courseStudent cs
JOIN courses c ON cs.courseId = c.courseId
WHERE cs.paid>c.price/2
GROUP BY c.courseId,c.code,c.title

Related

Micorosft Access SQL - Counting Number of foreign key records across 3 related tables

I am experienced at VBA but new to SQL.
I am developing a test sheet program in MS Access for the plant that I work at. This test sheet program will be used across 3 product lines.
When an order is created, it can contain up to all 3 products. The products are unique enough that I cannot put them all into their own table. So I have broken the test sheets up into 3 tables, each table representing its respective product test sheet. Please see the image below for my relationship setup.
What I am trying to do:
I am trying to design a query that will be my master list (outputting to a continuous form). The master list will show all orders, and also show how many units have been tested in each order. See below for my desired output.
My Issue:
It is not properly counting the number of related records. See the linked photo.
I know my key field is Order Number but I am searching by Job name. Originally my key field was job name but then switched it to order number.
thank you for your time, I am happy to provide more information if needed.
Consider joining three aggregate saved queries:
SELECT OrderNumber
, COUNT(*) AS CountofCassetteTests
FROM TblCassetteTestSheet
GROUP BY OrderNumber
SELECT OrderNumber
, COUNT(*) AS CountofSentinelTests
FROM TblSentinelTestSheet
GROUP BY OrderNumber
SELECT OrderNumber
, COUNT(*) AS CountofHUVTests
FROM TblHUVTestSheet
GROUP BY OrderNumber
Then join to TblJobName in your final query (parentheses are required):
SELECT j.OrderNumber, j.JobName
, c.CountofCassetteTests AS [# Of Cassettes Tested]
, s.CountofSentinelTests AS [# Of Sentinels Tested]
, h.CountofHUVTests AS [# Of HUV Tested]
, j.JobEndDate, j.SPONumber
FROM ((TblJobName j
LEFT JOIN QryCassetteTestSheet c
ON j.OrderNumber = c.OrderNumber)
LEFT JOIN QrySentinelTestSheet s
ON j.OrderNumber = s.OrderNumber)
LEFT JOIN QryHUVTestSheet h
ON j.OrderNumber = h.OrderNumber
Conceivably you can run all in one query using subqueries (and maybe one day even Common Table Expressions, CTEs, if the MS Access team ever enhances its older SQL dialect):
SELECT j.OrderNumber, j.JobName
, c.CountofCassetteTests AS [# Of Cassettes Tested]
, s.CountofSentinelTests AS [# Of Sentinels Tested]
, h.CountofHUVTests AS [# Of HUV Tested]
, j.JobEndDate, j.SPONumber
FROM ((TblJobName j
LEFT JOIN
(SELECT OrderNumber, COUNT(*) AS CountofCassetteTests
FROM TblCassetteTestSheet
GROUP BY OrderNumber) c
ON j.OrderNumber = c.OrderNumber)
LEFT JOIN
(SELECT OrderNumber, COUNT(*) AS CountofSentinelTests
FROM TblSentinelTestSheet
GROUP BY OrderNumber) s
ON j.OrderNumber = s.OrderNumber)
LEFT JOIN
(SELECT OrderNumber, COUNT(*) AS CountofHUVTests
FROM TblHUVTestSheet
GROUP BY OrderNumber) h
ON j.OrderNumber = h.OrderNumber
I have created 3 queries. Each query counts the number of tests for that product that are associated with an order number. Below you will see my code for
QryCountofTestedHUV
SELECT TblJobName.OrderNumber, TblJobName.JobName
(
SELECT
Count(*)
FROM
TblHUVTestSheet
Where TblHUVTestSheet.OrderNumber=TblJobName.OrderNumber
) AS CountofHUVTests
FROM TblJobName LEFT JOIN TblHUVTestSheet ON TblJobName.OrderNumber = TblHUVTestSheet.OrderNumber
GROUP BY TblJobName.OrderNumber, TblJobName.JobName;
From there I have gone into the relationships view, and linked QryCountofTestedHUV.OrderNumber to TblJobName.OrderNumber. Now I can add CountofHUVTests from query QryCountofTestedHUV as a field to my query, QryAllJobs! QryAllJobs is my master list I showed earlier.
I have repeated this 3 times for all 3 product lines and it works!

SQL Group by not returning accurate answer

it is current result
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(sch.sms_name) as schoolname,
count(stu.accountnumber)NoofStudent
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid=sch.sms_schoolcategoryid
inner join FilteredAccount stu
on sch.sms_schoolid=stu.sms_schoolid
group by cat.sms_schoolcategoryid,
cat.sms_name
;
I have three tables one is Category and 2nd is Schools and 3rd is students. i just want to count the schools on behalf of category when I join tables category and school it returns me accurate result and when i join students table with schools table it returns me wrong result. Please Guide me how it is possible.
There is guesswork involved unless you provide more information on you data model. However, it appears that your issue is rooted in the following:
When you join the student table, for each combination of school category and school (*) you generate additional records. I.e., your sql no longer counts schools by school category but students.
For a concrete solution and a good advice see #Nick.McDermaid's comment.
Try this one
select count(stu.accountnumber) as NoofStudent
, catsch.categoryId
, catsch.category
, catsch.schoolname
from FilteredAccount stu
inner join (
select cat.sms_schoolcategoryid as categoryId
, cat.sms_name as category
, count(sch.sms_name) as schoolname
, sch.sms_schoolid
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid
group by cat.sms_schoolcategoryid, cat.sms_name, sch.sms_schoolid) catsch
on catsch.sms_schoolid = stu.sms_schoolid
group catsch.categoryId
, catsch.category
, catsch.schoolname
count() returns the number of non-NULL values. So, your two count() will return the same values. You can quickly fix the query using count(distinct):
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(distinct sch.sms_name) as schoolname,
count(distinct stu.accountnumber) as NoofStudent
from Filteredsms_schoolcategory cat inner join
Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid inner join
FilteredAccount stu
on sch.sms_schoolid = stu.sms_schoolid
group by cat.sms_schoolcategoryid, cat.sms_name ;
Actually, you probably don't need the second count distinct. Just count(stu.accountnumber) should count the students.

How to use an inner join with group by or count function

I'm use this SQL query to fetch data from a access database
SELECT Absence.date_, stages.name, course.course_name, student.name AS st_name
FROM (((Absence INNER JOIN course ON Absence.course_id = course.ID)
INNER JOIN stages ON course.id_stage = stages.ID)
INNER JOIN student ON Absence.student_id = student.ID AND stages.ID = student.stage_id`
when i wont to group the field or get count of field it show to me this error message
Tried to execute a query that does not include the specified
expression 'Absence.date_' as part of an aggregate function
how can i get number of row ,distinct it by st_name , and group by stage name or course name
For getting the count you should use
SELECT Absence.date_, stages.name, course.course_name, student.name AS st_name, count(*)
FROM (((Absence INNER JOIN course ON Absence.course_id = course.ID)
INNER JOIN stages ON course.id_stage = stages.ID)
INNER JOIN student ON Absence.student_id = student.ID AND stages.ID = student.stage_id
GROUP BY Absence.date_, stages.name, course.course_name, student.name
What table contains the field "Absence.date_" ?
And you do not need to specify "Inner" (or Natural for that matter).
When using aggregate functions (Count(), Sum(), ect) your group by must contain the same list of fields as your select statement. And that error states that either Absence.date_ does not exist as a field in any of the tables in the from clause. But, you have no Group By statement in your query.

SQL: Distinct and OrderBy issue

I'm currently working on a query that should return all the rows from CONCENTRATOR table. However, it has to be sortable by all concentrator's columns, as well as department name and type name.
Here are the concentrator's columns :
CONCENTRATOR_ID
NAME
INTERNALADDRESS
TYPE_ID
DEPARTMENT_ID
TYPE_ID and DEPARTMENT_ID are linked to respectively DEPARTMENT table and TYPE table, with both a NAME column.
Here are the constraints :
concentrators are sortable by id, name, address, type's name and department's name
distinct department names (if a same concentrator has 2 department, return only one row)
To resume, I would need something like SELECT * on concentrator columns, and DISTINCT department.name as well but seems complicated... I tried lots of requests but couldn't find any one working.
Could anybody help me please ?
The request I'm looking for should be something like this:
SELECT DISTINCT d.NAME as "department.name", t.NAME as "type.name", *
FROM "CONCENTRATOR" c
LEFT OUTER JOIN "CONCENTRATOR_GROUP" USING(CONCENTRATOR_ID)
LEFT OUTER JOIN "GROUP" g USING(GROUP_ID)
LEFT OUTER JOIN "TYPE" t USING(TYPE_ID)
LEFT OUTER JOIN "DEPARTMENT" d USING(DEPARTMENT_ID)
ORDER BY TRIM(UPPER(c.name)) ASC
There are a few things to note here. I'm really not fond of "natural joins" as they simply disguise useful detail in my view, so I have not used them. I had to assume that the table "GROUP" joins via CONCENTRATOR_GROUP for an example of that missing detail.
The table name "GROUP" isn't a great idea as it is a very commonly used reserved word. I'd not recommend using such a word as a table name. Due to this "GROUP" is quoted (it isn't normal to quote object names in Oracle my experience).
You talk about "distinct" as if it has some magical quality that I should intuitively understand. It doesn't, and I don't. Let's say there are just 2 departments both are also "distinct"
DeptX
DeptY
So now let's assume there are 2 concentrators, both of these are "distinct" too:
ConcenA
ConcenB
Both concentrators are used in both departments, so we produce this query:
select distinct
c.name as c_name, d.name as d_name
from concentrators c
inner join departments d on c.dept_id=d.dept_id
The result is:
ConcenA DeptX
ConcenB DeptX
ConcenA DeptY
ConcenB DeptY
All 4 rows are "distinct"
The point is that "select distinct" is a "row operator", i.e. it considers the entire row to determine if any part of the row is different to all other rows. There are no subtleties or options to "select distinct", it always works the same way (over the entire row). So, with this in mind, we now know that "select distinct" simply is not going to be the right technique (and due to the technical definition of distinct you might also sense it isn't a good way to describe your problem either).
So, as "select distinct" isn't the right technique typically one can turn to these as techniques: "group by" or "row_number()"
because these do give us subtleties and options.
Now you haven't explained why or how you would choose just one department (in fact, to me, it sounds weird you would choose just one) but below I offer you A way to do this using row_number() and the "subtlety" being used is the ORDER BY which gives the number 1 to the first Department Name in alphabetic order, all other departments get more than 1; and this occurs for each CONCENTRATOR_ID because row_number() is "partitioned by" that field.
SELECT
department_name
, type_name
, NAME
, CONCENTRATOR_ID
, INTERNALADDRESS
, TYPE_ID
, DEPARTMENT_ID
FROM (
SELECT
d.NAME AS department_name
, t.NAME AS type_name
, c.CONCENTRATOR_ID
, c.NAME
, c.INTERNALADDRESS
, c.TYPE_ID
, c.DEPARTMENT_ID
, ROW_NUMBER() OVER (PARTITION BY c.CONCENTRATOR_ID
ORDER BY d.NAME, t.NAME, c.NAME) AS RN
FROM CONCENTRATOR c
LEFT OUTER JOIN CONCENTRATOR_GROUP cg
ON c.CONCENTRATOR_ID = cg.CONCENTRATOR_ID
LEFT OUTER JOIN "GROUP" g
ON cg.GROUP_ID = g.GROUP_ID
LEFT OUTER JOIN TYPE t
ON c.TYPE_ID = t.TYPE_ID
LEFT OUTER JOIN DEPARTMENT d
ON c.DEPARTMENT_ID = c.DEPARTMENT_ID
) sq
WHERE RN = 1 /* HERE is where we restrict output to one department per concentrator */
ORDER BY
NAME ASC
, CONCENTRATOR_ID
;
I have no reason to change the type of joins as you can see they remain as left outer joins - but I suspect there may be no valid reason for all or some of these. Do use the more efficient INNER JOIN if you can.

SQL Query for finding values that do not exist in one table, with WHERE clause

I'm struggling to compile a query for the following and wonder if anyone can please help (I'm a SQL newbie).
I have two tables:
(1) student_details, which contains the columns: student_id (PK), firstname, surname (and others, but not relevant to this query)
(2) membership_fee_payments, which contains details of monthly membership payments for each student and contains the columns: membership_fee_payments_id (PK), student_id (FK), payment_month, payment_year, amount_paid
I need to create the following query:
which students have not paid fees for March 2012?
The query could be for any month/year, March is just an example. I want to return in the query firstname, surname from student_details.
I can query successfully who has paid for a certain month and year, but I can't work out how to query who has not paid!
Here is my query for finding out who has paid:
SELECT student_details.firstname, student_details.surname,
FROM student_details
INNER JOIN membership_fee_payments
ON student_details.student_id = membership_fee_payments.student_id
WHERE membership_fee_payments.payment_month = "March"
AND membership_fee_payments.payment_year = "2012"
ORDER BY student_details.firstname
I have tried a left join and left outer join but get the same result. I think perhaps I need to use NOT EXISTS or IS NULL but I haven't had much luck writing the right query yet.
Any help much appreciated.
I'm partial to using WHERE NOT EXISTS Typically that would look something like this
SELECT D.firstname, D.surname
FROM student_details D
WHERE NOT EXISTS (SELECT * FROM membership_fee_payments P
WHERE P.student_id = D.student_id
AND P.payment_year = '2012'
AND P.payment_month = 'March'
)
This is know an a correlated subquery as it contains references to the outer query. This allows you to include your join criteria in the subquery without necessarily writing a JOIN. Also, most RDBMS query optimizers will implement this as a SEMI JOIN which does not typically do as much 'work' as a complete join.
You could use a left join. When the payment is missing, all the columns in the left join table will be null:
SELECT student_details.firstname, student_details.surname,
FROM student_details
LEFT JOIN membership_fee_payments
ON student_details.student_id = membership_fee_payments.student_id
AND membership_fee_payments.payment_month = "March"
AND membership_fee_payments.payment_year = "2012"
WHERE membership_fee_payments.student_id is null
ORDER BY student_details.firstname
You can also write following query. This will gives your expected output.
SELECT student_details.firstname,
student_details.surname,
FROM student_details
Where
student_details.student_id Not in
(SELECT membership_fee_payments.student_id
from membership_fee_payments
WHERE
membership_fee_payments.payment_year = '2012'
AND membership_fee_payments.payment_month = 'March'
)