My database has the following two tables
jobs:
+----+--------------+
| id | name |
+----+--------------+
| 1 | Mechanic |
| 2 | Programmer |
| 3 | Cleaner |
| 4 | Truck driver |
+----+--------------+
qualifications:
+--------+--------------------+
| job_id | qualification |
+--------+--------------------+
| 1 | drivers_license |
| 1 | engine_certificate |
| 2 | mysql_certificate |
| 4 | drivers_license |
+--------+--------------------+
Let's say that I have a drivers_license and a mysql_certificate. I want to create an SQL query that returns all jobs that don't have requirement I don't have. So the result of the query should be job id 2, 3 and 4.
I have tried the following query:
select *
from jobs j
join qualifications q
on j.id = q.job_id
where q.qualification = 'drivers_license' ||
q.qualification = 'mysql_certificate';
This returns id 1, 2 and 4 and therefore obviously doesn't work.
How can this be achieved in SQL? Any help is greatly appreciated.
You can use group by and having:
select j.name, j.id
from jobs j join
qualifications q
on j.id = q.job_id
group by j.name, j.id
having sum(q.qualification not in ('drivers_license' , 'mysql_certificate')) = 0;
Related
I have 1 master_table and 2 sub_tables. I want the join the 3 columns together (but the problem is the 2 sub_tables do not have any column that share the same value) and then SELECT * based on 2 different columns from the 2 sub_tables.
I've searched and tried many ways of coding, but couldn't find a solution.
SELECT *
FROM (master INNER JOIN sub_1 ON master.id=sub_1.id WHERE sub_1.column_1 = 'Y')
AND (master INNER JOIN sub_2 ON master.id=sub_2.id WHERE sub_2.column_2 = 'Y')
ORDER BY master.id
++++++++++++++++++++++++++++++++++++++++++++++++++
* Finally, solved. See the solution at the bottom of this post. *
++++++++++++++++++++++++++++++++++++++++++++++++++
===========
Edit: explain more about my data, problem and MySQL code
I have 3 tables stored in MySQL as follow
Master_table: regist
------------------------------------------
| reg_no | firstname | lastname | submit |
------------------------------------------
| 1 | first_A | last_A | N |
| 2 | first_B | last_B | A |
| 3 | first_C | last_C | P |
| 4 | first_D | last_D | P |
| 5 | first_E | last_E | A |
| 6 | first_F | last_F | N |
| 7 | first_G | last_G | N |
| 8 | first_H | last_H | A |
------------------------------------------
Sub_1: sub_A Sub_2: sub_P
------------------------------ ------------------------------
| reg_no | A_title | reply_A | | reg_no | P_title | reply_P |
------------------------------ ------------------------------
| 2 | 222 | Y | | 3 | 333 | N |
| 5 | 555 | N | | 4 | 444 | Y |
| 8 | 888 | Y | ------------------------------
------------------------------
I want to create a query that gives result like this
----------------------------------------------------------------------------------
| reg_no | firstname | lastname | submit | A_title | reply_A | P_title | reply_P |
----------------------------------------------------------------------------------
| 2 | first_B | last_B | A | 222 | Y | | |
| 8 | first_H | last_H | A | 888 | Y | | |
| 4 | first_D | last_D | P | | | 444 | Y |
----------------------------------------------------------------------------------
or
-----------------------------------------------------------
| reg_no | firstname | lastname | submit | title | reply |
-----------------------------------------------------------
| 2 | first_B | last_B | A | 222 | Y |
| 8 | first_H | last_H | A | 888 | Y |
| 4 | first_D | last_D | P | 444 | Y |
-----------------------------------------------------------
$sql = "SELECT *
FROM (regist INNER JOIN sub_A ON regist.reg_no = sub_A.reg_no WHERE sub_A.reply_A = 'Y')
AND (regist INNER JOIN sub_P ON regist.reg_no = sub_P.reg_no WHERE sub_P.reply_P = 'Y')
ORDER BY regist.reg_no";
Expected outcome:
ECHO personal data of all registrants who got reply as 'Y'
if($row['submit']=="A") $title = $row['A_title'];
elseif($row['submit']=="P") $title = $row['P_title'];
$result = mysql_query($sql) or die(mysql_error());
while($row = mysql_fetch_array($result))
{
echo $row['reg_no']." / ".$row['firstname']." ".$row['lastname']." / ".$title."<br>";
}
Problem: my SELECT code resulted in error. The code from #GMB and #Rogue didn't error, but echo give nothing.
If it is not possible to code a query as I want, I will just modify the column names (sub_1.reply_A and sub_2.reply_P) to be the same and change the input code in other webpages. However, it would be best if there is a way because I don't know whether the 'reply' columns were used somewhere else.
========================
Solution: a little modification from #Rogue code
SELECT *
FROM master
LEFT OUTER JOIN sub_1
ON master.id=sub_1.id
LEFT OUTER JOIN sub_2
ON master.id=sub_2.id
WHERE sub_1.column_1 = 'Y'
OR sub_2.column_2 = 'Y'
ORDER BY master.id
Do you just want simple JOINs between these 3 tables ?
SELECT m.*, s1.*, s2.*
FROM master m
INNER JOIN sub_1 s1 ON m.id=s1.id AND s1.column_1 = 'Y'
INNER JOIN sub_2 s2 ON m.id=s2.id AND s2.column_2 = 'Y'
ORDER BY m.id;
If you have master records that may not exist in both sub tables, you can switch to LEFT JOIN to avoid filtering them out.
Guidelines :
typical syntax is SELECT ... FROM table1 INNER JOIN table2 ON ... INNER JOIN table3 ON...
better put all conditions related to a JOINed table in the ON clause of the join rather than in the WHERE clause
avoid SELECT * : be specific about the columns you want to select
use table aliases to make the query easier to read
You're a little off syntactically:
SELECT *
FROM master
LEFT OUTER JOIN sub_1
ON master.id=sub_1.id
LEFT OUTER JOIN sub_2
ON master.id=sub_2.id
WHERE sub_1.column_1 = 'Y'
AND sub_2.column_2 = 'Y'
ORDER BY master.id
Personally I would recommend not using SELECT * and only grabbing the data you will need. As for determining what join to use, I like to link to CodingHorror's blog post in these times.
Edit: swapped INNER to LEFT OUTER, per OP's update
I have a tables like this:
Users
+----+----------+-------------+
| id | name | other_stuff |
+----+----------+-------------+
| 1 | John Doe | x |
| 2 | Jane Doe | y |
| 3 | Burt Olm | z |
+----+----------+-------------+
Places
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Building A | x |
| 2 | Building B | y |
+----+------------+-------------+
Subjects
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Math | x |
| 2 | English | y |
+----+------------+-------------+
And a joining table:
PastLectures = lectures that took place
+----+-----------+----------+------------+---------+------------+
| id | id_users | id_place | id_subjects| length | date |
+----+-----------+----------+------------+---------+------------+
| 1 | 1 | 1 | 1 | 60 | 2015-10-25 |
| 2 | 1 | 1 | 1 | 120 | 2015-11-06 |
| 3 | 2 | 2 | 2 | 120 | 2015-11-04 |
| 4 | 2 | 2 | 1 | 60 | 2015-11-10 |
| 5 | 1 | 2 | 1 | 60 | 2015-11-10 |
| 6 | 2 | 2 | 1 | 40 | 2015-11-15 |
| 7 | 1 | 2 | 2 | 30 | 2015-11-15 |
+----+-----------+----------+------------+---------+------------+
I would like to display SUM of all lessons for each user for given month. The SUM should by grouped by each Places and Subjects.
The result in final PHP output should look like this:
November 2015
+------------+-------------+---------------+-------------+
| Users.name | Places.name | Subjects.name | sum(length) |
+------------+-------------+---------------+-------------+
| Burt Olm | - | - | - |
| Jane Doe | Building B | Math | 100 |
| = | = | English | 120 |
| John Doe | Building A | Math | 120 |
| = | Building B | Math | 60 |
| = | = | English | 30 |
+------------+-------------+---------------+-------------+
I have tried creating the full output in pure SQL query using multiple GROUP BY (Group by - multiple conditions - MySQL), but when I do GROUP BY User.id,Places.id it shows each user only once (3 results) no matter the other GROUP BY conditions.
SQL:
SELECT PastLectures.id_users,Users.name AS user,Places.name AS places,Subjects.name AS subjects
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date >= \''.$monthStart->format('Y-m-d H:i:s').'\' AND date <= \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY Users.id,Places.id
ORDER BY Users.name,Places.name,Subjects.name
But I don't mind if part of the solution is done in PHP, I just don't know what to do next.
EDIT:
I also have a table Timetable, that stores who regularly teaches what and where. It stores only used combinations of the tables (each valid combination once).
Timetable = lectures that regularly take place
+----+-----------+----------+------------+-------------+
| id | id_users | id_place | id_subjects| other_stuff |
+----+-----------+----------+------------+-------------+
| 1 | 1 | 1 | 1 | x |
| 2 | 1 | 2 | 1 | y |
| 3 | 1 | 2 | 2 | z |
| 4 | 2 | 2 | 1 | a |
| 5 | 2 | 2 | 2 | b |
+----+-----------+----------+------------+-------------+
Is it possible to add only users with combinations that have a row in this table?
In this case it would mean omitting Burt Olm (no id=3 in Timetable). But if Burt has a Timetable entry and still no PastLectures entry, he would show here as in sample result (he should have had a lecture that month, because he is in Timetable, but no lectures took place).
Based on #Barmar's solution I updated the final SQL by making Timetable a primary table and adding one more LEFT JOIN to suffice those needs.
Final SQL:
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(PastLectures.length)
FROM Timetable
LEFT JOIN PastLectures ON PastLectures.id_users = Timetable.id_users AND PastLectures.id_place = Timetable.id_place AND PastLectures.id_subjects = Timetable.id_subjects
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON Timetable.id_Place = Places.id
LEFT JOIN Subjects ON Timetable.id_Subjects = Subjects.id
LEFT JOIN Users ON Timetable.id_users = Users.id
GROUP BY Timetable.id,Timetable.id_users,Timetable.id_Place,Timetable.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
You need to include Subjects.id in the GROUP BY, so you get a separate result for each subject.
Also, you shouldn't use columns in tables that are joined with LEFT JOIN in the GROUP BY column. If you do that, all the non-matching rows will be grouped together, because they all have NULL in that column. Use the columns in the main table.
GROUP BY PastLectures.id_users, PastLectures.id_Place, PastLectures.id_Subjects
DEMO
Note that there's no row for Burt Olm in the demo output, because all his rows are filtered out by the WHERE clause. If you want all users to be shown, you should make Users the main table, not PastLectures. And the date criteria needs to be moved into the ON clause when joining with PastLectures.
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(length)
FROM Users
LEFT JOIN PastLectures ON PastLectures.id_users = Users.id
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON PastLectures.id_Place = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
GROUP BY Users.id, PastLectures.id_Place, PastLectures.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
DEMO
According to standard SQL, you should GROUP BY all the fields you select, except for the aggregated fields (like sum). Althought MySql allows to do otherwise, when it can be done adhering to the standards, it is better to do so (who knows when you need to port your code to another database engine). So write your SQL like this:
SELECT PastLectures.id_users,
Users.name AS user,
Places.name AS places,
Subjects.name AS subjects,
Sum(length)
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date BETWEEN \''.$monthStart->format('Y-m-d H:i:s').'\'
AND \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY PastLectures.id_users,
Users.name,
Places.name,
Subjects.name
ORDER BY Users.name,
Places.name,
Subjects.name
I am trying to get some statistics for an online game I maintain. I am searching for an SQL statement to get the result on the bottom.
There are three tables:
A table with teams, each having a unique identifier.
table teams
---------------------
| teamid | teamname |
|--------|----------|
| 1 | team_a |
| 2 | team_x |
---------------------
A table with players, each having a unique identifier and optionally an affiliation to one team by it's unique teamid.
table players
--------------------------------
| playerid | teamid | username |
|----------|--------|----------|
| 1 | 1 | user_a |
| 2 | | user_b |
| 3 | 2 | user_c |
| 4 | 2 | user_d |
| 5 | 1 | user_e |
--------------------------------
Finally a table with events. The event (duration in seconds) is related to one of the players through their playerid.
table events.
-----------------------
| playerid | duration |
|----------|----------|
| 1 | 2 |
| 2 | 5 |
| 3 | 3 |
| 4 | 8 |
| 5 | 12 |
| 3 | 4 |
-----------------------
I am trying to get a result where the durations of all team members is summed up.
result
--------------------------
| teamid | SUM(duration) |
|--------|---------------|
| 1 | 14 | (2+12)
| 2 | 15 | (3+8+4)
--------------------------
I tried several combinations of UNION, WHERE IN, JOIN and GROUP but could not get it right. I am using PostgreSQL and PHP. Can anyone help me?
Just use sum with group by:
select t.teamid, sum(e.duration)
from team t
join players p on t.teamid = p.teamid
join events e on p.playerid = e.playerid
group by t.teamid
If you need all teams to be returned even if they don't have events, then use an outer join instead.
Try this
SELECT teamid, Sum(duration),
AS LineItemAmount, AccountDescription
FROM teams
JOIN teams ON teams.teamid = players.teamid
JOIN events ON players.playersid = events.playersid
JOIN GLAccounts ON InvoiceLineItems.AccountNo = GLAccounts.AccountNo
GROUP BY teamid
http://www.w3computing.com/sqlserver/inner-joins-join-two-tables/
I have a big filter with many options and want to generate the query for sql automaticle and without many code.
GET:
searchvalue=abc
&title=abc
&description=abc
&category=1
&subcategory=2
&zip=7
&city=ke
&country=DE
SQL:
SELECT activity.* FROM activity,subcategory,city,country
WHERE activity.title LIKE '%abc%' OR activity.description LIKE '%abc%'
AND subcategory.SubID = 2
AND city.zip LIKE '%7%'
AND city.City LIKE '%ke%'
AND country.CShort= 'DE'
With this options, I have 1 row in my database.
The answer is this row many times, many many times.
I know that the sql duplicate a row, when a table is not used in a WHERE clausel - but why he do it now and how can I solve that?
Edit: I have a ER, but the database is in german (school project), maybe it help you to understand:
Thanks!
You are doing a cross product by selecting multiple tables. SQL will return every row from the one table combined with every row in the other table.
For example in a database with table a
|------|----------|
| idA | textA |
|------|----------|
| 1 | fooA |
| 2 | barA |
|------|----------|
and table b
|------|----------|
| idB | textB |
|------|----------|
| 1 | fooB |
| 2 | barB |
|------|----------|
when you do
SELECT * FROM a, b
you would get
|------|----------|------|----------|
| idA | textA | idB | textB |
|------|----------|------|----------|
| 1 | fooA | 1 | fooA |
| 1 | fooA | 2 | barA |
| 2 | barA | 1 | fooB |
| 2 | barA | 2 | barB |
|------|----------|------|----------|
To combine these rows logically you do a JOIN. That means you tell in your query which rows belong together. You can do so by JOIN clause or without JOIN clause directly in the WHERE clause.
Back to the example you would do
SELECT * FROM a, b
WHERE a.idA = b.idB
-- or
SELECT * FROM a
JOIN b ON a.idA = b.idB
you would get only 2 rows.
|------|----------|------|----------|
| idA | textA | idB | textB |
|------|----------|------|----------|
| 1 | fooA | 1 | fooA |
| 2 | barA | 2 | barB |
|------|----------|------|----------|
To answer your question:
You have to support JOIN/WHERE clauses to connect your tables activity, subcategory, city and country according to your database schema.
I don't know your table structures but for example clauses like this:
WHERE
...
AND city.country_id = country.id
AND activity.subcategory_id = subcategory.id
AND ...
I have the following schema (two tables):
**APPS**
| ID (bigint) | USERID (Bigint) | USAGE_START_TIME (datetime) |
------------------------------------------------------------------
| 1 | 12 | 2013-05-03 04:42:55 |
| 2 | 12 | 2013-05-12 06:22:45 |
| 3 | 12 | 2013-06-12 08:44:24 |
| 4 | 12 | 2013-06-24 04:20:56 |
| 5 | 13 | 2013-06-26 08:20:26 |
| 6 | 13 | 2013-09-12 05:48:27 |
**USAGE**
| ID (bigint) | APPID (bigint) | DEVICEID (bigint) | HIGH_COUNT (bigint) | MEDIUM_COUNT (bigint) |
--------------------------------------------------------------------------------------------------------
| 1 | 1 | 2 | 400 | 200 |
| 2 | 1 | 3 | 200 | 100 |
| 3 | 2 | 3 | 350 | 40 |
| 4 | 3 | 4 | 2 | 400 |
| 5 | 4 | 2 | 4 | 30 |
| 6 | 5 | 3 | 50 | 300 |
Explanation:
So, there are two tables.
Now I want to find the following:
Given a USERID, Get sum of HIGH_COUNT & MEDIUM_COUNT. While counting
the SUM it should be taken care that: If in USAGE, same device is used
more than once, then the record which has the latest info (based on
APPS.USAGE_START_TIME), should be considered while calculating the
sum.
For ex:
For above schema, result should be (for userid=12) :
| HIGH_COUNT (bigint) | MEDIUM_COUNT (Bigint) |
-----------------------------------------------
| 356 | 470 |
SQL Fiddle: http://sqlfiddle.com/#!2/74ae0f
If a user uses multiple APPS on one device, this query will use the APPS row with the highest usage_start_time:
select a.userid
, sum(u.high_count)
, sum(u.medium_count)
from apps a
join `usage` u
on u.appid = a.id
join (
select u.device_id
, a.userid
, max(a.usage_start_time) as max_start_time
from apps a
join `usage` u
on u.appid = a.id
group by
u.device_id
, a.userid
) filter
on filter.device_id = u.device_id
and filter.userid = a.userid
and filter.max_start_time = a.usage_start_time
group by
a.userid
In your dataset, it will select usage rows 5, 3, 4 for user 12.
See it working at SQL Fiddle.
I can't quite get your numbers, but something like this should work...
SELECT a.userid
, SUM(u.high_count)
, SUM(u.medium_count)
FROM apps a
JOIN `usage` u
ON u.appid = a.id
JOIN
( SELECT userid
, deviceid
, MAX(usage_start_time) max_usage_start_time
FROM apps a
JOIN `usage` u
ON u.appid = a.id
GROUP
BY userid
, deviceid
) x
ON x.userid = a.userid
AND x.deviceid = u.deviceid
AND x.max_usage_start_time = a.usage_start_time
GROUP
BY userid;
Note that usage is a reserved word. Therefore, this is a bad name for a column (or a table). Also, note inconsistencies between your question and your fiddle.
I think not had chance to test it but
SELECT SUM(HIGH_COUNT), SUM(MEDIUM_COUNT) FROM `USAGE` INNER JOIN `APPS` ON USAGE.APPID=APPS.ID WHERE APPS.USERID=$input_user_id_to_lookup
will give you your counts.
For yoru other question (homework?) you didn't give us the full schema so we can't guess what you need doing.
Also whoever designed that db should be shot its horrible