Selecting data based on other table - mysql

I'm still newbie with database and mysql stuff, i want to learn how to use JOINs.I'm sorry i just have no idea to put this case on words. I hope all of You can understand by looking these data. Here's the tables with records:
Table student
student_id | student_name
-------------------------
1 Ana
2 Billy
3 Connor
Table comp
comp_id | subj_id | comp_name
--------------------------
1 24 Run
2 24 Swim
3 24 Jump
4 25 Eat
Table comp_mark
semester | subj_id | student_id | comp_id | mark
-------------------------------------------------
1 24 1 1 7
1 24 1 2 4
1 24 1 3 6
1 24 2 1 4
1 24 2 2 8
1 24 3 1 9
I'm expecting the result something like this from the query selecting comp_mark table(update):
student_name | semester | subject_id | comp_id | mark
-------------------------------------------------------
Connor 1 24 1 9
Connor null 24 2 null
Connor null 24 3 null
is there anyone can help me with this? Thank a ton
UPDATE
I've tried this (Guillaume Poussel's query) and ORDER it by student_name :
SELECT s.student_name, cm.semester, c.subj_id AS subject_id, c.comp_id, cm.mark
FROM student s
CROSS JOIN comp c
LEFT JOIN comp_mark cm ON s.student_id = cm.student_id
AND cm.subj_id = c.subj_id
AND cm.comp_id = c.comp_id
ORDER BY s.student_name
The results:

Try this one:
SELECT s.student_name, cm.semester, c.subj_id AS subject_id, c.comp_id, cm.mark
FROM student s
CROSS JOIN comp c
LEFT JOIN comp_mark cm ON s.student_id = cm.student_id AND cm.subj_id = c.subj_id AND cm.comp_id = c.comp_id

Think you need to add a cross join to get all the possible values from comp. Something like this:-
SELECT s.student_name, c.subj_id, s.student_id, c.comp_id, cm.mark
FROM student s
CROSS JOIN comp c
LEFT OUTER JOIN comp_mark cm
ON s.student_id = cm.student_id
AND cm.semester = '1'
AND cm.subj_id = '1'

Related

Create a view based on two tables with value substitution

I struggle with a query and I am not sure how to do this.
I would like to create a view my_view based on the table original_data_table where each values are substituted by non-null values from the table replacement_data_table whenever an id is specified.
Both original_data_table and replacement_data_table have the same structure but with different values.
I tried to use JOIN in my query but I'm not sure if it's the way to go.
SELECT * FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id
original_data_table
id name value
1 David 10
2 John 20
3 Sarah 30
4 Amy 40
replacement_data_table
id name value
1 NULL 50
2 Rick NULL
4 Emma 60
my_view
id name value
1 David 50
2 Rick 20
3 Sarah 30
4 Emma 60
You need COALESCE() to get the right value from the right table:
SELECT o.id,
COALESCE(r.name, o.name) as name,
COALESCE(r.value, o.value) as value
FROM original_data_table o LEFT JOIN
replacement_data_table r
ON o.id = r.id;
Try this:
SELECT o.id as id, IFNULL(r.name, o.name) as new_name, IFNULL(r.value, o.value) as new_value
FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id
Try it use case when
SELECT
o.`id`,case when o.`name` <> r.`name` and r.`name` is not null then
r.`name`
else o.`name`
end as name
,case when o.`value` <> r.`value` and r.`value` is not null then
r.`value`
else o.`value`
end as value
FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id
order by o.id
SQL Fiddle Demo Link
| id | name | name |
|----|-------|------|
| 1 | David | 50 |
| 2 | Rick | 20 |
| 3 | Sarah | 30 |
| 4 | Emma | 60 |

Query on two tables merged with same column names

I have these tables in my MySQL database:
BUYERS
ID|SELLER
----------------
0 |Paul
1 |Jean
2 |David
3 |Jack
4 |John
5 |Fred
6 |Peter
PARIS
ID|CAR
---------
0 |Toyota
1 |BMW
2 |Honda
LONDON
ID|CAR
---------
3 |Ford
4 |BMW
5 |Honda
6 |Honda
I use the followinq query :
SELECT b.id, b.seller, p.car
FROM buyers b
JOIN paris p
ON b.id = p.id
UNION ALL
SELECT b.id, b.seller, l.car
FROM buyers b
JOIN london l
ON g.id = l.id;
To get the following result :
ID|SELLER |CAR
----------------
0 |Paul |Toyota
1 |Jean |BMW
2 |David |Honda
3 |Jack |Ford
4 |John |BMW
5 |Fred |Honda
6 |Peter |Honda
I wanted to retrieve rows with "Honda" as "CAR" and I Tried to append the query with "Where car = 'Honda'" but without success..
Thanks for any help
Adding
WHERE car = 'Honda';
to your query only refers to the second query, i.e. the one after UNION ALL.
So either:
SELECT b.id, b.seller, p.car FROM buyers b JOIN paris p ON b.id = p.id
WHERE p.car = 'Honda'
UNION ALL
SELECT b.id, b.seller, l.car FROM buyers b JOIN london l ON b.id = l.id
WHERE p.car = 'Honda'
;
or
SELECT id, seller, car
FROM
(
SELECT b.id, b.seller, p.car FROM buyers b JOIN paris p ON b.id = p.id
UNION ALL
SELECT b.id, b.seller, l.car FROM buyers b JOIN london l ON b.id = l.id
) data
WHERE car = 'Honda';
Just appending WHERE car = 'Honda' is ambiguous. Which car column should be checked?
The easiest way to achieve this is to wrap your existing query within another select statement so that the query is applied on the resulting table, i.e.
SELECT * FROM
(
SELECT b.id, b.seller, p.car
FROM buyers b
JOIN paris p
ON b.id = p.id
UNION ALL
SELECT b.id, b.seller, l.car
FROM buyers b
JOIN london l
ON g.id = l.id;
)
WHERE car = 'Honda'
Can you please try below query:
select * from (
SELECT b.id, b.seller, p.car
FROM buyers b
JOIN paris p
ON b.id = p.id
UNION ALL
SELECT b.id, b.seller, l.car
FROM buyers b
JOIN london l
ON g.id = l.id;
)
where car = 'HONDA'
This is not an answer, but mere advice.
Just in case this is your real database (I'm pretty sure it isn't), here is some advice:
The table names tell us what entities you are dealing with. In your database this is buyers, parises, and londons. You probably see your mistake ;-)
Then looking into the buyers table we see the main column is called seller. What the heck? Is it buyer or seller?
Then a column called ID should be the table's ID and uniquely identify a record in the table. You, however, are using the buyers IDs in other tables and still call them ID.
In your example each buyer has only one other record either in London or in Paris. If this is the case, then you can simply make the car a column in the buyers table instead. If you do need an n:m relation, then call the IDs by what they are, i.e. buyer_id or the like.
In any case there should be a car table containing one record per car, in order to avoid misspellings like 'Homda' in some records.
You are showing car brands, but call the table cars. If it's really about brands only, a better name would hence be brands or car_brands or the like.
Here is an example on how to design the database:
Cars
id_car | name
-------+-------
1 | BMW
2 | Ford
3 | Honda
4 | Toyota
Sellers
id_seller | name
----------+------
0 | Paul
1 | Jean
2 | David
3 | Jack
4 | John
5 | Fred
6 | Peter
Sellers_Cars
id_seller | id_car
----------+-------
0 | 1
0 | 2
0 | 4
1 | 1
2 | 3
3 | 2
4 | 1
5 | 3
6 | 3
6 | 4
A possible query:
select name as honda_seller
from sellers
where id_seller in
(
select id_seller
from sellers_cars
where car_id = (select car_id from cars where name = 'Honda')
);

Selecting two conditions simultaneously

Say i've got the next to tables: Doctors, and Workdays:
DocNumbers | idNum
118 | 11
119 | 12
120 | 13
121 | 14
122 | 15
Notice: a doctor can work in several different workdays.
DocNum | Workday |AmountOfHours |
118 | 1 | 8 |
118 | 3 | 9 |
120 | 1 | 6 |
121 | 3 | 5 |
122 | 4 | 7 |
I want to create a new table containing all id's of the doctors that work in day 1 and day 3 - That means that i will get a table containing only 118.
So far i've got:
SELECT distinct Doctors.doctorNumber, idNum
FROM Doctors, Workdays
WHERE Workdays.dayInWeek in (1,3)
AND Workdays.doctorNumber=Doctors.doctorNumber
But it seems like a i get irrelevant results like 120 and 121.
So 'IN' is more like a 'OR'. Can't seem to find the equivalence for 'and'?
This is easy to do if you join the Workdays table twice, once for each day you want to check:
select Doctors.DocNumbers, Doctors.idNum
from Doctors
inner join Workdays as Workdays1 on Workdays1.DocNum = Doctors.DocNumbers and Workdays1.Workday = 1
inner join Workdays as Workdays3 on Workdays3.DocNum = Doctors.DocNumbers and Workdays3.Workday = 3;
http://www.sqlfiddle.com/#!2/4c530/3
Try this with simple join
SELECT DISTINCT w.`DocNum`, d.idNum
FROM doctors d
LEFT JOIN Workdays w ON(d.`DocNumbers`=w.`DocNum`)
LEFT JOIN Workdays ww ON(d.`DocNumbers`=ww.`DocNum`)
WHERE w.`Workday` = 1 AND ww.`Workday` =3
See fiddle here
Here is another way of doing same
SELECT w.`DocNum`, d.idNum
FROM doctors d
LEFT JOIN Workdays w ON(d.`DocNumbers`=w.`DocNum`)
GROUP BY d.`DocNumbers`
HAVING GROUP_CONCAT(w.`Workday` SEPARATOR ',')= '1,3'
See fiddle here

SQL for following scenario

I have following two tables in the database. One table is person table and other table is Entry table, entry table records the persons entrance to a department
Person Table
Person
Person_ID Person_NIC Person_Name Person_Last_Name Person_Age State
1 121212 ABC BCD 12 ACTIVE
2 212121 ABB BBB 13 NONACTIVE
3 111111 BBB CCC 14 ACTIVE
Entry Table
ENTRY
Entry_ID Person_ID Entry_Escort Entry_Date
1 1 David 20121210
2 1 David 20130110
3 1 David 20130111
4 1 David 20130112
5 1 David 20130113
6 2 David 20121210
7 2 David 20130110
8 2 David 20130111
9 2 David 20130112
10 2 David 20130113
I have to write sql for following scenario. I need to find Last Entry date for persons how are non active in the database for the month of January. could anybody help me with query. thanks in advance for any help.
This should be as as easy as this:
SELECT p.Person_ID, MAX(e.Entry_Date) as MaxEntryDate
FROM
Person p
INNER JOIN Entry e
ON p.Person_NIC = e.EntryID
WHERE p.State = 'NONACTIVE'
AND Entry_Date BETWEEN 20130101 AND 20130131
GROUP BY p.Person_ID
Try this::
Select
p.*,
MAX(Entry_Date)
from
persons p
inner join entry ep on (p.Person_NIC=ep.Entry_ID)
where State='NONACTIVE'
GROUP BY ep.Entry_ID
this should do what you need:
select p.person_id, p.person_nic, p.Person_Name, p.Person_Last_Name,
max(e.entry_date) max_entry_date
from person p
inner join entry e
on e.entry_id = p.person_nic
where p.state = 'NONACTIVE'
group by p.person_id, p.person_nic, p.Person_Name, p.Person_Last_Name
p.s. storing AGE in a database table is not good, as you'll have to constantly keep it up to date. it's better to store date of birth and compute age on the fly (or in a view/virtual column)
Try this out please: updated as you need month of january...
SQLFIDDLE DEMO
Select p.person_id, p.person_nic,
p.Person_Name,
p.Person_Last_Name,max(e.entry_date )
from person p
inner join entry e
on (p.Person_id = e.person_id)
where p.State='NONACTIVE'
and month(e.entry_date) = 1
GROUP BY p.person_nic, e.Entry_ID
;
| PERSON_ID | PERSON_NIC | PERSON_NAME | PERSON_LAST_NAME |MAX(E.ENTRY_DATE ) |
---------------------------------------------------------------------------------
| 2 | 212121 | ABB | BBB | January, 13 2013 |

mysql join query

I have two tables that I need to join. These are:
art
id | art
--------
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F
7 | G
8 | H
9 | I
and
Sess
artid | sessid
--------------
1 | 1
2 | 1
3 | 1
4 | 1
1 | 2
4 | 2
5 | 2
6 | 2
1 | 3
2 | 3
7 | 3
4 | 3
where Sess.artid is a foregin key to art.id.
From the tables above we can see that there are 3 sessions: A,B,C,D, A,D,E,F and A,B,G,D.
I want to get a ranking of the arts that occur along with art A. Something like:
D=3
B=2
How could I form such a query in mysql or postgres?
You need to join twice the session table to get the article sharing the same session.
Then join one time with article for the filter clause, and another time to get the name of the other article in the other session.
SELECT aSameSession.art, count(*)
FROM art a
INNER JOIN Sess s
ON a.id = s.artid
INNER JOIN Sess sSameArticle
ON sSameArticle.sessid = s.sessid
INNER JOIN art aSameSession
ON sSameArticle.artid = aSameSession.id
WHERE A.art = 'A'
AND aSameSession.art <> 'A'
GROUP BY aSameSession.art
Output :
B 2
C 1
D 3
E 1
F 1
G 1
This version could be a little difficult to understand, so here a version just with the ID of the article, which is much more simple :
SELECT sSameArticle.artid, count(*)
FROM Sess s
INNER JOIN Sess sSameArticle
ON sSameArticle.sessid = s.sessid
WHERE s.artid = 1
AND sSameArticle.artid != 1
GROUP BY sSameArticle.artid
Output :
2 2
3 1
4 3
5 1
6 1
7 1
Adding the name of the article is just cosmetic.
Something like this, perhaps:
select art,count(*)
from sessid
left join art on art.id=artid
where sessid in (select sessid from sess where artid=1)
group by artid;
?
Example of table structure and join queries on PostgreSQL
CREATE TABLE arts (
arts_id serial PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE sessions (
sessions_id integer NOT NULL,
arts_id integer NOT NULL REFERENCES arts
);
SELECT arts.name, count(sessions_id)
FROM arts
JOIN sessions USING (arts_id)
GROUP BY arts.name
ORDER BY count(sessions_id) DESC;
SELECT a.art, count(*) as ranking
FROM art a, sess s
WHERE a.id = s.artid
group by a.art
order by count(*) DESC;
For a statement in ANSI-92 syntax have a look at Konerak's answer.