Soccer SQL Query Home- and Roadteam Issue - mysql

In a soccer environment I want to display the current standings. Meaning: points and goals per team. The relevant tables look similar to the following (simplified).
Match Table
uid (PK) hometeamid roadteamid
------------------------------------------------------------------
Result Table
uid (PK) hometeamscore roadteamscore resulttype (45min, 90min, ..)
-------------------------------------------------------------------
Team Table
uid (PK) name shortname icon
------------------------------------------------------------------
Now I don't get my head around it, how to write the standings in one query. What I managed was to write a query, which returns the "homegames"-standings only. I guess that's the easy part. Anyway here is how it looks:
SELECT ht.name,
Count(*) As matches,
SUM(res.hometeamscore) AS goals,
SUM(res.roadteamscore) AS opponentgoals,
SUM(res.hometeamscore - res.roadteamscore) AS goalDifference,
SUM(res.hometeamscore > res.roadteamscore) * 3 + SUM(res.hometeamscore = res.roadteamscore) As Points
FROM league_league l
JOIN league_gameday gd
ON gd.leagueid = l.uid
JOIN league_match m
ON m.gamedayid = gd.uid
JOIN league_result res
ON res.matchid = m.uid
AND res.resulttype = 2
JOIN league_team ht
ON m.hometeamid = ht.uid
Where l.uid = 1
Group By ht.uid
Order By points DESC, goalDifference DESC
Any idea how to modify this, that it will return home- and roadgames would be big time appreciated.
Many thanks,
Robin

Create views. If your data does not change often and you need performance, create one or more pre-computed tables.
Views in MySQL are juste pseudo-tables that are dynamically computed from a SELECT query. Using the SQL in your question, you can create a view of the teams results at home: CREATE VIEW homegames AS SELECT ...
Then do the same for road games. Then it will be easy to synthesize both views in a third one (you just need to sum up the columns).
Views have at least one flaw: they are slow. A view built on views is like using complex subqueries, and MySQL is quite bad at this. I don't think it's a problem for you as you're probably dealing with hundreds of games at most. But if you find these views to be too slow to query, and provided you don't use any kind of cache that could mitigate this, then use simple tables instead of views. Of course, you'll need to keep them in sync. You can TRUNCATE and INSERT INTO homegames SELECT ... each time you have a new game, or you can be smarter and just UPDATE the tables. Both are right, depending on your needs.

Could you not abstract this out into a stored procedure or stored function to call rather than constructing such a big-ass complicated query?

Related

MySQL: Taxonomy - Get ID that belongs to multiple categories

I was hoping someone could help me come up with a query for what I'm looking to do.
I have a website that lists game servers and I'm trying to improve my search system a bit.
There's three tables of interest; servers, version_taxonomy and category_taxonomy. The taxonomy tables contain two columns, one for a server ID and one for a version/category ID, where associations between a server and it's supported versions and categories can be made.
Up till now, I've been joining both taxonomy tables to the server table and be looking up servers for one version and one category, it's been working fine. However I'm looking to allow the search of a server that has multiple categories at the same time.
I've made an image to try and illustrate what I'm looking to do:
Say I'm looking for a server that has both categories 5 and 12 - Based on the table on the left that would be servers 1 and 3. But how would that be in a query? And how would I use that query to later get and work with the rest of the server data (JOIN like I'd normally do?)
Hopefully that makes sense! Looking forward to your responses.
Assuming I understand the question:
Join the two tables then count the distinct values of category ID while limiting by them. Distinct is not be needed if you can guarantee the uniqueness of serverID, categoryID from table A and a 1:1 relationship to server taxonomy which would be true if you always limit by 1 and only 1 version...
SELECT A.ServerID, count(A.CategoryID) CatCnt
FROM A
INNER JOIN B
on A.ServerID = B.ServerID
WHERE A.CATEGORYID in (5,12)
and B.Version= 1.16
GROUP BY A.ServerID
HAVING count(distinct A.CategoryID) = 2
The category ID could be parameter passed in as well as the count distinct as you know both values.
This could be used as a CTE or as a inline derived table as a source then join in to get the addiontal data; or left join in the desired data assuming it's a 1:1 relationship.
If you want a working example: post DDL for table and SQL to create sample data and I'll put something in https://rextester.com/.

Learning to use Advanced features of SQL

I'm at the point where i need to learn to use more advanced features of SQL if i would like to advance my career. I confess i don't know what a join is or how to use them. i start reading queries with joins and my brain just turns to mush, but i have encountered a situation where i might benefit from using a join, but i don't know if what i want to do is even possible.
consider the following tables:
user
id | user_name | location_id
===============================
1 | r3wt | 316
location
id | state | city
=========================
316 | Clarksville | Arkansas
Now when i select the user, i get back an array like
[
id => 1,
user_name => r3wt ,
location_id =>316
]
What i would like to get back is:
[
id => 1,
user_name => r3wt ,
location_id =>[
id=>316,
city=>Clarksville,
state=>Arkansas
]
]
I'm wondering if this is possible, and if so how i might alter my quite bland select query to make this possible. Thanks, and if you need more details or want me to make what will be a pathetic attempt at figuring it out myself, i am willing to embarrass myself to learn. thank you
My pathetic little attempt which of course doesnt work:
SELECT id, user_name, location AS location_id
FROM user JOIN location ON location.id = user.location_id
Keep in mind i have absolutely no idea what i'm doing. i don't even understand what joins are and how they work, but i understand what i would like to be able to do with SQL.
You should:
SELECT u.id, u.name, u.location_id, l.city, l.state
FROM user u -- identify columns from user table with 'u.'
LEFT OUTER JOIN location l -- identify columns from location with 'l.'
ON u.location_id = l.id --- the join predicate when the two ids match
WHERE l.state = 'Arkansas' -- other selection criteria
This will give you almost what you wanted but as a single flat array:-
[
u.id => 1,
u.user_name => r3wt ,
u.location_id => 316,
l.city=>Clarksville,
l.state=>Arkansas
]
SQL results are always a two dimensional table. This is fundamental to the way SQL works! The "LEFT OUTER" means you want null values for city and state in the result if no location is present.
Just google "MYSQL TUTORIAL" and you will find dozens of sites offering on-line guides for free. Cannot recommend a particular one as I learnt all this stuff in pre-history.
Joins are used to combine 2 or more tables to get results from them all. There are several types of Joins and you can read about them here: http://www.w3schools.com/sql/sql_join.asp
It is important to remember that MySql return rows so in your example if for the discussion a user can have several locations you will not be using join - or if you do use joins you will have several rows of the user id and the different locations he have.
In your example, for a user that have one row of location you can run this query
SELECT *
FROM user
INNER JOIN location
ON user.location_id=location.id;
This query will return you rows for all users that have a location record and you will get all the fields of both tables (as we used *).

MySQL Query - Lowest Values with Multiple Tables / Joins

i am requesting some help for a query to be used on a custom golf website.
what i need is to find the lowest score per player per course. my club has 3 nine hole loops, 27 holes in total, but i want to find the lowest per 9 holes (i.e. course as i am describing it).
i have the following database structure (note, i haven’t put in all rows, only those that are pertinent to the query i am stuggling with).
Golf DB ERP Diagram
a query to get the full set of data would be (note some field names are different - the diagram was trying to better descriptive…):
select * from round r, round_hole rh, player p, course_nine c, course_hole ch
where r.r_id = rh.rh_rid
and p.id = r.r_pid
and c.cn_nine = r.r_nine
and ch.ch_nine = c.cn_nine
and rh.rh_hid = ch.ch_no
a snapshot of the results are:
Full query ouput
however, i then need to filter it as above, into "per player, per course”
i am presuming this is some subquery, join, temp table or “in” type statement, but struggling, particularly as it spans multiple tables.
any help is appreciated
This can be accomplished using some simple aggregation. As long as you are able to properly join all of your tables, you can do this:
SELECT player, course, MIN(score) AS lowestScore
FROM myTables
GROUP BY player, course;

Efficiency of Query to Select Records based on Related Records in Composite Table

Setup
I am creating an event listing where users can narrow down results by several filters. Rather than having a table for each filter (i.e. event_category, event_price) I have the following database structure (to make it easy/flexible to add more filters later):
event
event_id title description [etc...]
-------------------------------------------
fllter
filter_id name slug
-----------------------------
1 Category category
2 Price price
filter_item
filter_item_id filter_id name slug
------------------------------------------------
1 1 Music music
2 1 Restaurant restaurant
3 2 High high
4 2 Low low
event_filter_item
event_id filter_item_id
--------------------------
1 1
1 4
2 1
2 3
Goal
I want to query the database and apply the filters that users specify. For example, if a user searches for events in 'Music' (category) priced 'Low' (price) then only one event will show (with event_id = 1).
The URL would look something like:
www.site.com/events?category=music&price=low
So I need to query the database with the filter 'slugs' I receive from the URL.
This is the query I have written to make this work:
SELECT ev.* FROM event ev
WHERE
EXISTS (SELECT * FROM event_filter_item efi
JOIN filter_item fi on fi.filter_item_id = efi.filter_item_id
JOIN filter f on f.filter_id = fi.filter_id
WHERE efi.event_id = ev.event_id AND f.slug = 'category' AND fi.slug ='music')
AND EXISTS (SELECT * FROM event_filter_item efi
JOIN filter_item fi on fi.filter_item_id = efi.filter_item_id
JOIN filter f on f.filter_id = fi.filter_id
WHERE efi.event_id = ev.event_id AND f.slug = 'price' AND fi.slug = 'low')
This query is currently hardcoded but would be dynamically generated in PHP based on what filters and slugs are present in the URL.
And the big question...
Is this a reasonable way to go about this? Does anyone see a problem with having multiple EXISTS() with sub-queries, and those subqueries performing several joins? This query is extremely quick with only a couple records in the database, but what about when there are thousands or tens of thousands?
Any guidance is really appreciated!
Best,
Chris
While EXISTS is just a form of JOIN, MySQL query optimizer is notoriously "stupid" about executing it optimally. In your case, it will probably do a full table scan on the outer table, then execute the correlated subquery for each row, which is bound to scale badly. People often rewrite EXISTS as explicit JOIN for that reason. Or, just use a smarter DBMS.
In addition to that, consider using a composite PK for filter_item, where FK is at the leading edge - InnoDB tables are clustered and you'd want to group items belonging to the same filter physically close together.
BTW, tens of thousands is not a "large" number of rows - to truly test the scalability use tens of millions or more.

How do I make the rows of a lookup table into the columns of a query?

I have three tables: students, interests, and interest_lookup.
Students has the cols student_id and name.
Interests has the cols interest_id and interest_name.
Interest_lookup has the cols student_id and interest_id.
To find out what interests a student has I do
select interests.interest_name from `students`
inner join `interest_lookup`
on interest_lookup.student_id = students.student_id
inner join `interests`
on interests.interest_id = interest_lookup.interest_id
What I want to do is get a result set like
student_id | students.name | interest_a | interest_b | ...
where the column name 'interest_a' is a value in interests.name and
the interest_ columns are 0 or 1 such that the value is 1 when
there is a record in interest_lookup for the given
student_id and interest_id and 0 when there is not.
Each entry in the interests table must appear as a column name.
I can do this with subselects (which is super slow) or by making a bunch of joins, but both of these really require that I first select all the records from interests and write out a dynamic query.
You're doing an operation called a pivot. #Slider345 linked to (prior to editing his answer) another SO post about doing it in Microsoft SQL Server. Microsoft has its own special syntax to do this, but MySQL does not.
You can do something like this:
SELECT s.student_id, s.name,
SUM(i.name = 'a') AS interest_a,
SUM(i.name = 'b') AS interest_b,
SUM(i.name = 'c') AS interest_c
FROM students s
INNER JOIN interest_lookup l USING (student_id)
INNER JOIN interests i USING (interest_id)
GROUP BY s.student_id;
What you cannot do, in MySQL or Microsoft or anything else, is automatically populate columns so that the presence of data expands the number of columns.
Columns of an SQL query must be fixed and hard-coded at the time you prepare the query.
If you don't know the list of interests at the time you code the query, or you need it to adapt to changing lists of interest, you'll have to fetch the interests as rows and post-process these rows in your application.
What your trying to do sounds like a pivot.
Most solutions seem to revolve around one of the following approaches:
Creating a dynamic query, as in Is there a way to pivot rows to columns in MySQL without using CASE?
Selecting all the attribute columns, as in How to pivot a MySQL entity-attribute-value schema
Or, identifying the columns and using either a CASE statement or a user defined function as in pivot in mysql queries
I don't think this is possible. Actually I think this is just a matter of data representatioin. I would try to use a component to display the data that would allow me to pivot the data (for instance, the same way you do on excel, open office's calc, etc).
To take it one step further, you should think again why you need this and probably try to solve it in the application not in the database.
I know this doesn't help much but it's the best I can think of :(