Getting object if count is less then a number - mysql

I have 2 simple tables - Firm and Groups. I also have a table FirmGroupsLink for making connections between them (connection is one to many).
Table Firm has attributes - FirmID, FirmName, City
Table Groups has attributes - GroupID, GroupName
Table FirmGroupsLink has attributes - FrmID, GrpID
Now I want to make a query, which will return all those firms, that have less groups then #num, so I write
SELECT FirmID, FirmName, City
FROM (Firm INNER JOIN FirmGroupsLink ON Firm.FirmID =
FirmGroupsLink.FrmID)
HAVING COUNT(FrmID)<#num
But it doesn't run, I try this in Microsoft Access, but it eventually should work for Sybase. Please show me, what I'm doing wrong.
Thank you in advance.

In order to count properly, you need to provide by which group you are couting.
The having clause, and moreover the count can't work if you are not grouping.
Here you are counting by Firm. In fact, because you need to retrieve information about the Firm, you are grouping by FirmId, FirmName and City, so the query should look like this:
SELECT Firm.FirmID, Firm.FirmName, Firm.City
FROM Firm
LEFT OUTER JOIN FirmGroupsLink
ON Firm.FirmID = FirmGroupsLink.FrmID
GROUP BY Firm.FirmID, Firm.FirmName, Firm.City
HAVING COUNT(FrmID) < #num
Note that I replace the INNER JOIN by a LEFT OUTER JOIN, because you might want Firm which doesn't belongs to any groups too.

Related

SQL Temporary Table or Select

I've got a problem with MySQL select statement.
I have a table with different Department and statuses, there are 4 statuses for every department, but for each month there are not always every single status but I would like to show it in the analytics graph that there is '0'.
I have a problem with select statement that it shows only existing statuses ( of course :D ).
Is it possible to create temporary table with all of the Departments , Statuses and amount of statuses as 0, then update it by values from other select?
Select statement and screen how it looks in perfect situation, and how it looks in bad situation :
SELECT utd.Departament,uts.statusDef as statusoforder,Count(uts.statusDef) as Ilosc_Statusow
FROM ur_tasks_details utd
INNER JOIN ur_tasks_status uts on utd.StatusOfOrder = uts.statusNR
WHERE month = 'Sierpien'
GROUP BY uts.statusDef,utd.Departament
Perfect scenario, now bad scenario :
I've tried with "union" statements but i don't know if there is a possibility to take only "the highest value" for every department.
example :
I've also heard about
With CTE tables, but I don't really get how to use it. Would love to get some tips on it!
Thanks for your help.
Use a cross join to generate the rows you want. Then use a left join and aggregation to bring in the data:
select d.Departament, uts.statusDef as statusoforder,
Count(uts.statusDef) as Ilosc_Statusow
from (select distinct utd.Departament
from ur_tasks_details utd
) d cross join
ur_tasks_status uts left join
ur_tasks_details utd
on utd.Departament = d.Departament and
utd.StatusOfOrder = uts.statusNR and
utd.month = 'Sierpien'
group by uts.statusDef, d.Departament;
The first subquery should be your source of all the departments.
I also suspect that month is in the details table, so that should be part of the on clause.

MySQL, two columns sharing the same join

SELECT
COALESCE (reports_straddons.employeeid, ldap_karen.uid) as uid,
COALESCE (reports_straddons.thomslocationcountrydescr, ldap_karen.thomslocationcountrydescr, 'NOVALUE') as thomslocationcountrydescr,
COALESCE (reports_straddons.thomslocationcity, ldap_karen.thomslocationcity, 'NOVALUE') as thomslocationcity,
COALESCE (reports_straddons.regionname, ldap_karen.regionname) as regionname,
CONCAT(reports_jobroles.groupabrev, COALESCE(CONCAT(' - ', reports_straddons.sup_role),'')) as sa_title,
ldap_karen.created
FROM
reports_straddons
LEFT JOIN ldap_karen ON reports_straddons.employeeid = ldap_karen.uid
LEFT JOIN reports_jobroles ON reports_straddons.roleid = reports_jobroles.nameid
Right now I am getting the user ID, country, city, region, but the group (compounded roles of roleid - sup_role) is a bit jacked up. The group is a compound name that is made up of:
roleid
sup_role
We have one job role table that we are linking to with names like Manager, Engineer, Sales, AM, PM, whatever.
So an example would be PM - Engineer. The roleid part I got working fine but not understanding what I have done wrong with the second part (and I have tried like 10 things which range from showing up anyone with a sup_role multiple times to only showing those with sup_roles - not everyone has both). So I have a join that is used for two distinct fields and both of those just contain the IDs to the joining table for the same column.
So right now I am getting Engineer - 9, 9 being the ID for the sup_role when it should read Developer if grabbing that name from the table based on the sup_role.
Note: I first logically used LEFT JOIN reports_jobroles ON reports_straddons.sup_role = reports_jobroles.nameid but alas this did not work at all.
I think I understand. The problem is you need to join to a table twice, but you're only joining it once. A simplified version of your problem would look like this:
SELECT
COALESCE (reports_straddons.employeeid, ldap_karen.uid) as uid,
CONCAT(reports_jobroles.groupabrev, COALESCE(CONCAT(' - ', reports_straddons.sup_role),'')) as sa_title
FROM
reports_straddons
LEFT JOIN reports_jobroles ON reports_straddons.roleid = reports_jobroles.nameid
To fix it, you need to do something like this:
SELECT
COALESCE (reports_straddons.employeeid, ldap_karen.uid) as uid,
CONCAT(roles.groupabrev, COALESCE(CONCAT(' - ', suproles.groupabrev),'')) as sa_title
FROM
reports_straddons
LEFT JOIN reports_jobroles roles ON reports_straddons.roleid = roles.nameid
LEFT JOIN reports_jobroles suproles ON reports_straddons.sup_role = suproles.nameid
Notice how I'm joining to reports_jobroles twice, and naming it something different. I'm under the assumption that sup_role will join to the nameid field much like roleid.

Using two SELECT statements in SQL?

I have two tables, one is 'points' which contains ID and points. The other table is 'name' and contains ID, Forename, and Surname.
I'm trying to search for the total number of points someone with the forename Anne, and surname Brown, scored.
Would I have to do a join? If so, is this correct?
SELECT Name.Forename, Name.Surname
FROM Name
FULL OUTER JOIN Points
ON Name.ID=Points.ID
ORDER BY Name.Forename;
But then I also have to add the points, so would I have to use:
SELECT SUM (`points`) FROM Points
Then there is also the WHERE statement so that it only searches for the person with this name:
WHERE `Forename`="Anne" OR `Surname`="Brown";
So how does this all come together (based on the assumption that you do something like this)?
SELECT Name.ID, Forename, Surname, SUM(Points)
FROM Name
INNER JOIN Points ON Name.ID = Points.ID
/* Optional WHERE clause:
WHERE Name.ForeName = 'Anne' AND Name.Surname='Brown'
*/
GROUP BY Name.ID, Name.Forename, Name.Surname
So, first, your answer:
select sum(points) as Points
from
Points
inner join Name on Name.ID = Points.ID
where
Name.Forename ='Anne' and Name.SurName='Brown'
Secondly, FULL JOINS are bad since they pull all values from both sets even those without matches. If you want to only return values that match your criteria (A & B) you must use an INNER JOIN.
Thirdly, here is the MySQL reference documentation on SQL statement syntax. Please consider reading up on it and familiarizing yourself at least with the basics like JOINs, aggregation (including GROUP BY and HAVING), WHERE clauses, UNIONs, some of the basic functions provided, and perhaps subqueries. Having a good base in those will get you 99% of the way through most MySQL queries.
You can write it like this with a subquery.
SELECT Name.Forename, Name.Surname, Name.ID,
(SELECT SUM (`points`) FROM Points where Points.ID = Name.ID) as total_points
FROM Name ORDER BY Name.Forename;
However, I would like to point out, that it appears that your linking of the tables is incorrect. I can not be completely sure without seeing the tables, but I imagine it should be where points.userid = name.id

Remove duplicates from LEFT JOIN query

I am using the following JOIN statement:
SELECT *
FROM students2014
JOIN notes2014 ON (students2014.Student = notes2014.NoteStudent)
WHERE students2014.Consultant='$Consultant'
ORDER BY students2014.LastName
to retrieve a list of students (students2014) and corresponding notes for each student stored in (notes2014).
Each student has multiple notes within the notes2014 table and each note has an ID that corresponds with each student's unique ID. The above statement is returning a the list of students but duplicating every student that has more than one note. I only want to display the latest note for each student (which is determined by the highest note ID).
Is this possible?
You need another join based on the MAX noteId you got from your select.
Something like this should do it (not tested; next time I'd recommed you to paste a link to http://sqlfiddle.com/ with your table structure and some sample data.
SELECT *
FROM students s
LEFT JOIN (
SELECT MAX(NoteId) max_id, NoteStudent
FROM notes
GROUP BY NoteStudent
) aux ON aux.NoteStudent = s.Student
LEFT JOIN notes n2 ON aux.max_id = n2.NoteId
If I may say so, the fact that a table is called students2014 is a big code smell. You'd be much better off with a students table and a year field, for many reasons (just a couple: you won't need to change your DB structure every year, querying across years is much, much easier, etc, etc). Perhaps you "inherited" this, but I thought I'd mention it.
GROUP the query by studentId and select the MAX of the noteId
Try :
SELECT
students2014.Student,
IFNULL(MAX(NoteId),0)
FROM students2014
LEFT JOIN notes2014 ON (students2014.Student = notes2014.NoteStudent)
WHERE students2014.Consultant='$Consultant'
GROUP BY students2014.Student
ORDER BY students2014.LastName

Retrieving aggregate values from multiple tables

I'm trying to get some aggregate values from different tables, but my problem is that they're being returned incorrectly, i.e. as multiplications of each other.
I've got these tables:
Institutes
----------
ID
name
Conversions
-----------
ID
institute_id
training_id
Trainings
---------
ID
institute_id
And I want to do get the Institute.name, the number of conversions and the number of trainings. I can get the name and one of the aggregate values, but when I try to get both values it returns the wrong number, because it will repeat all conversions for each training (or the other way round).
I'm using this SQL:
SELECT Institute.id
,Institute.name
,Institute.friendly_url
,count(Training.`id`) as totalTrainings
,count(Conversion.`id`) AS totalConversions
FROM institutes Institute
LEFT JOIN trainings Training ON Institute.`id` = Training.`institute_id`
LEFT JOIN conversions Conversion ON Training.`institute_id` = Conversion.`institute_id`
GROUP BY 1,2,3
I need to add more tables later on, using the same structure and also with the sole purpose of retrieving aggregates. I can make it work by using multiple queries, but then I'd run into the problem of not being able to use proper sorting, pagination and problems when I want to filter the data.
Creating a view is a solution I've been thinking about too, but I still need some SQL to create that view with.
I've also tried tinkering with the GROUP BY but to no avail.
Update
I'm now using subqueries in my join, which seems to work. Is there a more optimal solution though?
SELECT Institute.id
,Institute.name
,Institute.friendly_url
,Training.total
,Conversion.total
FROM institutes Institute
LEFT JOIN (SELECT institute_id, count(id) AS total
FROM `trainings`
GROUP BY institute_id) AS Training ON Training.institute_id = Institute.id
LEFT JOIN (SELECT institute_id, count(id) AS total
FROM `conversions`
GROUP BY institute_id) AS Conversion ON Conversion.institute_id = Institute.id
Try that. Doing a separate subqueries is better IMHO. And looks better also, easier to read the query.
SELECT
Institute.id
,Institute.name
,Institute.friendly_url
,(SELECT count(Training.`id`) FROM trainings Training WHERE Institute.`id` = Training.`institute_id` LIMIT 1) as totalTrainings
,(SELECT count(Conversion.`id`) FROM conversions Conversion ON Institute.`id` = Conversion.`institute_id` LIMIT 1) AS totalConversions
FROM institutes Institute
(realize this is two years later)
I believe a DISTINCT within your COUNT clause would fix the problem.
SELECT Institute.id
,Institute.name
,Institute.friendly_url
,count(DISTINCT Training.`id`) as totalTrainings
,count(DISTINCT Conversion.`id`) AS totalConversions
FROM institutes Institute
LEFT JOIN trainings Training ON Institute.`id` = Training.`institute_id`
LEFT JOIN conversions Conversion ON Training.`institute_id` = Conversion.`institute_id`
GROUP BY 1,2,3