Either my brain isn't functioning the way it should today or this is actually a hard thing to do.
I've got the following table containing my users.
id | name | birthdate |
------+----------+------------
1 | John | 1990-08-27
2 | Jane | 1985-08-29
3 | Joe | 1985-08-31
birthdate is a date column in the above table.
I'd like to get the users that have their birthday today or in previous days including the weekend.
I've come no further than the following attempt:
SELECT
*
FROM users
WHERE (DATE_FORMAT(users.birthdate, '%d-%m') IN (
'29-08',
'28-08',
'27-08',
'26-08'
)
Can any of you help me out with a query?
This query should do what you want. First it translates the user's birthdate to the current year:
STR_TO_DATE(CONCAT(YEAR(CURRENT_DATE()), DATE_FORMAT(birthdate, '-%m-%d')), '%Y-%m-%d')
then it sees if that date is between today (CURRENT_DATE()) and last Saturday:
CURRENT_DATE() - INTERVAL (WEEKDAY(CURRENT_DATE()) - 5 + 7) % 7 DAY
Full query:
SELECT *
FROM users
WHERE STR_TO_DATE(CONCAT(YEAR(CURRENT_DATE()), DATE_FORMAT(birthdate, '-%m-%d')), '%Y-%m-%d') BETWEEN
CURRENT_DATE() - INTERVAL (WEEKDAY(CURRENT_DATE()) - 5 + 7) % 7 DAY AND CURRENT_DATE()
SQLFiddle Demo
SELECT *
FROM users
WHERE DATE(users.birthdate + INTERVAL (YEAR(NOW()) - YEAR(users.birthdate)) YEAR)
BETWEEN
DATE(NOW() - INTERVAL WEEKDAY(NOW()) DAY)
AND
DATE(NOW() + INTERVAL 6 - WEEKDAY(NOW()) DAY);
http://www.sqlfiddle.com/#!9/71573/7
use NOW() - INTERVAL WEEKDAY(birthdate) +2 DAY get last Saterday Date.
Then use day and month function to get the day and month number, then check in where clause.
month(birthdate) is the same as NOW() month,
day number from last Saturday to NOW()
You can try this.
CREATE TABLE users(
id INT,
name VARCHAR(50),
birthdate DATE
);
INSERT INTO users VALUES (1,'John' , '1990-08-27');
INSERT INTO users VALUES (2,'Jane' , '1985-08-29');
INSERT INTO users VALUES (22,'Joe' , '1985-05-31');
INSERT INTO users VALUES (33,'Joe' , '1985-08-11');
INSERT INTO users VALUES (3,'Joe' , '1985-08-31');
Query 1:
SELECT *
FROM users
WHERE
month(birthdate) = month(NOW())
AND
day(birthdate) between day(NOW() - INTERVAL WEEKDAY(birthdate) +2 DAY) and day(NOW())
Results:
| id | name | birthdate |
|----|------|------------|
| 1 | John | 1990-08-27 |
| 2 | Jane | 1985-08-29 |
Try this query: curdate() - INTERVAL DAYOFWEEK(curdate()) this will give you date including saturday
SELECT * FROM users
WHERE day(users.birthdate)>=day(curdate() - INTERVAL DAYOFWEEK(curdate())) and <=day(curdate()) and (month(users.birthdate)=month(curdate()) or month(users.birthdate)=month(curdate() - INTERVAL DAYOFWEEK(curdate())))
Related
I have a DB in which I save all the daily visits to a lot of websites, the fields of the day table are name, nvisit and date
I want to make a comparison between the visits of the day before yesterday and those of yesterday, so the result to show should be 3 rows. name, the day before yesterday and yesterday
I have tried with this query but it shows me everything in 2 rows.
SELECT *
FROM (
SELECT name as nombre, nvisit as anteayer
from day
WHERE date < CURDATE() -1 and date > CURDATE() -2
UNION ALL
SELECT name as nombre, nvisit as ayer
from day
where date < CURDATE() and date > CURDATE() -1
GROUP by name
)
day
What can I do to solve this problem?
When executing the sentence the result is:
|name |beforeyesterday|
|.............|...............|
|example1.com |2154 |
|example1.com |3215 |
|example2.com |1524 |
|example2.com |2546 |
What I need:
|name |beforeyesterday|yesterday|
|.............|...............|.........|
|example1.com |2154 |3215 |
|example2.com |1524 |2546 |
I would want a separate row for each name with the columns for the counts on the various days:
select name,
sum(case when date < curdate() and date > curdate - interval 1 day
then nvisit
end) as yesterday,
sum(case when date < curdate() - interval 1 day and date > curdate - interval 2 day
then nvisit
end) as day_before
from day
group by name;
I have an insurance policies table like this:
+-------------------------------------------------------------+
| id | cancellation_val | cancellation_interval | expire_date |
+-------------------------------------------------------------+
| 1 | 30 | day | 2019-06-09 |
| 2 | 2 | month | 2019-12-01 |
+-------------------------------------------------------------+
I need to get the ids of the policies that are going to expire based on cancellation, from today and within 4 months, calculating the last day of the month, like this pseudo-code:
'today' <= LAST_DAY( expire_date - cancellation_val/interval ) < 'today + 4 months'
Being not a pro I think I should use JOINs but I don't know how, after days of trying the only thing I achieved was this:
SELECT LAST_DAY(
DATE_FORMAT(
STR_TO_DATE(
(SELECT CASE cancellation_interval
WHEN "day" THEN date_sub(expire_date, INTERVAL cancellation_val DAY)
WHEN "month" THEN date_sub(data_scadenzaexpire_date, INTERVAL cancellation_val MONTH)
END
AS newDate
FROM insurance WHERE id=2
), '%Y-%m-%d'
), '%Y-%m-%d'
)
)
This is working but I don't need the "WHERE id=2" clause (because I need to process ALL rows of the table), and if I remove it I got error "subquery returns more than 1 row".
So how I can proceed? And using the result to stay between 'today' AND 'today + 4 months' ?
I think with some kind of JOIN I could do it in a easier way but I don't know how.
Thank you all
The problem is the structure of the query, not the LAST_DAY function.
We want to return the id values of rows that meet some condition. So the query would be of the form:
SELECT t.id
, ...
FROM insurance t
WHERE ...
HAVING ...
Introducing another SELECT keyword basically introduces a subquery. There are restrictions on subqueries... in the SELECT list, a subquery can return a single column and (at most) a single row.
So let's ditch that extra SELECT keyword.
We can derive the newdate as an expression of the SELECT list, and then we can reference that derived column in the HAVING clause. The spec said we wanted to return the id value, so we include that in the SELECT list. We don't have to return any other columns, but for testing/debugging, it can be useful to return the values that were used to derive the newdate column.
Something like this:
SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
, t.expire_date
, t.cancellation_interval
, t.cancellation_val
FROM insurance t
HAVING newdate >= DATE(NOW())
AND newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY newdate ASC
We don't have to include the newdate in the SELECT list; we could just replace occurrences of newdate in the HAVING clause with the expression.
We could also use an inline view to "hide" the derivation of the newdate column
SELECT v.id
, v.newdate
FROM ( SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
FROM insurance t
) v
WHERE v.newdate >= DATE(NOW())
AND v.newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY v.newdate ASC
check this query: remove the HAVING Line to see all rows
SELECT
IF(cancellation_interval = 'day',
i.expire_date - INTERVAL i.`cancellation_val` DAY,
i.expire_date - INTERVAL i.`cancellation_val` MONTH
) as cancellation_day,
i.*
FROM `insurance` i
HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
SAMPLES
MariaDB [test]> SELECT IF(cancellation_interval = 'day', i.expire_date - INTERVAL i.`cancellation_val` DAY, i.expire_date - INTERVAL i.`cancellation_val` MONTH ) as cancellation_day, i.* FROM `insurance` i HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
+------------------+----+------------------+-----------------------+-------------+
| cancellation_day | id | cancellation_val | cancellation_interval | expire_date |
+------------------+----+------------------+-----------------------+-------------+
| 2019-05-10 | 1 | 30 | day | 2019-06-09 |
+------------------+----+------------------+-----------------------+-------------+
1 row in set (0.001 sec)
When you use a SELECT query as an expression, it can only return one row.
If you want to process all the rows, you need to call LAST_DAY() inside the query, not on the result.
SELECT *
FROM insurance
WHERE CURDATE() <= LAST_DAY(
expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH))
AND LAST_DAY(expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH)) < CURDATE + INTERVAL 4 MONTH
This question already has answers here:
multiple query same table but in different columns mysql
(4 answers)
Closed 4 years ago.
I have an the following table fields:
Invitations (user_id, type, created_at, completed_at)
I'm currently able to obtain last week's invitation conversation rate by running the following query and manually computing.
SELECT *
FROM invitations
WHERE created_at >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY
AND created_at < curdate() - INTERVAL DAYOFWEEK(curdate())-1 DAY
AND user_id != 1
AND type = 'email'
ORDER BY completed_at, created_at
Is it possible with SQL to output more of a report... Something that returns:
LAST WEEK | Total Invitations | Invitations Completed | % Conversion
| 100 | 50 | 50%
TWO WEEKS | Total Invitations | Invitations Completed | % Conversion
| 100 | 60 | 60%
Is something like this possible with SQL or do I need to create this with application logic?
Maybe you want to aggregate using count() and do a UNION ALL.
SELECT 'LAST WEEK' `Period`,
count(created_at) `Total Invitations`,
count(completed_at) `Invitations completed`,
concat(count(completed_at) / count(created_at) * 100, '%') `% Conversion`
FROM invitations
WHERE created_at >= curdate() - INTERVAL dayofweek(curdate()) + 6 DAY
AND created_at < curdate() - INTERVAL dayofweek(curdate()) - 1 DAY
AND user_id <> 1
AND type = 'email'
UNION ALL
SELECT 'TWO WEEKS' `Period`,
count(created_at) `Total Invitations`,
count(completed_at) `Invitations completed`,
concat(count(completed_at) / count(created_at) * 100, '%') `% Conversion`
FROM invitations
WHERE created_at >= curdate() - INTERVAL dayofweek(curdate()) + 13 DAY
AND created_at < curdate() - INTERVAL dayofweek(curdate()) - 1 DAY
AND user_id <> 1
AND type = 'email';
count(completed_at) does only count the rows, where completed_at isn't null. I assume that completed_at is null if and only if the invitation isn't completed. count(created_at) works analog. But I assume there are no null values in that column (and if they were, those rows won't match the conditions in the WHERE clause, so they aren't even candidates for counting). UNION ALL just unites the two result sets (without eliminating duplicates, which aren't in there anyway, as at least the Period differs).
I have a database table visitors with three columns:
id | Name | checkin_date |
1 | Reg | 2018-04-20T08:28:54.446Z |
2 | Meg | 2018-04-21T08:28:54.446Z |
3 | Ted | 2018-04-21T08:28:54.446Z |
4 | Bin | 2018-04-23T08:28:54.446Z |
There are several records such as these.
I want to fetch the count of records per each day for only the past 7 days. Right now i was able to fetch the count of visitors per day for all the dates using :
select count(id) as no_of_users
, DATE_FORMAT(checkin_date, '%d %b, %Y') as date
from visitors
GROUP
BY DATE(checkin_date)
But this displays the count of users per each day of all the records. How to get the records of only past 7 days.
select count(id) as no_of_users, DATE_FORMAT(checkin_date, '%d %b, %Y') as date from visitors
where checkin_date >= DATE(NOW()) - INTERVAL 7 DAY
GROUP BY DATE(checkin_date)
in the where is where you want to do the date field >= last 7 days
From your question.
You need to create a calendar table, then LEFT JOIN on the calendar table.
SELECT DATE(t.dt),count(t1.id) cnt
FROM
(
SELECT NOW() dt
UNION ALL
SELECT NOW() - INTERVAL 1 DAY
UNION ALL
SELECT NOW() - INTERVAL 2 DAY
UNION ALL
SELECT NOW() - INTERVAL 3 DAY
UNION ALL
SELECT NOW() - INTERVAL 4 DAY
UNION ALL
SELECT NOW() - INTERVAL 5 DAY
UNION ALL
SELECT NOW() - INTERVAL 6 DAY
UNION ALL
SELECT NOW() - INTERVAL 7 DAY
) t LEFT JOIN T t1
ON DATE(t.dt) = DATE(t1.checkin_date)
group by t1.name,DATE(t.dt)
sqlfiddle:http://sqlfiddle.com/#!9/59f49b/5
select id, count(id) as TOTAL, min (checkin_date) as no_of_users
from visitors
where checkin_date between '<Start Date>' and '<End Date>'
GROUP
BY Id,checkin_date
I seem to be having a bit of trouble coming up a query to achieve what I want. I have a table like the following..
| Date(TIMESTAMP) | Count |
|---------------------|-------|
| 2016-02-01 01:00:00 | 52 |
| 2016-01-05 11:30:00 | 14 |
| 2016-02-01 04:20:00 | 36 |
| ... | ... |
The table has about 40,000 rows. What I would like to do is grab the totals for multiple date ranges so I end up with the following...
| Period | Total |
|------------|-------|
| All | 10245 |
| Past year | 1401 |
| Past month | 104 |
| Past week | 26 |
Currently I am running through a loop in my PHP script and doing an individual query for each date range I'm looking for. Actually there are about 10 queries I'm doing per loop to grab different stats but for the example I'm simplifying it. This takes forever and I am hoping there is a more elegant way to do this, however I've spent quite a bit of time now trying different things and researching and have gotten nowhere. I understand how to use CASE to group but not when a record may need to be in multiple bins. Any help?
Try this UNION query:
SELECT 'All', COUNT(*) AS Total FROM yourTable
UNION
SELECT 'Past year', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 YEAR)
UNION
SELECT 'Past month', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 MONTH)
UNION
SELECT 'Past week', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 WEEK)
1st. get known to function getting first date of year, first date of month and first date of week.
Then compose your sql using count and filter with first and last date of different period.
ref:
MySQL Select First Day of Year and Month
month
https://stackoverflow.com/a/19259159/1258492
week https://stackoverflow.com/a/11831133/1258492
select 'All' as period, count(1) from
tbl
union
select 'Past Year' as period, count(1) from
tbl
where timestamp between
MAKEDATE(year(now())-1,1) and
last_day(MAKEDATE(year(now())-1,1) + interval 11 month)
union
select 'Past Month' as period, count(1) from
tbl
where timestamp between
LAST_DAY(NOW() - INTERVAL 2 MONTH) + INTERVAL 1 DAY and
LAST_DAY(NOW() - INTERVAL 1 MONTH)
union
select 'Past Week' as period, count(1) from
tbl
where timestamp between
adddate(curdate(), INTERVAL 1-DAYOFWEEK(curdate())-7 DAY) and
adddate(curdate(), INTERVAL 7-DAYOFWEEK(curdate())-7 DAY) ;
You may use subqueries. Use one subquery per time breakdown like so:
SELECT everything, 'past year'
FROM
(
SELECT sum(c) AS 'everything'
FROM reports
) t1,
(
SELECT sum(c) AS 'past year'
FROM reports
WHERE d >= DATE_ADD(CURDATE(), INTERVAL -1 YEAR)
) t2