Say I've got the next to tables: Drugs, and Clients:
idNum | drugName
118 | drug1
118 | drug2
120 | drug1
120 | drug2
120 | drug3
121 | drug1
121 | drug3
122 | drug2
Clients:
idNum | Name | lastName |
118 | name1 | last1 |
119 | name2 | last2 |
120 | name3 | last3 |
121 | name4 | last4 |
122 | name5 | last5 |
I want to create a new table containing all id's, Names and last names of clients who took the same drugs as client with the idNum='118'
Which means i need to get a table with only '120'.
So far i've got:
SELECT Clients.idNum,firstName, lastName
FROM Clients, Drugs
WHERE Drugs.idNum=Clients.idNum
AND Clients.idNum<>'118'
AND Clients.idNum IN
(Select idNum From Drugs Where drugName IN
(Select drugName from Drugs where idNum='118'))
But this gives me also 121 and 122.
I guess 'IN' is something like 'Exists', So if someone has only 1 of the drugs the condition is enough. How do i actually compare between a table which i get from here:
(Select drugName from Drugs where idNum='118')
to a field in 'Drugs'?
Or maybe, How do i create a table for each of the customers and then compare it with the table?
This is a tricky query. The idea is to generate the list of drugs for each client that matches client 118. This is the "master" list. Then, use left outer join to bring in the actual drugs for each client. When a match fails, then filter out the client:
select master.idNum, master.firstName, master.lastName
from (select d.drugName, c.*
from Drugs d cross join
Clients c
where d.idnum = 118
) master left outer join
Drugs d
on master.drugName = d.drugName and
master.idNum = d.idNum
group by master.idNum, master.firstName, master.lastName
having count(d.drugName) = count(master.drugName);
The SQL Fiddle is here. If you want to additionally filter out client 118, then change the nested where clause to something like d.idnum = 118 and c.idnum <> 118.
You should also learn proper join syntax and the use of table aliases.
Related
I'm trying to find the most efficient and optimized way of querying husband and wife data for a search function in a finance application. The clients can be single or married.
Currently, when the data is created, there is a table for the household that shares information such as username, password, address, location, etc...
There is a separate table that stores individual information about the husband and wife in separate rows including birth dates and income.
The app has a search function where a user can search using criteria such as location, husband age range and income range and wife age range and income range and should return individual household results.
For instance, a user can search for clients that are located within 20 miles where the husband is between 50 and 60 years old and the wife is between 40 and 50 years old with an income range of $30,000 to $40,000.
The result would produce all results for singles and couples.
Here is just an idea of what the tables and results may look like. Keep in mind that the location data would actually use lat and long but for the purpose of this example, we are just using actual miles to keep it simple.
Table Users:
ID | Username | Location | Password | Email | Status
-------------------------------------------
1 | singleclient | 5 miles | 24##$#dls | user1#email.com | Single
2 | marriedclient | 7 miles | $#$sls33 | user2#email.com | Married
Table UserDetails
ID | User_ID | Gender | Name | Age | Income
----------------------------------
1 | 1 | Male | John Smith | 55 | 32000
2 | 2 | Male | Mike Jones | 53 | 37000
3 | 2 | Female | Cindy Jones | 47 | 31000
Result:
ID | Username | Distance | Status | Male Name | Female Name | Male Age | Female Age | Male Income | Female Income
----------------------------------------
1 | singleclient | 5 miles | Single | John Smith | null | 55 | null | 32000 | null
2 | marriedclient | 7 miles | Married | Mike Jones | Cindy Jones | 53 | 47 | 37000 | 31000
First, in many countries, the assumption that a marital unit consists of a single male and a single female is not true. I would try to avoid building this assumption into the data model or application.
I think you can approach this question using aggregation with a having clause:
select ud.user_id
from UserDetails ud
group by ud.user_id
having sum(case when ud.gender = 'Male' and ud.age between 50 and 60) = 1 and
sum(case when ud.gender = 'Female' and ud.age between 40 and 50 and ud.income between 30000 and 40000) = 1;
This gives you the user_ids that match. You can then format it however you like.
The above is quite generic. You might find that this version works faster:
select ud1.*, ud2.*
from UserDetails ud1 join
UserDetails ud2
on ud1.user_id = ud2.user_id
where ud1.gender = 'Male' and ud1.age between 50 and 60 and
ud2.gender = 'Female' and ud2.age between 40 and 50 and ud2.income between 30000 and 40000;
Which is faster depends on the size of your data and how indexes are set up.
You can join the same table twice under different names, and use this to populate all the fields.
You could do this by selecting the male person and the female person, but this will obviously get you in trouble when you need to deal with parties where both members are of the same sex. It might be better then to just pick the lowest ID and the highest ID in the database, or the youngest and oldest person, or whatever.
Query would look something like this (untested)
SELECT u.id, u.username, u.distance, u.status, p1.name, p2.name, p1.age, p2.age, p1.income, p2.income
FROM Users u
INNER JOIN UserDetails p1 ON u.id = p1.user_id AND p1.id = (SELECT MIN(id) FROM UserDetails WHERE user_id = u.id)
RIGHT JOIN UserDetails p2 ON u.status == 'married' AND u.id = p2.user_id AND p2.id = (SELECT MAX(id) FROM UserDetails WHERE user_id = u.id)
Adding in the "status == married" in the second (right) join will make sure that the second query will not show the same person twice, but will instead just return a row of nulls.
You'll probably need to do the query twice (so that each person can be searched as p1 and as p2) if one of the two spouses should be "50-60" and the other "40-50" or one should make $10.000 and the other $20.000, because you don't know in which order they'll come out.
I want to make a search on one table that returns a value to be used in search on different table.
I have this code, which looks for a team code in the club table:
SELECT Team, Teamcode
FROM epl.club
WHERE Teamcode =
(SELECT Teamcode
FROM epl.club
WHERE Team='Manchester City');
Now I want to use the resulting Teamcode for a select on the matches table.
I have this code that searches the matches table and finds all the matches with a given team code but I need it to get the code from the first search above.
Select *
from epl.matches
where HomeTeam = 35
or AwayTeam = 35
and FTR like "A"
or FTR like "H";
Another thing I don't understand is that I want to make that it would just return the line only if HomeTeam=35'= and '=FTR is A or FTR is H or if AwayTeam=35 and FTR is A or FTR is H, but what the code does is that it returns all the lines even if they contain the 35 but only contain the H or A in the FTR column.
You have to use parentheses in your boolean expression:
SELECT *
FROM epl.matches
WHERE (HomeTeam = 35 or AwayTeam = 35)
AND (FTR like "A" or FTR like "H")
This is because AND has a higher operator precedence as OR.
You can combine the queries with a join:
SELECT Team, Teamcode FROM epl.club c
INNER JOIN epl.matches m ON (m.HomeTeam = c.Teamcode or m.AwayTeam = c.Teamcode)
WHERE (c.Team = 'Manchester City')
AND (m.FTR like "A" or m.FTR like "H")
Additional info:
Here is a very simple explanation how a 'INNER JOIN' can be understood - just if you don't know this already. If you have two tables:
{ Table: Club }----------------| { Table: Matches }----------|
| | | |
| Teamcode | Team | | HomeTeam | AwayTeam | FTR |
|----------+-------------------| |----------+----------+-----|
| 35 | Manchester City | | 38 | 39 | A |
| 38 | Arsenal London | | 38 | 35 | A |
| 39 | Leeds United | | 35 | 39 | H |
|----------+-------------------| | 38 | 35 | A |
| 39 | 38 | H |
|----------+----------+-----|
an INNER JOIN between the tables club and matches means that of all row combinations of the two tables only the rows are included in the result if the join condition m.HomeTeam = c.Teamcode or m.AwayTeam = c.Teamcode is met. If you restrict club.Team to 'Manchester City' you would have the following result for the join:
{ Table: Join Result }------|
| |
| HomeTeam | AwayTeam | FTR |
|----------+----------+-----|
| 38 | 35 | A |
| 35 | 39 | H |
| 38 | 35 | A |
|----------+----------+-----|
It takes some time to get used to declarative style of the join syntax but it helps you to structure your queries (opposed to multiple FROM tables and nested SELECT subqueries). Furthermore, the SQL query optimizer can handle an INNER JOIN better then nested subqueries in most cases.
First query could just be:
SELECT
Team,
Teamcode
FROM epl.club
WHERE Team='Manchester City';
Why a subquery on the same table when you can access directly the Team field?
Then you can do:
SELECT *
FROM epl.matches
WHERE HomeTeam = (SELECT Teamcode FROM epl.club WHERE Team='Manchester City')
OR AwayTeam = (SELECT Teamcode FROM epl.club WHERE Team='Manchester City')
AND FTR like "A"
OR FTR like "H";
I have a SQL database with a table called staff, having following columns:
workerID (Prim.key), name, department, salary
I am supposed to find the workers with the highest salary per department and used the following statement:
select staff.workerID, staff.name, staff.department, max(staff.salary) AS biggest
from staff
group by staff.department
I get one worker shown from each department, but they are NOT the workers with the highest salary, BUT the biggest salary value is shown, even though the worker does not get that salary.
The person shown is the worker with the "lowest" workerID per department.
So, there is some sorting going on using the primary key, even though it is not mentioned in the group by statement.
Can someone explain, what is going on and maybe how to sort correctly.
Explanation for what is going on:
You are performing a GROUP BY on staff.department, however your SELECT list contains 2 non-grouping columns staff.workerID, staff.name. In standard sql this is a syntax error, however MySql allows it so the query writers have to make sure that they handle such situations themselves.
Reference: http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
In standard SQL, a query that includes a GROUP BY clause cannot refer to nonaggregated columns in the select list that are not named in the GROUP BY clause.
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause.
The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate.
Starting with MySQL 5.1 the non-standard feature can be disabled by setting the ONLY_FULL_GROUP_BY flag in sql_mode: http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_only_full_group_by
How to fix:
select staff.workerID, staff.name, staff.department, staff.salary
from staff
join (
select staff.department, max(staff.salary) AS biggest
from staff
group by staff.department
) t
on t.department = staff.department and t.biggest = staff.salary
In the inner query, fetch department and its highest salary using GROUP BY. Then in the outer query join those results with the main table which would give you the desired results.
This is the usual case group by with a aggregate function does not guarantee proper row corresponding to the aggregate function. Now there are many ways to do it and the usual practice is a sub-query and join. But if the table is big then performance wise it kills, so the other approach is to use left join
So lets say we have the table
+----------+------+-------------+--------+
| workerid | name | department | salary |
+----------+------+-------------+--------+
| 1 | abc | computer | 400 |
| 2 | cdf | electronics | 200 |
| 3 | gfd | computer | 400 |
| 4 | wer | physics | 300 |
| 5 | hgt | computer | 700 |
| 6 | juy | electronics | 100 |
| 7 | wer | physics | 400 |
| 8 | qwe | computer | 200 |
| 9 | iop | electronics | 800 |
| 10 | kli | physics | 800 |
| 11 | qsq | computer | 600 |
| 12 | asd | electronics | 300 |
+----------+------+-------------+--------+
SO we can get the data as
select st.* from staff st
left join staff st1 on st1.department = st.department
and st.salary < st1.salary
where
st1.workerid is null
The above will give you as
+----------+------+-------------+--------+
| workerid | name | department | salary |
+----------+------+-------------+--------+
| 5 | hgt | computer | 700 |
| 9 | iop | electronics | 800 |
| 10 | kli | physics | 800 |
+----------+------+-------------+--------+
My favorite solution to this problem uses LEFT JOIN:
SELECT m.workerID, m.name, m.department, m.salary
FROM staff m # 'm' from 'maximum'
LEFT JOIN staff o # 'o' from 'other'
ON m.department = o.department # match rows by department
AND m.salary < o.salary # match each row in `m` with the rows from `o` having bigger salary
WHERE o.salary IS NULL # no bigger salary exists in `o`, i.e. `m`.`salary` is the maximum of its dept.
;
This query selects all the workers that have the biggest salary from their department; i.e. if two or more workers have the same salary and it is the bigger in their department then all these workers are selected.
Try this:
SELECT s.workerID, s.name, s.department, s.salary
FROM staff s
INNER JOIN (SELECT s.department, MAX(s.salary) AS biggest
FROM staff s GROUP BY s.department
) AS B ON s.department = B.department AND s.salary = B.biggest;
OR
SELECT s.workerID, s.name, s.department, s.salary
FROM (SELECT s.workerID, s.name, s.department, s.salary
FROM staff s
ORDER BY s.department, s.salary DESC
) AS s
GROUP BY s.department;
I have 2 tables in MySQL, in both of these tables I have merchant_id, merchant, branch and some another fields, the name of one table is merchant and another table is product.
tbl_merchant :
id | merchant_id | merchant_name | branch | ...
------+---------------+--------------------+----------------+
1 | 1001 | McDonalds | branch 1 mcd | ...
2 | 2002 | KFC | branch 1 kfc | ...
tbl_product :
id | product_id | product_name | price | merchant_id
------+---------------+-----------------+---------+-------------
1 | 100101 | Chicken | 10 | 1001
2 | 100102 | Potato | 5 | 1001
3 | 100101 | Burger | 10 | 2002
4 | 100102 | Fish Fillet | 10 | 2002
I want to know how can to show merchant_name, branch from both tables using SQL WHERE Clause by product_id = 100101 and merchant_id = 1001 ?
Like this :
Result :
id | merchant | branch | product_name | price
------+-------------+----------------+---------------+-------
1 | McDonalds | branch 1 mcd | Chicken | 10
Thank You
First, I'll show you the query, then I'll explain each part line by line to help you understand:
SELECT
merchant_name, branch
FROM
tbl_merchant INNER JOIN tbl_product ON (tbl_product.merchant_id = tbl_merchant.merchant_id)
WHERE
product_id = 100101 AND merchant_id = 1001
Alright, so if we look at the first part following the select, it should be clear that the two columns that will be printed out are merchant_name and branch. Based on your output, you can print out any field from either table just by adding its name to the list. If the field has the same name in both tables, then you need to qualify it like this:
SELECT
tbl_merchant.id, tbl_product.id
FROM
tbl_merchant INNER JOIN tbl_product USING(merchant_id)
The tricky part of this query is the line that joins the two tables together. Essentially what you have as of now is two tables that are linked together by a merchant id, which makes sense because 1 merchant can have many products (i.e. a 1 to many relationship). I'm assuming that the merchant ID is unique. The join then pairs together all the rows that have the same merchant_id (which is unique in one of the tables and therefore guaranteed to be correct). More specially you can think of it as a qualified cross product where each tuple from tbl_product is joined with each tuple from tbl_merchant and then qualified based on the condition tbl_product.merchant_id = tbl_merchant.merchant_id.
The last part of the query (WHERE clause) simply eliminates rows based on the conditions provided.
The query for this is:
SELECT merchant_name, branch
FROM
tbl_merchant
INNER JOIN
tbl_product
ON (tbl_product.merchant_id = tbl_merchant.merchant_id)
WHERE
product_id = 100101 AND merchant_id = 1001
SELECT merchants.id, merchants.merchant_id, merchants.branch, products.product_name, products.price
FROM merchants
INNER JOIN products ON products.merchant_id = merchants.merchant_id
WHERE merchants.merchant_id = 1001 AND products.product_id = 100101
you can use JOIN to solve this type of query
there are some good article to learn more about JOIN with visula explanation::
1) http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/ "A Visual Explanation of SQL Joins"
2) http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins "Visual Representation of SQL Joins"
Try this:
select * from marchant join product on marchant.id=product.merchant_id where merchant_id = 1001
This statement will join both tables together where the primary key form merchant is equals the merchant_id in product.
The case:
I have 2 tables, 'contracts' and 'members' tied with contract.id = members.cid.
Each contract has one main member and several secondary members (usually the children and spouse of the main member). The main member's details (name, address etc) are stored in table contracts whereas, extra members details are kept in table members. (bad logic, i know but this was a mess to begin with and i need time to redesign the db and website)
The desired output:
When I run a batch print of all contracts (lets say, every Friday) I need to also print a copy of the contract for each member, too but with the member's details on the contract instead of the main member.
The question:
How does this translate into a mysql query? Ok, its a left join, but how do I say "print data from table members instead of contracts for the joined rows"?
Main fields that occur in the 2 tables are name + surname, those should be enough for a draft query example.
Example tables and data:
contracts
-------------------------
id | name | surname |
-------------------------
1 | Tom | Jones |
2 | Jamie | Oliver |
members
--------------------------------
id | cid | name | surname |
--------------------------------
1 | 1 | Jack | Jones |
2 | 1 | Anne | Jones |
3 | 2 | Cathy | Wilson |
So the results I want shoudld be:
cid | name | surname |
--------------------------
1 | Tom | Jones |
1 | Jack | Jones |
1 | Anne | Jones |
2 | Jamie | Oliver |
2 | Cathy | Wilson |
If i write
SELECT c.name as name, c.surname as surname, m.name as name, m.surname as surname
FROM contracts c
join members m on c.id = m.cid
I simply end up with
name and name_1, surname and surname_1 but I want ALL names to fall under name and likewise for all other matching columns.
Hope this works :::
select c1.id, c1.name, c1.surname
from contracts c1
union
(Select m.id, m.name, m.surname
from
members m left join contracts c on (c.id = m.cid))
This is what I finally did and it worked (field names are different but the syntax is what matters):
SELECT u.id, u.onoma_u, u.name_u,
coalesce(u.programa, aa.programa) as programa,
coalesce(u.barcode, aa.barcode) as barcode,
coalesce(u.plan, aa.plan) as plan,
coalesce(u.im_exp, aa.im_exp) as im_exp,
coalesce(u.symb, aa.symb) as symb
FROM (SELECT a1.id, a1.onoma_u, a1.name_u, a1.programa, a1.barcode, a1.plan, a1.im_exp, a1.symb
FROM aitisi a1
UNION
SELECT a2.id, m.name, m.surname, NULL, NULL, NULL, NULL, NULL
FROM members m
JOIN aitisi a2 ON a2.id = m.symbid) u
JOIN aitisi aa ON aa.id = u.id;
I used aliases and NULLS as dummy fields to fill in the blanks.