Mysql: Get records from last date - mysql

I want to get all records which are not "older" than 20 days. If there are no records within 20 days, I want all records from the most recent day. I'm doing this:
SELECT COUNT(DISTINCT t.id) FROM t
WHERE
(DATEDIFF(NOW(), t.created) <= 20
OR
(date(t.created) >= (SELECT max(date(created)) FROM t)));
This works so far, but it is awful slow. created is a datetime, might be due tue the conversion to a date... Any ideas how to speed this up?

SELECT COUNT(*) FROM (
SELECT * FROM t WHERE datediff(now(),created) between 0 and 20
UNION
SELECT * FROM (SELECT * FROM t WHERE created<now() LIMIT 1) last1
) last20d
I used the between clause just in case there might be dates in the future in the table. These will be excluded. Also you can simplify the select, if you just need the count() to
SELECT COUNT(*) FROM (
SELECT id FROM t WHERE datediff(now(),created) between 0 and 20
UNION
SELECT id FROM (SELECT id FROM t WHERE created<now() LIMIT 1) last1
) last20d
otherwise, in the first select version you can leave out the outer select if you want all the data of the chosen records. The UNION will make sure that duplicates will be excluded (in other cases I always use UNION ALL since it is faster).

Related

Getting missing time period value with an interval in My SQL

I'm trying to fetch the records with half an hour time interval of the current day with concern data count for that time period.
So, my output came as expected. But, If count(no records) on the particular time period let's say 7:00 - 7:30 I'm not getting that record with zero count.
My Query as follows :
SELECT time_format( FROM_UNIXTIME(ROUND(UNIX_TIMESTAMP(start_time)/(30* 60)) * (30*60)) , '%H:%i')
thirtyHourInterval , COUNT(bot_id) AS Count FROM bot_activity
WHERE start_time BETWEEN CONCAT(CURDATE(), ' 00:00:00') AND CONCAT(CURDATE(), ' 23:59:59')
GROUP BY ROUND(UNIX_TIMESTAMP(start_time)/(30* 60))
For reference of my output :
We need a source for that 7:30 row; a row source for all the time values.
If we have a clock table that contains all of the time values we want to return, such that we can write a query that returns that first column, the thirty minute interval values we want to return,
as an example:
SELECT c.hhmm AS thirty_minute_interval
FROM clock c
WHERE c.hhmm ...
ORDER BY c.hhmm
then we can do an outer join the results of the query with the missing rows
SELECT c.hhmm AS _thirty_minute_interval
, IFNULL(r._cnt_bot,0) AS _cnt_bot
FROM clock c
LEFT
JOIN ( -- query with missing rows
SELECT time_format(...) AS thirtyMinuteInterval
, COUNT(...) AS _cnt_bot
FROM bot_activity
WHERE
GROUP BY time_format(...)
) r
ON r.thirtyMinuteInterval = c.hhmm
WHERE c.hhmm ...
ORDER BY c.hhmm
The point is that the SELECT will not generate "missing" rows from a source where they don't exist; we need a source for them. We don't necessarily have to have a separate clock table, we could have an inline view generate the rows. But we do need to be able to SELECT those value from a source.
( Note that bot_id in the original query is indeterminate; the value will be from some row in the collapsed set of rows, but no guarantee which value. (If we add ONLY_FULL_GROUP_BY to sql_mode, the query will throw an error, like most other relational databases will when non-aggregate expressions in the SELECT list don't appear in the GROUP BY are aren't functionally dependent on the GROUP BY )
EDIT
In place of a clock table, we can use an inline view. For small sets, we could something like this.
SELECT c.tmi
FROM ( -- thirty minute interval
SELECT CONVERT(0,TIME) + INTERVAL h.h+r.h HOUR + INTERVAL m.mm MINUTE AS tmi
FROM ( SELECT 0 AS h UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
) h
CROSS JOIN ( SELECT 0 AS h UNION ALL SELECT 12 ) r
CROSS JOIN ( SELECT 0 AS mm UNION ALL SELECT 30 ) m
ORDER BY tmi
) c
ORDER
BY c.tmi
(Inline view c is a standin for a clock table, returns time values on thirty minute boundaries.)
That's kind of ugly. We can see where if we had a rowsource of just integer values, we could make this much simpler. But if we pick that apart, we can see how to extend the same pattern to generate fifteen minute intervals, or shorten it to generate two hour intervals.

I scratch my head with UNION ALL

I am not an SQL query wizard at all, and here is my problem:
I have those 3 separate querys that works very well and each one gives me a nice looking frame with results on my website.
SELECT arretsautressb AS Raison, SUM(minutesarrets) AS Minutes
FROM rapport_production_salles_blanches_2_repeat
GROUP BY Raison
ORDER BY Minutes DESC
SELECT redresseuseminutesarrets AS Raison, SUM(minutesarretsredresseuse) AS Minutes
FROM rapport_production_salles_blanches_3_repeat
GROUP BY Raison
ORDER BY Minutes DESC
SELECT raisonarretsconvoyeurair AS Raison, SUM(minutesarretsconvoyeurair) AS Minutes
FROM rapport_production_salles_blanches_4_repeat
GROUP BY Raison
ORDER BY Minutes DESC
So everything is fine with those 3 results...the Raison column in my table return all the rows and the Minutes query SUM all rows Group by Raison...
but i would like to merge those querys so it would give me only 1 big table with the results,instead on 3 tables.
But no matter how i try to format my UNION ALL code, what i get is 1 result only from each Raison query (so it takes only 1 row in sql table), instead of all the rows when they are separated. but the Minutes query is doing fine calculating the SUM of all the rows.
It would be cool if someone would just show me how to do it...cause i have been reading documentation for a couple of hours, and i am still stuck on this one.
This is what i tried so far, no error, but only 1 row of Raison is taken from sql table, instead of all rows:
SELECT *
FROM ( (SELECT arretsautressb AS Raison,
SUM(minutesarrets) AS Minutes
FROM rapport_production_salles_blanches_2_repeat t1)
UNION ALL
(SELECT redresseuseminutesarrets AS Raison,
SUM(minutesarretsredresseuse) AS Minutes
FROM rapport_production_salles_blanches_3_repeat t2)
UNION ALL
(SELECT raisonarretsconvoyeurair AS Raison,
SUM(minutesarretsconvoyeurair) AS Minutes
FROM rapport_production_salles_blanches_4_repeat t3)
) AS t123
GROUP BY Raison
ORDER BY Minutes DESC
This is what i get from my UNION ALL query:
UNION ALL
But this is what i get from 3 separated querys:
3 querys
I think your query doesn't return your desired result because of the following things:
It's fine to use a sub query where you specify the three tables and union them. However, you cannot use an aggregate (in this case SUM) without the use of GROUP BY.
Next, whenever you use GROUP BY, you should refer to the attribute instead of the column name. In my query I changed GROUP BY Raison to GROUP BY t1.arretsautressb.
I have used an ORDER BY on the outer query and I order by the second column, which is in this case the SUM(minutesarrets).
The query I would use is the following:
SELECT *
FROM (
SELECT arretsautressb AS Raison
, SUM(minutesarrets) AS sum_minutes
FROM rapport_production_salles_blanches_2_repeat AS t1
GROUP BY t1.arretsautressb
UNION ALL
SELECT redresseuseminutesarrets AS Raison
, SUM(minutesarretsredresseuse) AS sum_minutes
FROM rapport_production_salles_blanches_3_repeat AS t2
GROUP BY t2.redresseuseminutesarrets
UNION ALL
SELECT raisonarretsconvoyeurair AS Raison
, SUM(minutesarretsconvoyeurair) AS sum_minutes
FROM rapport_production_salles_blanches_4_repeat AS t3
GROUP BY t3.raisonarretsconvoyeurair
) AS t123
ORDER BY 2 DESC
Try this:
SELECT * FROM (
SELECT * FROM (
(SELECT arretsautressb AS Raison, SUM(minutesarrets) AS Minutes FROM rapport_production_salles_blanches_2_repeat t1)
UNION ALL
(SELECT redresseuseminutesarrets AS Raison, SUM(minutesarretsredresseuse) AS Minutes FROM rapport_production_salles_blanches_3_repeat t2)
) t1
UNION All
(SELECT raisonarretsconvoyeurair AS Raison, SUM(minutesarretsconvoyeurair) AS Minutes FROM rapport_production_salles_blanches_4_repeat t3)
) AS t123 GROUP BY t123.Raison ORDER BY t123.Minutes DESC

Grab x amount of records from MySQL and get duplicates if not enough

Not really sure how to do this, but is it possible in one query to fetch x amount of records from a table, and if not enough is found, it will just randomly select duplicates.
I have a photos table, let's say it has 5 records in it, and I want to pull out 10 records and order them randomly, so I have something like:
SELECT * FROM TABLE
ORDER BY RAND()
LIMIT 10
This will just pull back 5 randomly, cos that is all I have in the table. Can I tell MySQL, hey, if you find less than 10, just randomly grab more until you reach that number?
Any help appreciated!
Thanks
This will do it:
select * from Table1
union all
select * from
(
select * from
(
select * from Table1 limit 10
union all
select * from Table1 limit 10
union all
select * from Table1 limit 10
union all
select * from Table1 limit 10
-- more unions...
) t2 order by rand()
) rand_ordered
limit 10
Union the table for as many times as your number of needed records is (10 times in this example) to make it work with only one row in the table, order the result by rand() and append it to your table with another union all.
This might not be the best performing solution tho, but it will do it.
Example here: SQLFIDDLE

SQL Distinct - Get all values

Thanks for looking, I'm trying to get 20 entries from the database randomly and unique, so the same one doesn't appear twice. But I also have a questionGroup field, which should also not appear twice. I want to make that field distinct, but then get the ID of the field selected.
Below is my NOT WORKING script, because it does the ID as distinct too which
SELECT DISTINCT `questionGroup`,`id`
FROM `questions`
WHERE `area`='1'
ORDER BY rand() LIMIT 20
Any advise is greatly appreciated!
Thanks
Try doing the group by/distinct first in a subquery:
select *
from (select distinct `questionGroup`,`id`
from `questions`
where `area`='1'
) qc
order by rand()
limit 20
I see . . . What you want is to select a random row from each group, and then limit it to 20 groups. This is a harder problem. I'm not sure if you can do this accurately with a single query in mysql, not using variables or outside tables.
Here is an approximation:
select *
from (select `questionGroup`
coalesce(max(case when rand()*num < 1 then id end), min(id)) as id
from `questions` q join
(select questionGroup, count(*) as num
from questions
group by questionGroup
) qg
on qg.questionGroup = q.questionGroup
where `area`='1'
group by questionGroup
) qc
order by rand()
limit 20
This uses rand() to select an id, taking, on average two per grouping (but it is random, so sometimes 0, 1, 2, etc.). It chooses the max() of these. If none appear, then it takes the minimum.
This will be slightly biased away from the maximum id (or minimum, if you switch the min's and max's in the equation). For most applications, I'm not sure that this bias would make a big difference. In other databases that support ranking functions, you can solve the problem directly.
Something like this
SELECT DISTINCT *
FROM (
SELECT `questionGroup`,`id`
FROM `questions`
WHERE `area`='1'
ORDER BY rand()
) As q
LIMIT 20

How to use query results in another query?

I am trying to write a query which will give me the last entry of each month in a table called transactions. I believe I am halfway there as I have the following query which groups all the entries by month then selects the highest id in each group which is the last entry for each month.
SELECT max(id),
EXTRACT(YEAR_MONTH FROM date) as yyyymm
FROM transactions
GROUP BY yyyymm
Gives the correct results
id yyyymm
100 201006
105 201007
111 201008
118 201009
120 201010
I don’t know how to then run a query on the same table but select the balance column where it matches the id from the first query to give results
id balance date
120 10000 2010-10-08
118 11000 2010-09-29
I've tried subqueries and looked at joins but i'm not sure how to go about using them.
You can make your first select an inline view, and then join to it. Something like this (not tested, but should give you the idea):
SELECT x.id
, t.balance
, t.date
FROM your_table t
/* here, we make your select an inline view, then we can join to it */
, (SELECT max(id) id,
EXTRACT(YEAR_MONTH FROM date) as yyyymm
FROM transactions
GROUP BY yyyymm) x
WHERE t.id = x.id