Mysql query with pivot table and multiple joins [closed] - mysql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have a sales table, a sale_staff table, a staff table and an offices table.
We are selling properties, and I want to find out the numbers of sales per seller for X month and per office.
The pivot table looks like this
sale_id , staff_id , type
type can be either seller or lister, so I need a where clause for this.
The sales table has a FK to the offices table; office_id
What I have so far is this, its TOTALLY wrong I know, but that's why i'm here - i need to fix the sums and include the office name from the office table, so
select st.first_name, st.last_name, office, count(*) as sold
from sales s, sale_staff ss
left join staff st
on st.id = ss.staff_id
left join offices off
on off.id = s.office_id
where ss.`type` = 'lister' and
year(s.sale_date) = 2017 and
month(s.sale_date) = 12
group by st.id
Sales table is simply a property sale item, price, address, office_id.
Besides the error unknown column s.office_id, as I said, the sum value is incorrect anyway. I'm really not experienced enough to understand this level of relationship joins and aggregating, any pointers please.
Basically I would like to simply see a resultset like
staff(seller) , count , office
Mike , 12 , West
Jim , 7 , East
Fred , 3 , East
Edit: SQLFiddle in case that helps :) Will add some sample test data.

Never use commas in the FROM clause. Always use proper, explicit JOIN syntax. Your problem is because of the scoping rules around commas.
I would recommend:
select st.first_name, st.last_name, o.office, count(*) as sold
from staff st left join
sale_staff ss
on st.id = ss.staff_id join
sales sa
on sa.sale_id = ss.sale_id join
offices o
on o.id = s.office_id
where ss.`type` = 'lister' and
s.sale_date >= '2017-12-01' and
s.sale_date < '2018-01-01'
group by st.first_name, st.last_name, o.office;
I think this has the join condition correctly laid out, but it is hard to be sure without sample data and desired results.
Notes:
left join is probably not necessary. If it is, you should probably be starting with the staff table (to keep all staff).
Qualify all column names.
The group by includes all the non-aggregated columns in the from. This is a good habit if you are learning SQL.
The date comparisons are direct, without the use of functions.

Related

Why is my average query showing incorrect values? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I asked a question a few hours ago that was marked as closed and referred me a link that did not clear up my confusion.
I am trying to query a sports database in MySQL to list the names of players who are above average age compared to their teammates. Ideally, I want to group by team, find the average of each team, and compare that to each respective player on that team.
My results from this query seem to be comparing players to the entire databases' average, rather than the average of the team. Can anyone correct my query or propose an alternate query to get the correct data? A friend of mine suggested perhaps using two copies of the tables, but that is beyond the scope of my limited MySQL skills.
My relational schema are as follows:
player(player_name, age, position)
plays_for (player_name, team_name)
SELECT player.player_name, player.age
FROM
plays_for
INNER JOIN player ON player.player_name=plays_for.player_name
WHERE (SELECT AVG(age) FROM player
GROUP BY plays_for.team_name1)< player.age
Your WHERE statement does not include the team grouping. I personally like WITH statements which seems to be the direction your friend was going.
> WITH average_ages AS ( SELECT AVG(p.age) AS average_age, pf.team_name
> FROM player p join plays_for pf on p.player_name = pf.player_name
> GROUP BY pf.team_name) aa
> SELECT player.player_name, player.age
> FROM plays_for
> INNER JOIN player ON player.player_name=plays_for.player_name
> INNER JOIN average_ages ON plays_for.team_name = average_ages.team_name
> WHERE player.age > average_ages.average_age;
The WITH statement at the top creates a temporary table of average ages and then joins it to the plays_for table.
The first few rows of the entire SELECT query before the WHERE statement would look like this
Name Age Team Average_age
Tara 51 KOs 25
Bomb 45 KOs 25
Jess 20 BES 30
Buster 40 BES 30

MySQL summing totals by state by year

I have been playing around with this for what seems like hours and I can't get the results I want. Here is the query I am having trouble with:
SELECT year.year, dstate,
(SELECT sum(amount) FROM gift
WHERE year.year = gift.year
AND gift.donorno = donor.donorno)
FROM donor, gift, year
WHERE year.year = gift.year
AND gift.donorno = donor.donorno;
This seems redundant. Anyway, I am trying display the total donations (gift.amount) for each state by year.
ex.
1999 GA 500 (donorno 1 from GA donated 200 and donorno 2 from GA donated 300)
1999 FL 400
2000 GA 600
2000 FL 500
...
To clarify donors can be from the same state but I am trying to total the gift amounts for that state for the year it is donated.
Any advice is appreciated. I feel like the answer is right in front of me.
Here is a picture of tables for reference:
This is a very simple join & aggregation problem.
SELECT y.year, d.state, SUM(g.amount) AS total
FROM gift AS g
INNER JOIN year AS y ON y.year=g.year
INNER JOIN donor AS d ON d.donorno=g.donorno
GROUP BY y.year, d.state
You don't need the sub-query in your SELECT clause in order to get the total amount. You can sum it by grouping. (I think the GROUP BY clause is what you're missing. I recommend reading up on it.) What you've done is called a correlated sub-query and it is going to be very slow over large data sets because it has to be calculated row-by-row instead of as a set operation.
Also, please don't use the old style comma join syntax. Instead use the explicit join syntax as shown above. It is much clearer and will help avoid accidental Cartesian products.

MySQL dynamic order

In general, all I need to do is to order a MySQL table.
But, it has to be a "smart" order and I would like to hear your opinions.
There is a table of customers
id, name, email, phone, country, language, registration_time
There is another table which holds the skills of sales managers as numeric values -
sales_manager_id, skill_type, skill_name, skill_value
7, language , English , 5
Which means that manager number 7 speaks English on level 5.
Every sales manager can have multiple skills.
Now, I want to order the customers table by country, language and registration_time (in this exact order) for a specific sales manager in such a way that the top rows will be from a country in which this sales manager has highest skills, after this by language in which he has the highest skills and after this by registration time.
Do you have any suggestions? The biggest problem is that this query should be simple and readable as much as possible because there would be modifications in the future and I don't want to deal with enormous queries.
I assume I understand your problem. If not please correct me, else here is my solution.
I did a few changes to your tabels in order to test the query and you can test my solution like I did here: SQL Fiddle
(1) You need to JOIN your customers table two times with the skills table - first for the country skill and second for the language skill of a sales manager:
select *
from customers
join skills as country_skills on country_skills.skill_name = customers.country
join skills as language_skills on language_skills.skill_name = customers.language
(2) You need to restrict your results for just the sales manager you want, e.g. sales manager with id = 11:
where country_skills.sales_manager_id = 11
and language_skills.sales_manager_id = 11
(3) The 'dynamic' order you want:
order by country_skills.skill_value desc, language_skills.skill_value desc, regTime desc
This would be my complete query:
select *
from customers
join skills as country_skills on country_skills.skill_name = customers.country
join skills as language_skills on language_skills.skill_name = customers.language
where country_skills.sales_manager_id = 11
and language_skills.sales_manager_id = 11
order by country_skills.skill_value desc, language_skills.skill_value desc, regTime desc
So you got all customers sorted by country skills of a specific sales manager > language skills of a specifiy sales manager > regTime of customer.
This query ignores customers with a country or language the sales manager got no skill... you can avoid that with a LEFT JOIN

Nobel prize winner database [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I have the Nobel prize winners database since the year it was first awarded. The source of the data is : http://www.aggdata.com/awards/nobel_prize_winners
It has one single table, so there is no foreign key nor any ER.
The table has following 12 columns: Id, Year, Category, Name, Birthdate, Birth Place, County, Residence, Role/Affiliate, Field/Language, Prize Name, Motivation.
Obviously there are multiple winners for categories such as physics, medicine etc.
I have been trying to see the number of Nobel prize winners per category per decade using query. I want my result table to look like:
______________________________________________
1960-1969 Peace 9 winners
1960-1969 Physics 19 winners
..............................................
1970-1979 Peace 6 winners
1960-1969 Physics 12 winners
..............................................
______________________________________________
Can this be possible through a query in MySQL?
Much appreciate your attention. Thank you all/
Here is a more SQL way to do this:
select concat(floor(npw.year / 10)*10, '-', floor(npw.year / 10)*10+9) as decade,
npw.category,
count(*) as winners
from NobelPriceWinners npw
group by floor(npw.year / 10), npw.category
order by decade, category;
This produces three columns as:
Decade Category Winners
1960-1969 Peace 9
1960-1969 Physics 19
. . .
Here's some ideas:
Assuming you want to represent the decade as xxx0-xxx9, and assuming the award_date is DATE datatype, you can use an expression to get first three characters of year:
SUBSTR(DATE_FORMAT(t.award_date,'%Y'),1,3)
A query something like this may get you started.
SELECT CONCAT(s.ccy,'0-',s.ccy,'9') AS decade
, s.category
, CONCAT(s.count_winners,' winners') AS winners
FROM (
SELECT SUBSTR(DATE_FORMAT(t.award_date,'%Y'),1,3) AS ccy
, t.category
, COUNT(1) AS count_winners
FROM mytable t
WHERE t.award = 'winner'
GROUP
BY SUBSTR(DATE_FORMAT(t.award_date,'%Y'),1,3)
, t.category
) s
The inline view aliased as s does the heavy lifting to get the result, the outer query does some formatting.
Note that this query does not return any "zero" counts.
If you also need to include "zero" counts, for all categories for all decades, that could be done as well, by introducing a row sources for "all category" and "all decades", and then doing a cross join between those and a left join to the inline view aliased as s.
The query above does not return include "0" count

MySQL relational database query, correct terminology?

I think my issue with databases stems from not knowing the correct terminology to help me find an answer myself so I'll explain a generic version of what I'm doing and hopefully you can point some tutorials my way or give me some terms to check into.
Let's use an example of an employee directory.
Each employee can have multiple locations, multiple job duties which pull from a separate table. Example tables & some data, let's just focus on the multiple locations.
employees
Main employee data
- id (ex: 400)
- first (ex: John)
- last (ex: Doe)
locations
Unique list of locations
- id (ex: 3000)
- title (ex: FakeCo, LLC)
map_employees_locations
Tie ANY number of locations to an employee
- id
- employee_id (ex: 400)
- location_id (ex: 3000)
I'm struggling with the logic of how a single query would return something like this:
John
Doe
FakeCo, LLC
AnotherCo, LLC
It seems I would have to run a query to get the employee data, then within a nested query, grab locations associated with the employee id, etc... If there was only one location per employee, it would be a simple join, I just don't know how to handle the multiples.
Let me know if I'm way off, I'm just struggling.
You would join all of the tables together like this
select e.id,e.first,e.last,l.id,l.title
from employees e
inner join map_employees_locations el
on el.employee_id = e.id
inner join locations l
on el.location_id = l.id
where e.first = 'John'
AND e.last = 'Doe'
This would return data like this:
e.id e.first e.last l.id l.title
------------------------------------------------
1 John Doe 1 FakeCo, LLC
1 John Doe 2 AnotherCo, LLC
If you want only one line per employee you should maybe use group concat
select id, e.last, e.first
group_concat(l.title separator ',' ) as locations
from employee e
join location l on l.employee_id = e.id
group by e.id
Not sure about the syntax cos i'm more aware of postgres but this should do the job.