Select the first element in each day of the month - mysql

How to select the first element of each day in a month with mysql query ?
I have table with offers - startdate, so i can check for each day,month,year i'm getting the element but, i'm wondering how to get only the first element in each day of some month ?

Assume the following
Table is called mytable
Table has id as primary key
Table has dt as datatime
You want the first id of everyday in February 2012
Try this:
SELECT B.id FROM
(
SELECT DATE(dt) date_dt,MIN(dt) dt
FROM mytable
WHERE dt >= '2012-02-01 00:00:00'
AND dt < '2012-03-01 00:00:00'
GROUP BY DATE(dt)
) A
LEFT JOIN mytable B USING (dt);
If any dt has multiple B.id values try this:
SELECT dt,MIN(id) id
(
SELECT B.id,B.dt FROM
(
SELECT DATE(dt) date_dt,MIN(dt) dt
FROM mytable
WHERE dt >= '2012-02-01 00:00:00'
AND dt < '2012-03-01 00:00:00'
GROUP BY DATE(dt)
) A
LEFT JOIN mytable B USING (dt)
) AA GROUP BY dt;

Assuming startdate is a DATETIME type, and the earliest entry is the one with the earliest DATETIME value, for March, 2012:
SELECT DISTINCT *
FROM tbl t1
LEFT JOIN tbl t2
ON (t2.startdate BETWEEN '2012-02-01 00:00:00' AND '2012-02-29 23:59:59')
AND t2.startdate < t1.startdate
WHERE (t1.startdate BETWEEN '2012-02-01 00:00:00' AND '2012-02-29 23:59:59')
AND t2.startdate IS NULL
If there are no duplicate dates, then you don't need the DISTINCT.
This query works by joining with any earlier record for the same month, so if nothing was joined, it's the earliest, through process of elimination.
This technique is explained in detail in the book SQL Antipatterns.
This could also be solved with subqueries, but this type of JOIN is supposed to be easier to optimize by MySQL than subqueries, which often negate the use of indexes.

without knowing the exact structure of your table something like this should work:
SELECT MIN(offerId) FROM offers WHERE startdate <= '2012-03-06' AND startdate >= '2012-02-06' GROUP BY date(startdate)

It sounds like you are trying to do something like the following:
SELECT col_1, date_col, col_3 FROM tbl
WHERE
date_col = ( SELECT min(date_col) FROM tbl
WHERE
year(date_col) = 2006 AND
month(date_col) = 02
);
This can also be used to find the max( date_col ) . Hope this helps.

Just to offer a different way to skin this cat (much easier in SQL Server for once actually)
SELECT
t0.offerId
FROM
offers AS t0 LEFT JOIN
offers AS t1 ON t0.offerId = t1.offerId AND t1.startDate > t0.startDate AND
(t0.startDate BETWEEN '2012-02-01' AND '2012-03-01') AND
(t1.startDate BETWEEN '2012-02-01' AND '2012-03-01')
WHERE
t1.col1 IS NULL;
If you have multiple rows with the same exact time you will get multiple values returned, which you can weed out in your application logic or with a sub-query. BTW this is called a groupwise minimum/maximum.

Related

How do I select data going back a period of time for each group?

I want for each group id to get the latest week's worth of data. Not from a specific date, but counting backwards from the MAX(startTime) of each individual group.
However, the following does not seem to work. I assume it's because startTime in each group (a single value) is BETWEENed by itself? Otherwise, how do I keep it in my filter?
SELECT
id
, startTime
FROM MyTable
GROUP BY id, startTime
HAVING startTime BETWEEN MAX(startTime) - INTERVAL 1 WEEK AND MAX(startTime)
What's the right query?
Also, in my case it has to work with MySQL 5.7.
SELECT m.id, m.startTime
FROM (
SELECT id, MAX(startTime) AS startTime
FROM MyTable GROUP BY id
) AS x
JOIN MyTable AS m ON m.id = x.id
AND m.startTime BETWEEN x.startTime - INTERVAL 1 WEEK AND x.startTime;

MySql : Get data from two tables

I am stuck with mysql query.Not able to proceed.
I have 2 tables where user's login time is recorded.Login time should be considered when either of the table contains entry. I want to find userwise sum of logins for a month.
I could reach till here. But not able to understand how to get sum
select table1.employeeId
, date(table1.loginTime) as date1
, date(table2.loginTime) as date2
from table1
inner join table2 on table1.employeeId=table2.employeeId
and table1.loginTime>='2017-01-01 00:00:00'
and table1.loginTime<='2017-01-31 23:59:59'
and table2.loginTime>='2017-01-01 00:00:00'
and table2.loginTime<='2017-01-31 23:59:59'
For ex : count=0
employe1 logged
on 1-Jan-2017 in table1 & table2 <- count++ (if he logs in 2 tables then only 1 count should be considered)
on 2-Jan-2017 in table1 <- count++
So for employee1 count is 2
You could do this with outer joins, but it would be needlessly complex:
select employeeId
, count(*) as loginCount
from ( select employeeId
, loginTime
from table1
where loginTime between '2017-01-01 00:00:00'
and '2017-01-31 23:59:59'
union
select employeeId
, loginTime
from table2
where loginTime between '2017-01-01 00:00:00'
and '2017-01-31 23:59:59'
( as a
group by employeeId;
Another approach:
SELECT
employeeId,
COUNT(1)+
(
SELECT COUNT(1)
FROM TABLE2 T2
WHERE MONTH(T2.loginTime) = 1
AND YEAR(T2.loginTime) = 2017
AND T2.employeeId = T1.employeeId
)
AS LOGINTIMES
FROM
TABLE1 T1
WHERE MONTH(loginTime) = 1
AND YEAR(loginTime) = 2017
GROUP BY employeeId
Sample fiddle: http://sqlfiddle.com/#!9/45ce27/3/0

Reverse order for GROUP BY in MySQL

I need to select first value for every hour from my db. But I don't know how to reverse order on GROUP BY statement.
How can i rewrite my query (now it selects last value in hour)?
SELECT HOUR(`time`) as hour, mytable.*
FROM mytable
WHERE DATE(`time`) ="2015-09-12" GROUP BY HOUR(`time`) ORDER BY `time` ASC;
This query gave me expected result:
SELECT HOUR(`time`) as hour, sortedTable.* FROM
(SELECT electrolysis.* FROM electrolysis
WHERE DATE(`time`)='2015-09-12' ORDER BY `time`) as sortedTable
GROUP BY HOUR(`time`);
You can just select the MIN HOUR in sub query , try using the query:
SELECT * from mytable WHERE `time` IN (
SELECT MIN(HOUR(`time`)) as `hour`
FROM mytable
WHERE DATE(`time`) ="2015-09-12"
GROUP BY HOUR(`time`) ) ORDER BY `time` ASC;
You can do something like this:-
SELECT sub0.min_time,
mytable.*
FROM mytable
INNER JOIN
(
SELECT MIN(`time`) AS min_time
FROM mytable
GROUP BY HOUR(`time`)
) sub0
ON mytable.`time` = sub0.min_time
WHERE DATE(`time`) ="2015-09-12"
ORDER BY `time` ASC
This is using a sub query to get the smallest time in each hour. This is then joined back against your main table on this min time to get the record that has this time.
Note that there is a potential problem here if there are multiple records that share the same time as the smallest one for an hour. There are ways around this, but that will depend on your data (eg, if you have a unique id field which is always ascending with time then you could select the min id for each hour and join based on that)
You can use below query, which is more optimized just make sure that time field should be indexed.
SELECT HOUR(m.time), m.*
FROM mytable AS m
JOIN
(
SELECT MIN(`time`) AS tm
FROM mytable
WHERE `time` >= '2015-09-12 00:00:00' AND `time` <= '2015-09-12 23:59:59'
GROUP BY HOUR(`time`)
) AS a ON m.time=a.tm
GROUP BY HOUR(m.time)
ORDER BY m.time;

SQL query that checks by week

I need an SQL query that checks for whether a person is active for two consecutive weeks in the year.
For example,
Table1:
Name | Activity | Date
Name1|Basketball| 08-08-2014
Name2|Volleyball| 08-09-2014
Name3|None | 08-10-2014
Name1|Tennis | 08-14-2014
I want to retrieve Name1 because that person has been active for two consecutive weeks in the year.
This is my query so far:
SELECT DISTINCT Name
FROM Table1
Where YEAR(Date) = 2014 AND
Activity NOT 'None' AND
This is where I would need the logic that checked for an activity in two consecutive weeks. A week can be described as 7 to 14 days later. I am working with MYSQL.
I have avoided using YEAR(Date) in the where clause deliberately, and recommend you do too. Using functions on multiple rows of data to suit a single criteria (2014) never makes sense to me, plus it destroys the effectiveness of indexes (see "sargable" at wikipedia). Way easier to just define a filter by a date range IMHO.
I've used a correlated subquery to derive nxt_date which might not scale very well, but overall the performance will depend on your indexes most probably.
select distinct
name
from (
select
t.name
, t.Activity
, t.`Date`
, (
select min(table1.`Date`) from table1
where t.name = table1.name
and table1.Activity <> 'None'
and table1.`Date` > t.`Date`
) as nxt_date
from table1 as t
where ( t.`Date` >= '2014-01-01' and t.`Date` < '2015-01-01' )
and t.Activity <> 'None'
) as sq
where datediff(sq.nxt_date, sq.`Date`) <= 14
;
see: http://sqlfiddle.com/#!9/cbbb3/9
You can do the logic using an exists subquery:
select t.*
from table1 t
where exists (select 1
from table1 t2
where t2.name = t.name and
t2.date between t.date + 7 and t.date + 14
);
I don't know if it is performance relevant, but I like concise queries:
SELECT t1.Name
FROM Table1 t1, Table1 t2
Where t1.Name=t2.Name AND
t1.Date >= '2014-01-01' AND t1.Date < '2015-01-01' AND
t1.Activity <> 'None' AND
t1.Date < t2.Date AND
datediff(t2.Date, t1.Date) <= 14
I liked the hint of #user2067753 about the YEAR(date).
I used the sqlfiddle of the answer above to check the performance using the explain syntax. It seems that avoiding sub queries as in VACN's answer or mine is beneficial (see join vs sub query)
From the top of my head, I suggest this query:
SELECT DISTINCT t1.Name
FROM Table1 AS t1, Table1 AS t2
WHERE t1.Name = t2.Name
AND t2.Date BETWEEN t1.Date-7 AND t1.Date+7;
The idea is basically: you call your table twice, select the rows whose names match, and then keep only those whose second date are up to 7 days away from the first date.

Return results of query based on todays date in SQL (MySQL)

I have a query which I got help with but I am stuck on another bit.
The code I have is
SELECT a.name, COUNT(*) AS num FROM table2 b
INNER JOIN table1 a
ON b.status_id=a.id
GROUP BY status_id
What I would like to do now is only show the results if they have been entered on the current date? The date column is in table2. The format for the date column is date and time (eg 1341241153) which is automatically set by the CRM in this way. I am not sure what format this is in!
I only need to check if the date matches the current day. I hope that is clear.
This is a MySQL database.
Any help will be gratefully received!
Use this solution:
SELECT a.name, COUNT(*) AS num
FROM table2 b
INNER JOIN table1 a ON b.status_id = a.id
WHERE b.datecol >= UNIX_TIMESTAMP(CURDATE()) AND
b.datecol < UNIX_TIMESTAMP(CURDATE() + INTERVAL 1 DAY)
GROUP BY b.status_id
This will avoid wrapping your date column inside a function which would make the query non-sargable (i.e. not able to use indexes). By keeping the comparison on the bare date column, MySQL will still able to utilize an index(if you have it set up on the date column), and it should be quite efficient.
You could simplify it even further if you ABSOLUTELY KNOW that the date entered can never be sometime in the future (i.e. tomorrow or the next day):
SELECT a.name, COUNT(*) AS num
FROM table2 b
INNER JOIN table1 a ON b.status_id = a.id
WHERE b.datecol >= UNIX_TIMESTAMP(CURDATE())
GROUP BY b.status_id
The second conditional check is removed as it doesn't need to check if it's in a future day.
You should use from_unixtime() function on date column that holds values like 1341241153.
Because these values seem stored in unix timestamp format.
Example:
mysql> select
-> from_unixtime( 1341241153 ) as 'my_datetime_1341241153',
-> date( from_unixtime( 1341241153 ) ) as 'my_date_1341241153',
-> curdate(),
-> curdate() > date( from_unixtime( 1341241153 ) ) 'is_today_later?',
-> curdate() = date( from_unixtime( 1341241153 ) ) 'is_today_equal?',
-> curdate() < date( from_unixtime( 1341241153 ) ) 'is_today_before?'
-> from
-> dual
-> \G
*************************** 1. row ***************************
my_datetime_1341241153: 2012-07-02 20:29:13
my_date_1341241153: 2012-07-02
curdate(): 2012-07-15
is_today_later?: 1
is_today_equal?: 0
is_today_before?: 0
1 row in set (0.00 sec)
Your query should be:
SELECT a.name, COUNT(*) AS num FROM table2 b
INNER JOIN table1 a
ON ( b.status_id=a.id and curdate() = date( from_unixtime( b.crm_date_time_column ) ) )
GROUP BY status_id
Try this::
SELECT a.name, COUNT(*) AS num FROM table2 b
INNER JOIN table1 a
ON b.status_id=a.id
where DATE(column_date) = DATE(now())
GROUP BY status_id