mysql query that has join and counts - mysql

I need help getting the top 5 results and their counts from columns from two different tables in a mysql database joined together.
table1 cols
-------
id, country, timestamp
table2 cols
--------
id, table1_id, reason
The results id like to get are the top 5 countries and their number of times found between two timestamps, and the top 5 reasons and their counts for all the rows used to generate the first count. There is a one to many relationship between table1 and table2. This is stumping me and I appreciate any insight you could give me.

It's not entirely clear what resultset you want to return.
This may be of some help to you:
SELECT t.country
, COUNT(DISTINCT t.id) AS count_table1_rows
, COUNT(r.id) AS count_table2_rows
, COUNT(*) AS count_total_rows
FROM table1 t
LEFT
JOIN table2 r
ON r.table1_id = t.id
WHERE t.timestamp >= NOW() - INTERVAL 7 DAY
AND t.timestamp < NOW()
GROUP BY t.country
ORDER BY COUNT(DISTINCT t.id) DESC
LIMIT 5
That will return a maximum of 5 rows, one row per country, with counts of rows in table1, counts of rows found in table2, and a count of the total rows returned.
The LEFT keyword specifies an "outer" join operation, such that rows from table1 are returned even if there are no matching rows found in table2.
To get the count for each "reason", associated with each country, you could do something like this:
SELECT t.country
, COUNT(DISTINCT t.id) AS count_table1_rows
FROM table1 t
LEFT
JOIN ( SELECT s.country
, r.reason
, COUNT(*) AS cnt_r
FROM table1 s
JOIN table2 r
ON s.table1_id = t.id
WHERE s.timestamp >= NOW() - INTERVAL 7 DAY
AND s.timestamp < NOW()
GROUP
BY s.country
, r.reason
) u
ON u.country = t.country
WHERE t.timestamp >= NOW() - INTERVAL 7 DAY
AND t.timestamp < NOW()
GROUP
BY t.country
, u.reason
ORDER
BY COUNT(DISTINCT t.id) DESC
, t.country DESC
, u.cnt_r DESC
, u.reason DESC
This query doesn't "limit" the rows being returned. It would be possible to modify the query to have only a subset of the rows returned, but that can get complex. And before we muck the complexity of adding "top 5 within top 5" type limits, we want to ensure that the rows returned by a query are a superset of the rows we actually want.

Is this what you want?
select t2.reason, count(*)
from (select t1.country, count(*)
from table1 t1
where timestamp between #STARTTIME and #ENDTIME
group by country
order by count(*) desc
limit 5
) c5 join
table1 t1
on c5.country = t1.country and
t1.timestamp between #STARTTIME and #ENDTIME join
table2 t2
on t2.table1_id = t1.id
group by t2.reason;
The c5 subquery gets the five countries. The other two bring back the data for the final aggregation.

Related

full join 2 tables in mariaDB

I have 2 tables
Table1
Year,Month, data
2017,1,2
2018,2,10
Table2
Year,Month,data2
2017,1,5
2019,2,2
I am trying to consolidate the tables into 1 tables where we get all rows from both tables as follows.
Year,Month,data,data2
2017 ,1,2,5
2018,2,10,NULL
2019,2,NULL,2
It seems like standard outer joins will not work here and I can't use Union ALL either
Is there some kind of Outer join query to accomplish this?
You should use a UNION for obtain allthe year and month from both tables and use left join for relate this to table1 and table2
select a.Year , a.Month, b.data, c.data2
from (
select Year,Month
from Table1
union
select Year,Month
from Table2
) a
left join table1 b on a.Year = b.Year and a.month = b. month
left join table2 c on a.Year = c.Year and a.month = c. month
What you really want is a full join. One method that often works is union all and group by:
select year, month, max(data) as data, max(data2) as data2
from ((select year, month, data, null as data2
from table1
) union all
(select year, month, null, data2
from table2
)
) t
group by year, month;

SQL make COUNT() return total rows in query

I have a query in MySQL as follows (table names are changed):
SELECT
t1.name,
t2.type,
if(data1 = "6", sum(coalesce(data2, 0) * coalesce(data3, 1)) as more_data
FROM table_main tm
JOIN table1 t1 ON tm.t1_id = t1.id
JOIN table2 t2 ON tm.t2_id = t2.id
WHERE day = '2018-01-01'
GROUP BY t1.name, t2.type
ORDER BY more_data DESC
This query returns around 400 results, but for some reason every time I try to count() anything it keeps returning random data and not 1 record giving the total amount of records given by this query, now I've tried to get rid of most of the query and only leaving the 'if'-statement as a select also gives the required results, but then still it won't let me use that value in the count() method, is there any way I can count the records from this query without getting random data>
Your conditional aggregation is turned inside out... Use a case expression inside sum() instead.
SELECT
t1.name,
t2.type,
sum(case when data1 = "6" then coalesce(data2, 0) * coalesce(data3, 1) else 0 end) as more_data
FROM table_main tm
JOIN table1 t1 ON tm.t1_id = t1.id
JOIN table2 t2 ON tm.t2_id = t2.id
WHERE day = '2018-01-01'
GROUP BY t1.name, t2.type
ORDER BY more_data DESC

Rewrite 2 select statements as one JOIN Statement

I've been strugling with a JOIN statement which I can't figure out.
I want to get the latest(newest) entry in two different tables and join the result in the output.
The 2 Select statements look like this and they work as expected but I can't seem to get the right result when I try to rewite them as a JOIN statement.
Please help, Thanks.
SELECT MaxNum
FROM table1
WHERE UserID = 4
ORDER BY Date DESC
LIMIT 1
SELECT MinNum
FROM table2
WHERE UserID = 4
ORDER BY Date DESC
LIMIT 1
I'm not sure that a join necessarily makes sense here. However, you can slightly modify what you currently have to be one query:
SELECT
(SELECT MaxNum FROM table1 WHERE UserID = 4 ORDER BY Date DESC LIMIT 1) AS MaxNum,
(SELECT MinNum FROM table2 WHERE UserID = 4 ORDER BY Date DESC LIMIT 1) AS MinNum
Since each of these queries only returns one row, one dirty trick you can use is to cross join the results:
SELECT MaxNum, MinNum
FROM (SELECT MaxNum
FROM table1
WHERE UserID = 4
ORDER BY Date DESC
LIMIT 1) t
CROSS JOIN (SELECT MinNum
FROM table2
WHERE UserID = 4
ORDER BY Date DESC
LIMIT 1) s
select TOP 1 table1.MaxNum, table2.MinNum
FROM table1 INNER JOIN table2
ON table1.UserID = table2.UserID
WHERE table1.UserID = 4
ORDER BY table1.Date DESC
If you want to display result as one row then use your queries as subqueries in FROM clause.
SELECT *
FROM
(SELECT MaxNum FROM table1 WHERE UserID = 4 ORDER BY `Date` DESC LIMIT 1) AS q1,
(SELECT MinNum FROM table2 WHERE UserID = 4 ORDER BY `Date` DESC LIMIT 1) AS q2;

How do I select items in range of a SQL query?

My database is in MySQL.
Assume I have the following table:
id number
1 45
2 25
3 66
4 43
......
......
......
30 54
31 21
etc ... etc.
I want to have a query like so:
select * from myTable where number = 25
but I want to also include 2 more items, one above it and one below it (based on ID).
the result set of my query would turn up with the following result set: 1, 2, 3.
If I selected the number 66, then the result set would include 2, 3, 4. Etc, etc.
The idea would be to range the query by saying, hey, I want anything that has an ID equal to 1 minus this queries' id, and also one plus this queries' id.
I hope this makes sense.
Any help would be great.
Thanks!
P.S.
The point of this is to capture events in a log so that I can see what happened before and after a certain event happened
SELECT t.*
FROM
myTable AS t
JOIN
( SELECT id
FROM myTable
WHERE number = 25
) AS my
ON t.id BETWEEN my.id - 1 AND my.id + 1 ;
Notice that this will not show 3 rows if your ids have gaps.
Also, if the number you choose (25 in the example) appears more than once (but k times), the result will be 3*k rows.
If there are gaps, as expected, in the id column, you can use this:
SELECT *
FROM
( SELECT t.*
FROM
myTable AS t
JOIN
( SELECT MIN(id) AS id
FROM myTable
WHERE number = 25
) AS my
ON t.id <= my.id
ORDER BY t.id DESC
LIMIT 2
) AS a
UNION ALL
SELECT *
FROM
( SELECT t.*
FROM
myTable AS t
JOIN
( SELECT MIN(id) AS id
FROM myTable
WHERE number = 25
) AS my
ON t.id > my.id
ORDER BY t.id ASC
LIMIT 1
) AS b ;
If there are gaps in the id column and the number is not unique so the parameter (25) can appear more than once (but say k times), you can have a query that returns 3*k rows (almost all the times):
SELECT t.*
FROM
myTable AS t
JOIN
( SELECT id
FROM myTable
WHERE number = 25
) AS ti
ON t.id =
( SELECT tt.id
FROM myTable AS tt
WHERE tt.id < ti.id
ORDER BY tt.id DESC
LIMIT 1
)
OR t.id = ti.id
OR t.id =
( SELECT tt.id
FROM myTable AS tt
WHERE tt.id > ti.id
ORDER BY tt.id ASC
LIMIT 1
) ;
Not sure if it will work (I remember there are some limitations on ORDER BY and LIMIT in UNIONed queries, but don't have mysql instance to check it), but what if you try:
(SELECT t2.id
FROM myTable t1
INNER JOIN myTable t2 ON t2.id > t1.id
WHERE t1.number = 25
ORDER BY t2.id
LIMIT 1)
UNION ALL
(SELECT t2.id
FROM myTable t1
INNER JOIN myTable t2 ON t2.id < t1.id
WHERE t1.number = 25
ORDER BY t2.id DESC
LIMIT 1)
SELECT * FROM table WHERE id IN (SELECT id, id-1, id+1 FROM table WHERE number=25)

SQL select the last 3 dates from a table

I have a table with lots of fields in mysql
I need a query to return (in the same raw!) the top last 3 dates (dates can have large gaps between them)
ie:
2012/01/20
2012/01/18
2012/01/12
2012/01/10
2012/01/04
etc...
Any help will be appreciated
I must get them in the same row!
This is the query I am trying to use with no success:
SELECT a.id, a.thedate, b.id AS id1, b.thedate AS thedate1, c.id AS id2, c.thedate as thedate2
FROM mytable AS a INNER JOIN mytable AS b ON a.id = b.id INNER JOIN mytable AS c ON b.id=c.id
WHERE c.thedate = SELECT MAX(thedate)
EDIT :
SELECT group_concat(date) FROM (SELECT date FROM my_table ORDER BY date DESC LIMIT 3) AS temp
Corrected-
SELECT group_concat(date) FROM ( select date from table_name order by date desc limit 3) as a
SELECT GROUP_CONCAT(a.date )
FROM (
SELECT date
FROM my_table
ORDER BY date DESC
LIMIT 3
) AS a