MySQL Join with optional select value - mysql

I have a query on a Left join, where the Table who joins can contain zero rows. The problem is that if I select from that, then there are no rows where the join table contains zero rows.
How can I select it then anyway?
Here is my query
SELECT server.id
, COALESCE( AVG( playerData.player ) , 0 ) AS average
FROM server
LEFT JOIN playerData ON server.id = playerData.serverID
AND (playerData.timestamp > UNIX_TIMESTAMP( ) -10000)
The playerData table stores in one row how many people were on a server on a specific time. And multiple rows of this needs to be calculated to an average and this needs to be joined to the other query. When I omit the secound select column it gives me all rows (like it should), otherwise it only shows a result where also appropriate rows in the playerData table exists.
Additional Table data: playerData Table:
For the other table only the id column is important in this case.

Try this:
SELECT server.id
, COALESCE( AVG( playerData.player ) , 0 ) AS average
FROM server
LEFT JOIN playerData ON server.id = playerData.serverID
AND (playerData.timestamp > UNIX_TIMESTAMP( ) -10000)
group by server.id
Your query is missing the group by clause. When you include an aggregation function (like AVG()), the query automatically becomes an aggregation query. Without group by, all rows are aggregated into a single row. Presumably, you want one row per server.id.

Related

How to join a derived table

I have a complex query which results in a table which includes a time column. There are always two rows with the same time:
The result also contains a value column. The value of two rows with the same time is always different.
I now want to extend the query to join the rows with the same time together. So my thought was to join the derived table like this:
SELECT A.time, A.value AS valueA, B.value as valueB FROM
(
OLD_QUERY
) AS A INNER JOIN A AS B ON
A.time=B.time AND
A.value <> B.value;
However, the JOIN A AS B part of the query does not work. A is not recognized as the derived table. MySQL is searching for a table A in the database and does not find it.
So the question is: How can I join a derived table?
You cannot join a single reference to a table (or subquery) to itself; a subquery must be repeated.
Example: You cannot even do
SELECT A.* FROM sometable AS A INNER JOIN A ...
The A after the INNER JOIN is invalid unless you actually have a real table called A.
You can insert the subquery's results into another table, and use that; but it cannot be a true TEMPORARY table, as those cannot be joined to themselves or referenced twice at all in almost any query. _By referenced twice, I mean joined, unioned, used as an "WHERE IN" subquery when it is already referenced in the FROM.
If nothing else distinguishes the rows, you can just use aggregation to get the two values:
select time, min(value), max(value)
from (<your query here>) a
group by time;
In MySQL 8+, you can use a cte:
with a as (
<your query here>
)
select a1.time, a1.value, a2.value
from a a1 join
a a2
on a1.time = a2.time and a1.value <> a2.value;

MySQL 5.7 How to do GROUP BY with sorting?

Similar to this issue: MySQL 5.7 group by latest record
I'm not sure how to do this properly in 5.7. Also with possibility of 2nd sort column. Working query in 5.6 that I'm trying to replicate in 5.7:
SELECT id FROM test
GROUP BY category
ORDER BY sort1 DESC, sort2 DESC
id is not always the highest, so MAX(id) does not work.
Looking into the link above, the solution for single sort should be:
SELECT t1.*
FROM test t1
INNER JOIN (
SELECT category, max(sort) AS sort FROM test GROUP BY category
) t2 ON t2.category = t1.category AND t2.sort = t1.sort
But how will it work with 2 sorting?
You are using GROUP BY the wrong way.
Think of group by as a way to separate data row into different groups. Each group has multiple rows, based on the value of group by column.
Once you get those groups, selecting table columns (as in: select *) is like picking any row from that group randomly. This is not helpful nor useful.
Usually once we group records (or rows), we need to find meta information about those records. For example: get us the count of records in that group (as in: select count(*)), or the sum of values of a specific column in that group (as in: select sum(price)), or get the min, max or avg values.
So in a nutshell, when you use group by you should use on of the aggregation functions with it, otherwise it's not going to do you any good.
Why don't you have the ORDER BY at your outer query, instead?
SELECT *
FROM (
SELECT 100 AS id, 1 AS category, NULL AS sort
UNION
SELECT 200 AS id, 1 AS category, 2 AS sort
) dt
GROUP BY category
ORDER BY sort DESC;
It seems that what happened to the data when it was grouped, it took the first data while neglecting the ORDER BY DESC. On your first query, it ordered descending first then group by took the first record which is 200. And yes, this shouldn't be the way you should use GROUP BY. It is used in conjunction with aggregate functions.
when you select a column in a group by query that is not one of the columns you are grouping by, (ie, your id) you have no control over the value unless you use another aggregate function. If you want to sort, use MIN or MAX:
SELECT MAX(id), category, FROM `test2`
GROUP BY category; -- always returns 200
SELECT MIN(id), category, FROM `test2`
GROUP BY category; -- always returns 100

Finding item and dependent set of items from the same mysql table in one query

I have a table where I store items and the time where they are relevant. For this question the following columns are relevant:
CREATE TABLE my_items
(
id INTEGER,
category INTEGER,
t DOUBLE
);
I want to select all items from a specific category (e.g. 1) and the sets of items that have a time within +- 5 (seconds) from these items.
I will probably do this with two types of queries in a script:
SELECT id,t from my_items where category=1;
then loop over the result set, using each result row's time as t_q1, and do a separate query:
SELECT id from my_items where t >= t_q1-5 AND t <= t_q1+5;
How can I do this in one query?
You can use a join. Take your subquery that selects all category 1 items, and join it with the original table on the condition that the time is within +/- five. It's possible that duplicate rows are returned, so you can group by id to avoid that:
SELECT t.*
FROM myTable t
JOIN (SELECT id, timeCol FROM myTable WHERE category = 1) t1
ON t.timeCol BETWEEN (t1.timeCol - 5) AND (t1.timeCol + 5)
OR t.id = t1.id
GROUP BY t.id;
I added the OR t.id = t1.id to make sure that the rows of category 1 are still included.
You can use a single query with all you criteria if there is only one table
SELECT id,t from my_items where category=1 AND t >= t_q1-5 AND t <= t_q1+5;
If there is two tables, use a right join on the timestamps table for performance.
select id
from my_items i,
(select min(t) min_t, max(t) max_t from my_items where category=1) i2
where i.category = 1 or
i.t between i2.min_t-5 and i2.max_t+5

How to get information from two tables

I want to extract some information from two different table in one database,
[(first table): id-nbrNight-........]
[(second table): id-........]
I want to extract the nbrNight from the first table & the id from the second table:
so in my case I write this, but I don't know how to rassemble this two line in one line:
SELECT sum(nbrNight) as night FROM firsttab
SELECT count(`id`) as id FROM secondtab
I wirte this to rassemble this two line:
SELECT sum(nbrNight) as night,count(`id`) FROM firsttab,secondtab
But it doesn't work!
You can use UNION to combine the result from two query like
SELECT sum(nbrNight) as night FROM firsttab
UNION
SELECT count(`id`) as id FROM secondtab
(OR) do a JOIN with both tables using a common column between them (if any present) like below (assuming id is the common column between them)
SELECT sum(t1.nbrNight) as nightsum, count(t2.`id`) as idcount
FROM firsttab t1 JOIN secondtab t2 ON t1.id = t2.id
One option is to use the queries as inline views; reference those queries as a rowsource (like a table) in another query.
Since each query returns a single row, you can safely perform a JOIN operation, without need for any join predicate (aka CROSS JOIN).
For example:
SELECT f.night
, s.id
FROM ( SELECT SUM(nbrNight) AS night FROM firsttab ) f
CROSS
JOIN ( SELECT COUNT(id) AS id FROM secondtab ) s
Another option (since both queries are guaranteed to return exactly one row, if they don't return an error) is to include the query in the SELECT list of another query. It's not necessary to include a column alias in the subquery, but we can assign an alias.
For example:
SELECT ( SELECT SUM(nbrNight) FROM firsttab ) AS night
, ( SELECT COUNT(id) FROM secondtab ) AS id
If either of the queries were returning more than one column, then the approach in the first example will still work. The inline view queries can return multiple expressions, and we can reference those expressions in the outer query. With the pattern in the second example, that imposes a restriction that the subqueries must return only one expression (one column).
As an example to demonstrate an inline view returning more than one column, the inline view f returns three expressions:
SELECT f.night
, f.cnt
, f.min_nbr
, s.id
FROM ( SELECT SUM(nbrNight) AS night
, COUNT(nbrNight) AS cnt
, MIN(nbrNight) AS min_nbr
FROM firsttab
) f
CROSS
JOIN ( SELECT COUNT(id) AS id FROM secondtab ) s

SQL find distinct and show other columns

I have read many replies and to similar questions but cannot seem to apply it to my situation. I have a table that averages 10,000 records and is ever changing. It containing a column called deviceID which has about 20 unique values, another called dateAndTime and many others including status1 and status2. I need to isolate one instance each deviceID, showing the record that had the most current dateAndTime. This works great using:
select DISTINCT deviceID, MAX(dateAndTime)
from MyTable
Group By deviceID
ORDER BY MAX(dateAndTime) DESC
(I have noticed omitting DISTINCT from the above statement also yields the same result)
However, I cannot expand this statement to include the fields status fields without incurring errors in the statement or incorrect results. I have tried using IN and EXISTS and syntax to isolate rows, all without luck. I am wondering how I can nest or re-write this query so that the results will display the unique deviceID's, the date of the most recent record and the corresponding status fields associated with those unique records.
If you can guarantee that the DeviceID + DateAndTime is UNIQUE you can do the following:
SELECT *
FROM
MyTable as T1,
(SELECT DeviceID, max(DateAndTime) as mx FROM MyTable group by DeviceID) as T2
WHERE
T1.DeviceID = T2.DeviceID AND
T1.DateAndTime = T2.mx
So basically what happens is, that you do a group by on the DeviceID (NOTE: A GROUP BY always goes with an aggregate function. We are using MAX in this case).
Then you join the Query with the Table, and add the DeviceID + DateAndTime in the WHERE clause.
Side Note... GROUP BY will return distinct elements with or without adding DISTINCT because all rows are distinct by default.
Maybe:
SELECT a.*
FROM( SELECT DISTINCT *,
ROW_NUMBER() OVER (PARTITION BY deviceID ORDER BY dateAndTime DESC) as rown
FROM MyTable ) a
WHERE a.rown = 1