NOT DISTINCT query in mySQL - mysql

I have been asked to create a query for this on a simple employee database columns include:
ninumber - first name - last name - address - SuperVisorNiNumber
The employees and supervisors are all held in the same table and are referenced by their ninumbers. The query I've been asked to build is:
v. Find the NI Numbers, first and last names of employees and NI numbers of supervisors where the employees share that supervisor and both employees and supervisors work in department 8. You will refer to the employee relation twice was done in query vi of Practical 2. Your results should be presented in columns with the following titles ‘Employee NI number’, ‘First Name’, ‘Last Name’ and ‘Supervisor NI Number’.
Therefore I created this query:
SELECT e1.ninumber,
e1.fname,
e1.minit,
e1.lname,
e1.address,
e1.superNiNumber,
COUNT(*) AS nrOfOccurences
FROM employee AS e1,
employee AS e2
WHERE e1.dno = 8
AND e1.superNiNumber = e2.ninumber
AND e2.dno = 8
GROUP BY e1.superNiNumber
HAVING COUNT(*) > 1
I couldn't do a not distinct query to work out this part of the question - "where the employees share that supervisor". This query returns a grouping of the rows which in turn hides some of the rows I want to show.
My question is: Is my query correct for the question and can I do a NON DISTINCT query in mySQL to get the database to return all of the fields instead of grouping them together.
Reutrn results from my query
NInumber fname minit lname address supervisorNiNum number of occerences
666666601 Jill J Jarvis 6234 Lincoln, Antrim, UK 666666600 2
666666607 Gerald D Small 122 Ball Street, Letterkenny, IRL 666666602 3
666666604 Billie J King 556 WAshington, Antrim, UK 666666603 2
Thanks.

In your result table column description, I see no minit, address and number of occurrences. Therefore I would simplify your select to:
SELECT e1.ninumber,
e1.fname,
e1.lname,
e1.superNiNumber,
FROM employee AS e1,
employee AS e2
WHERE e1.dno = 8
AND e1.superNiNumber = e2.ninumber
AND e2.dno = 8
and (select count(*) from employee e3
where e3.superNiNumber = e1.superNiNumber) > 1;

The accepted answer is quite slow in performance. After a bit of searching I managed to produce a faster running equivalent:
SELECT e1.ninumber,
e1.fname,
e1.lname,
e1.superNiNumber
FROM employee AS e1, (SELECT superNiNumber,
COUNT(*) AS count
FROM employee
GROUP BY superNiNumber
HAVING count > 1) AS e2
WHERE e1.superNiNumber = e2.superNiNumber
Credit: http://www.programmingforums.org/thread14669.html

Related

How to use a text (string) to depict no relationship between tables from Oracle to HTML?

I am getting data from an Oracle database into an HTML table, I am replacing nulls with strings but I am just wondering if there is a way to add a string into html such as 'Unavailable' where there is no relationship
NB: PLACENAME_ID is a foreign key in the OWNER table
place = """ SELECT coalesce(NAME,'Unknown') FROM PLACES WHERE PLACENAME_ID = """ + str(pid)
ownerquery = """ SELECT NAME, FROM OWNER WHERE PLACENAME_ID = """ + str(pid)
The second query is invalid; comma between NAME and FROM?
Anyway: what you described sounds like outer join which enables you to display "something" when there's "nothing". Here's an example, based on Scott's schema:
There are 4 departments. Pay attention to department DEPTNO = 40, as there are no employees that work there:
SQL> select * from dept where deptno = 40;
DEPTNO DNAME LOC
---------- -------------- -------------
40 OPERATIONS BOSTON
SQL> select count(*) from emp where deptno = 40;
COUNT(*)
----------
0
In order to display it as well (when joined to the EMP table), you'd use outer join:
SQL> select d.deptno, d.dname, e.ename
2 from dept d left join emp e on e.deptno = d.deptno
3 order by d.deptno;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING KING
10 ACCOUNTING CLARK
10 ACCOUNTING MILLER
20 RESEARCH FORD
20 RESEARCH SMITH
20 RESEARCH JONES
30 SALES JAMES
30 SALES TURNER
30 SALES MARTIN
30 SALES WARD
30 SALES ALLEN
30 SALES BLAKE
40 OPERATIONS --> this!
13 rows selected.
SQL>
If there was no outer join, you wouldn't even see the last row.
Now, how to implement that to your code, I wouldn't know - it doesn't even have any kind of a join, but it should as you mentioned a "foreign key". We don't have that tables' description either.

Can we use multiple AND conditions for the same column in WHERE clause? [duplicate]

This question already has answers here:
How to return rows that have the same column values in MySql
(3 answers)
Closed 4 years ago.
I tried using AND for the same column in WHERE clause, but it is not giving me any output. But there are two Customer names in the data set which satisfies the given condition. Where have I gone wrong?
SELECT CONCAT(c.CustFirstName," ",c.CustLastName) AS CustomerName
FROM Musical_Preferences mp
JOIN Customers c ON c.CustomerID = mp.CustomerID
WHERE mp.StyleID = 15 AND mp.StyleID = 24;
This above query worked fine when I put OR for same column in WHERE clause, but why not for AND?
My data:
CustomerID CustFirstName CustLastName CustStreetAddress CustCity CustState CustZipCode CustPhoneNumber CustomerID StyleID PreferenceSeq
10001 Doris Hartwig 4726 - 11th Ave. N.E. Seattle WA 98105 555-2671 10001 10 2
10001 Doris Hartwig 4726 - 11th Ave. N.E. Seattle WA 98105 555-2671 10001 22 1
The condition StyleID = 15 AND StyleID = 24 can never return anything because it can never be true. So what you're getting is expected. If you want to get the customers that have both StyleID 15 and 24 (in different records), then you need to group the records by customer:
SELECT CONCAT(c.CustFirstName," ",c.CustLastName) AS CustomerName
FROM Musical_Preferences mp
JOIN Customers c ON c.CustomerID = mp.CustomerID
WHERE mp.StyleID IN(15, 24)
GROUP BY c.CustomerID, c.CustFirstName, c.CustLastName
HAVING COUNT(mp.CustomerID) = 2;
I added c.CustFirstName and c.CustLastName to the GROUP BY. Alternatively, you can only group by the ID and then use MIN() or MAX() on the first and last name. Both ways are more or less then same.
This query will only work if your data can never have a customer with the same StyleID more than once (example: customer 1001 has 3 records with StyleID 15, 24, and 24).
WHERE conditions operate on rows created by the FROM clause, not across rows if you try WHERE somefield = 1 AND somefield = 2 you will never get results, a field cannot have two values at the same time.
I think what you are looking for is
SELECT ...
FROM ...
WHERE somefield IN (15, 24)
GROUP BY ...
HAVING COUNT(DISINCT somefield ) = 2
;
If your query with a single condition does not return any row, your query won't return any row.
Please try this:
SELECT CONCAT(c.CustFirstName," ",c.CustLastName) AS CustomerName
FROM Musical_Preferences mp
JOIN Customers c ON c.CustomerID = mp.CustomerID
WHERE mp.StyleID = 24;
Does it return anything?

SQL - Find AT LEAST TWO DISTINCT / SEPARATE / DIFFERENT values on another Table

While taking an Online database course (for beginner) a problem has came to my attention, where I had to find queries involving ...AT LEAST TWO DISTINCT values... For example,
the COMPANY database in the ELMASRI book which states: Find all employee who work on at least two distinct projects. And the solution (which works great) is
SELECT DISTINCT LName FROM Employee e1
JOIN Works_On AS w1 ON (e1.Ssn = w1.Essn)
JOIN Works_On AS w2 ON (e1.Ssn = w2.Essn)
WHERE w1.Pno <> w2.Pno
Similarly in case of the STUDENT/COURSE database (i forgot the source): Find the Student_ID of the Students who take at least two distinct Courses. And the solution looks also simple (though its not tested)
SELECT e1.Student_ID FROM Enroll AS e1, Enroll AS e2
WHERE e1.Student_ID = e2.Student_ID
AND e1.Course_ID <> e2.Course_ID
In my problem, I have to Find the name and customer ID of those customers who have accounts in at least two branches of distinct types (i.e., which do not have the same Branch Type).
from the following table (MySql)
CUSTOMER: BRANCH: ACCOUNT:
Cust_ID Lname Br_ID Br_Type Acc_Num Br_ID Cust_ID Balance
------- ------ ----- ------- ------- ----- ------- -------
1 Mr.A 10 big 1001 10 1 2000
2 Mr.B 11 small 1002 11 1 2500
3 Mr.C 12 big 1003 13 1 3000
4 Mr.D 13 small 1004 12 2 4000
1005 13 3 4500
1006 10 4 5000
1007 12 4 6000
Result Table should look like the following:
Lname Cust_ID
----- -------
Mr.A 1
Only Mr.A has account in a branch whose type is 'big' as well as in a branch whose type is 'small'
I tried the following which didnt work
SELECT DISTINCT c1.Lname, a1.Cust_ID FROM Customer AS c1
JOIN Account a1 ON (c1.Cust_ID=a1.Cust_ID)
JOIN Branch b1 ON (a1.Br_ID=b1.Br_ID)
JOIN Branch b2 ON (a1.Br_ID=b2.Br_ID)
WHERE b1.Br_Type<>b2.Br_Type;
What am I exactly doing wrong? Sorry for such a long description but i wanted to make sure that the question is understandable and a little explanation on < > part will be highly appreciated.
You're trying to pull 2 different Branch records off the same Account record - but that can't happen. What you want is to search on 2 different Account records with associated Branches of a different type:
SELECT DISTINCT c1.Lname, a1.Cust_ID FROM Customer AS c1
JOIN Account a1 ON (c1.Cust_ID=a1.Cust_ID)
JOIN Account a2 ON (c1.Cust_ID=a2.Cust_ID)
JOIN Branch b1 ON (a1.Br_ID=b1.Br_ID)
JOIN Branch b2 ON (a2.Br_ID=b2.Br_ID)
WHERE b1.Br_Type<>b2.Br_Type;
SQLFiddle here
A more efficient approach that gives the same result, would be to use GROUP BY and HAVING COUNT(DISTINCT Br_Type) >= 2 - which is what #GordonLindoff proposed.
The problem with your query is the two on conditions. They are returning the same row in branch, because the join conditions are the same.
In any case, I think there is a better way to think about these types of queries (what I call "set--sets" queries). Think of these as aggregation. Aggregation at the customer level, then using the having clause to filter the customers:
SELECT c.Lname, a.Cust_ID
FROM Customer AS c JOIN
Account a
ON c.Cust_ID = a.Cust_ID JOIN
Branch b
ON a.Br_ID = b.Br_ID
GROUP BY c.Lname, a.Cust_ID
HAVING count(distinct b.br_type) > 1;

Subquery in SQL

So I have an Olympic database, the basic layout is that there's a competitors table with competitornum, givenname, and familyname (other columns aren't necessary for this)
There's also a results table with competitornum, and place (between 1 and 8).
I'm trying to get the givenname and familyname and total number of gold, silver, and bronze medals (place = 1, 2 or 3)
Here's what I've got so far:
SELECT c.Givenname, c.Familyname, places AS TotalPlaces
FROM Competitors c,
(SELECT COUNT(*) as places
FROM Results r, Competitors c
WHERE r.Competitornum = c.Competitornum
AND r.Place > 0
AND r.Place < 4) q
However it returns everyone's name, and the same total places (which happens to be 78). e.g.
John Smith 78
Cassandra Jane 78
Bob Turner 78
Currently your query is conducting CROSS JOIN producing caertesian product.
When using aggregate function (count, max, min,...), the records should be group by non-aggregated column. Try this,
SELECT c.Givenname, c.Familyname, COUNT(r.places) AS TotalPlaces
FROM Competitors c INNER JOIN Results r
ON r.Competitornum = c.Competitornum
WHERE r.place IN (1,2,3)
GROUP BY c.Givenname, c.Familyname

Grouping and Accumulating Records at the same time

I am writing a query against an advanced many-to-many table in my database. I call it an advanced table because it is a many-to-many table with and extra field. The table maps data between the fields table and the students table. The fields table holds potential fields that a student can used, kind of like a contact system (i.e. name, school, address, etc). The studentvalues table that I need to query against holds the field id, student id, and the field answer (i.e. studentid=1; fieldid=2; response=Dave Long).
So my table looks like this:
What I need to do is take a few passed in values and create a grouped accumulated report. I would like to do as much in the SQL as possible.
So that data that I have will be the group by field (a field id), the cumulative field (a field id) and I need to group the students by the group by field and then in each group count the amount of students in the cumulative fields.
So for example I have this data
ID STUDENTID FIELDID RESPONSE
1 1 2 *(city)* Wallingford
2 1 3 *(state)* CT
3 2 2 *(city)* Wallingford
4 2 3 *(state)* CT
5 3 2 *(city)* Berlin
6 3 3 *(state)* CT
7 4 2 *(city)* Costa Mesa
8 4 3 *(state)* CA
I am hoping to write one query that I can generate a report that looks like this:
CA - 1 Student
Costa Mesa 1
CT - 3 Students
Berlin 1
Wallingford 2
Is this possible to do with a single SQL statement or do I have to get all the groups and then loop over them?
EDIT Here is the code that I have gotten so far, but it doesn't give the proper stateSubtotal (the stateSubtotal is the same as the citySubtotal)
SELECT state, count(state) AS stateSubtotal, city, count(city) AS citySubtotal
FROM(
SELECT s1.response AS city, s2.response AS state
FROM studentvalues s1
INNER JOIN studentvalues s2
ON s1.studentid = s2.studentid
WHERE s1.fieldid = 5
AND s2.fieldid = 6
) t
GROUP BY city, state
So to make a table that looks like that, I would assume something like
State StateSubtotal City CitySubtotal
CA 1 Costa Mesa 1
CT 3 Berlin 1
CT 3 Wallingford 2
Would be what you want. We can't just group on Response, since if you had a student answer LA for city, and another student that responds LA for state (Louisiana) they would add. Also, if the same city is in different states, we need to first lay out the association between a city and a state by joining on the student id.
edit - indeed, flawed first approach. The different aggregates need different groupings, so really, one select per aggregation is required. This gives the right result but it's ugly and I bet it could be improved on. If you were on SQL Server I would think a CTE would help but that's not an option.
select t2.stateAbb, stateSubtotal, t2.city, t2.citySubtotal from
(
select city, count(city) as citySubTotal, stateAbb from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t1
group by city, stateabb
) t2 inner join (
select stateAbb, count(stateabb) as stateSubTotal from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t3
group by stateabb
) t4 on t2.stateabb = t4.stateabb