I have two MySQL Tables. In this abstract example I will use these to explain my issue:
Persons(person_id, name);
Competition(competition_id, first, second, third);
First, second and third refer to person_id and I want to get the names in this order. If I use
SELECT name
FROM Persons
, Competition
WHERE person_id = first
OR person_id second
OR person_id = third;
the names are sorted by their table order (usually the same order like sorted by primary key). How can I order them right?
Edit:
I need to enter a competition_id and want to get a table with three name lines in the right order
I think you just need multiple joins:
SELECT c.*, p1.name, p2.name, p3.name
FROM Competition c LEFT JOIN
Persons p1
ON p1.person_id = c.first LEFT JOIN
Persons p2
ON p2.person_id = c.second LEFT JOIN
Persons p3
ON p3.person_id = c.third;
This puts the names for a given row in competition in the same row. That seems like a sensible thing to do.
EDIT:
If you want the first matching name, you can use COALESCE():
SELECT c.*, COALESCE(p1.name, p2.name, p3.name)
The solution is, a seperate table is required.
Persons(person_id, name);
Mapper(pk_mapper_id, fk_competition_id, fk_person_id, rang)
Competition(competition_id);
Here I can order by rang.
Related
In a scenario where I have multiple people and every person can have many hobbies, how can I make it so I select the people that have a specified hobby among them but also all other hobbies. If I use a where clauses the query only returns the hobby specified without the others
Like, From all the people show me the people that like to go phishing and also show my all their other hobbies.
I can't explain it to well, I've made a JsFiddle
How can I make it so I can see Mike's all other hobbies but make sure one of them is Fighting ?
SELECT name, GROUP_CONCAT(HobbyName)
FROM people
INNER JOIN peoples_hobbies ON pId = peopleId
INNER JOIN hobby ON hobbyId = hId
WHERE HobbyName = 'Fighting'
GROUP BY name
/* if i do this the where clasue makes it so it only selects that hobby, I want to get,
the hobby that i specify and also all the rest that belong to that person ?*/
Starting from your existing query, you can just add a having clause to filter on persons that have one specific hobby:
SELECT p.name, GROUP_CONCAT(h.HobbyName) hobbies
FROM people p
INNER JOIN peoples_hobbies ph ON p.pId = ph.peopleId
INNER JOIN hobby h ON ph.hobbyId = h.Id
GROUP BY p.pId, p.name
HAVING MAX(h.HobbyName = 'Fighting')
Side notes:
in a multi-table query, always qualify each column with the table it belongs to - this make the query much easier to follow, and avoids ambiguity when two different table have a column that has the same name
HAVING MAX(h.HobbyName = 'Fighting') is a MySQL shortcut to say HAVING MAX(CASE WHEN h.HobbyName = 'Fighting' THEN 1 ELSE 0 END) = 1
adding the primary key of the people table to group by clause is safer, as it would properly handle homonyms
I have two tables "Employees" and "Positions"
I want to write a query that show the name of the Position that appears least in table "Employees" I have foreign key "ID_of_Position" in "Employees".
I came up with something like this, but it might not work:
SELECT Employees.ID_of_Position, Positions.ID_of_Position, Positions.Name_of_Position
FROM Positions
WHERE Employees.ID_of_Position in (SELECT ID_of_Position
FROM Employees
GROUP BY ID_of_Position
HAVING COUNT(ID_of_Position
)=1)
INNER JOIN Employees ON Positions.ID_of_Position = Employees.ID_of_Position;
The proper syntax would look like:
SELECT e.ID_of_Position, p.ID_of_Position, p.Name_of_Position
FROM Positions p JOIN
Employees e
ON p.ID_of_Position = e.ID_of_Position
WHERE e.ID_of_Position in (SELECT ID_of_Position
FROM Employees
GROUP BY ID_of_Position
HAVING COUNT(ID_of_Position) = 1
);
This doesn't answer the question (unless there is a position with exactly one employee), but it is at least syntactically correct.
It is important to understand that SELECT, FROM, and WHERE are all clauses in the SELECT statement. JOIN is an operator in the FROM clause.
At least where should be after join, and there is something with parentheses at the end ...
SELECT Employees.ID_of_Position, Positions.ID_of_Position, Positions.Name_of_Position
FROM Positions
INNER JOIN Employees ON Positions.ID_of_Position = Employees.ID_of_Position;
WHERE Employees.ID_of_Position in (SELECT ID_of_Position
FROM Employees
GROUP BY ID_of_Position
HAVING COUNT(ID_of_Position)=1
)
Let's say I have the following query to list the average value of a house people own:
SELECT PERSON.NAME, AVG
FROM PERSON, (
SELECT HOUSE.PID AS PID, AVG(HOUSE.VALUE) as AVG
FROM HOUSE
GROUP BY PID
) HOUSES
WHERE PERSON.PID = HOUSES.PID OR PERSON.ID NOT IN (
SELECT PID
FROM HOUSE
)
The query does what I want it to do, except it doesn't include the people who don't have any houses, who should have "0" as their house cost average.
Is this possible, or am I way off?
Simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax. For instance, in this case, you want a LEFT JOIN, but cannot express it.
SELECT p.NAME, AVG_VALUE
FROM PERSON p LEFT JOIN
(SELECT PID , AVG(VALUE) as AVG_VALUE
FROM HOUSE
GROUP BY PID
) h
ON p.PID = h.PID;
If you want 0 instead of NULL, use COALESCE(AVG_VALUE, 0) as AVG_VALUE in the outer query.
You should use left join, that way records that appear in persons table and dont have corresponding records in houses table will grt nulls on the columns from houses
I have two table in MySQL
Table 1: List of ID's
--Just a single column list of ID's
Table 2: Groups
--Group Titles
--Members **
Now the member field is basically a comments field where all the ID's that are part of that group are listed. So for instance one whole field of members looks like this:
"ID003|ID004|ID005|ID006|ID007|ID008|... Etc."
There they can be up to 500+ listed in the field.
What I would like to do is to run a query and find out which ID's appear in only three or less groups.
I've been taking cracks at it, but honestly I'm totally lost. Any ideas?
Edit; I misunderstood the question the first time, so I'm changing my answer.
SELECT l.id
FROM List_of_ids AS l
JOIN Groups AS g ON CONCAT('|', g.members, '|') LIKE CONCAT('%|', l.id, '|%')
GROUP BY l.id
HAVING COUNT(*) <= 3
This is bound to perform very poorly, because it forces a table-scan of both tables. If you have 500 id's and 500 groups, it must run 250000 comparisons.
You should really consider if storing a symbol-separated list is the right way to do this. See my answer to Is storing a delimited list in a database column really that bad?
The proper way to design such a relationship is to create a third table that maps id's to groups:
CREATE TABLE GroupsIds (
memberid INT,
groupid INT,
PRIMARY KEY (memberid, groupid)
);
With this table, it would be much more efficient by using an index for the join:
SELECT l.id
FROM List_of_ids AS l
JOIN GroupsIds AS gi ON gi.memberid = l.id
GROUP BY l.id
HAVING COUNT(*) <= 3
select * from
(
select ID,
(
select count(*)
From Groups
where LOCATE(concat('ID', a.id, '|'), concat(Members, '|'))>0
) as groupcount
from ListIDTable as a
) as q
where groupcount <= 3
I have 2 tables authors and authors_sales
The table authors_sales is updated each hour so is huge.
What I need is to create a ranking, for that I need to join both tables (authors has all the author data while authors_sales has just sales numbers)
How can I create a final table with the ranking of authors ordering it by sales?
The common key is the: authorId
I tried with LEFT JOIN but I must be doing something wrong because I get all the authors_sales table, not just the last.
Any tip in the right direction much appreciated
If you're looking for aggregate data of the sales, you'd want to join the tables, group by the authorId. Something like...
select authors.author_id, SUM(author_sales.sale_amt) as total_sales
from authors
inner join author_sales on author_sales.author_id = authors.author_id
group by authors.author_id
order by total_sales desc
However (I couldn't distinguish from your question whether the above scenario or next is true), if you're only looking for the max value of the author_sales table (if the data in this table is already aggregated), you can join on a nested query for author_sales, such as...
select author.author_id, t.sales from authors
inner join
(select top 1 author_sales.author_id,
author_sales.sale_amt,
author_sales.some_identifier
from author_sales order by some_identifier desc) t
on t.author_id = author.author_id
order by t.sales desc
The some_identifier would be how you determine which record is the most recent for author_sales, whether it is a timestamp of when it was inserted or an incremental primary key, however it is set up. Depending on if the data in author_sales is aggregated already, one of these two should do it for you...
select a.*, sum(b.sales)
from authors as a
inner join authors_sales as b
using authorId
group by b.authorId
order by sum(b.sales) desc;
/* assuming column sales = total for each row in authors_sales */