I've got a database with events and organizations that run those events. I am looking to create a query that shows any organization that has not created an event in 90 days or more.
So far, I have this query:
SELECT organizations.name, organizations.first_name, organizations.last_name,
organizations.email, events.created_at, events.start_date, events.end_date
FROM events
INNER JOIN organizations ON events.organizer_id = organizations.id
WHERE DATE_SUB(CURDATE(),INTERVAL 90 DAY) > events.created_at
GROUP BY events.organizer_id
ORDER BY events.created_at DESC
The problem is that this will just choose any event that is more than 90 days old, but not the latest event. How do I get the query to look at the event with the newest created_at and see if that is 90 days or older and include only that?
You can use aggregation. If you care about organizations, then you don't need event information in the select:
SELECT o.name, o.first_name, o.last_name, o.email
FROM events e INNER JOIN
organizations o
ON e.organizer_id = o.id
GROUP BY o.id
HAVING DATE_SUB(CURDATE(), INTERVAL 90 DAY) > MAX(e.created_at)
ORDER BY MAX(e.created_at) DESC;
This query will not select organizations that never have events. To do that, you need a left outer join. Here is one way:
SELECT o.name, o.first_name, o.last_name, o.email
FROM organizations o LEFT JOIN
events e
ON e.organizer_id = o.id AND
e.created_at >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
WHERE e.organizer_id is null
GROUP BY o.id
ORDER BY MAX(e.created_at) DESC;
Note that I also changed the query to use table aliases. These make the query easier to write and read.
Try this:
First create a query (call it "A" here) that selects all organiazations which created an event in the last 90 days.
SELECT organizer_id from events where DATE_SUB(CURDATE(),INTERVAL 90 DAY) <= events.created_at
Then write a query which lists all organizations which are not in the result set of query A:
SELECT * from organizations where id not in (A)
Which makes it to:
SELECT * from organizations where id not in (SELECT organizer_id from events where DATE_SUB(CURDATE(),INTERVAL 90 DAY) <= events.created_at)
This works because SQL is "orthogonal", i.e., you can embed queries into other queries.
Related
I'm trying to create a SQL query that retrieves records based on the start and end dates. However, the system doesn't have a very nice Database schema, so I'm looking for a way to create a query that works with it.
Basically, there's a meta table that stores the events' dates using the "date_NUMBER_(start|end)" format:
event_id
meta_key
meta_value
1
date_0_start
2022-04-02
1
date_0_end
2022-04-03
-
-
-
2
date_0_start
2022-03-21
2
date_0_end
2022-03-22
2
date_1_start
2022-06-24
2
date_1_end
2022-06-30
So, Event 1 has one date span: 2022-04-02 to 2022-04-03. While Event 2 has two: 2022-03-21 to 2022-03-22 and 2022-06-24 to 2022-06-30.
After 2022-03-22, Event 2 is not active, starting again only on 2022-06-24.
To create a filter that works with this schema, I came up with the following SQL query. For example, to search for events happening between the 2022-04-01 - 2022-04-03 range (any event that there's an event day between the range):
SELECT events.id
FROM events INNER JOIN events_meta ON ( events.id = events_meta.event_id ) INNER JOIN events_meta AS evt1 ON (
events.id = evt1.event_id )
WHERE
(
(
events_meta.meta_key LIKE 'date_%_start' AND
CAST(events_meta.meta_value AS DATE) >= '2022-04-01'
)
AND
(
evt1.meta_key LIKE 'date_%_end' AND
CAST(evt1.meta_value AS DATE) <= '2022-04-03'
)
)
The problem is that in this way, I should get only Event 1, but Event 2 is also returned since date_1_start is >= '2022-04-01' and date_0_end is <= '2022-04-03'
I'm not being able to find a way where I can match the NUMBER in the "date_NUMBER_(start|end)" meta_key format, so the query doesn't compare different NUMBERs.
Any help is appreciated :)
I made a fiddle with the INNER JOIN query:
http://sqlfiddle.com/#!9/fb497e/2
Use a self-join to get different keys for the same event ID. See MYSQL Select from tables based on multiple rows
SELECT m1.event_id, m1.meta_value AS start, m2.meta_value AS end
FROM events_meta AS m1
JOIN events_meta AS m2
ON m1.event_id = m2.event_id
AND SUBSTRING_INDEX(m1.meta_key, '_', 2) = SUBSTRING_INDEX(m2.meta_key, '_', 2)
WHERE m1.meta_key LIKE 'date_%_start' AND m2.meta_key LIKE 'date_%_end'
AND CAST(m1.meta_value AS DATE) >= '2022-04-01'
AND CAST(m2.meta_value AS DATE) <= '2022-04-03'
The SUBSTRING_INDEX() calls will return the prefixes date_0, date_1, etc. Including this in the ON condition will pair up the corresponding start and end keys.
I'm tring to write a query, without a good resultset.
I would to retreive a Number of reservations in last 7 days.
Grouped by NameOfResource.
When i'm tring to set a WHERE clause like this prenotazioni.Data >= CURDATE() - INTERVAL 7 DAY
I get only NameOfResource with reservations in latest 7 days,
but not the rooms without reservations.
My Query was like that: (without WHERE the result is good)
SELECT count(*) as NReservationsFromWeek,Nomeroom FROM reservations
INNER JOIN room ON reservations.FKRoom = room.IDRoom
WHERE reservations.Data >= CURDATE() - INTERVAL 7 DAY
group by room.IDRoom
Thank you to explain me where I was wrong.
You can use a LEFT JOIN, if you want all rooms, even those with a count of 0:
SELECT ro.IDRoom, ro.Nomeroom, COUNT(re.FKRoom) as NReservationsFromWeek,
FROM room ro LEFT JOIN
reservations re
ON re.FKRoom = ro.IDRoom AND
re.Data >= CURDATE() - INTERVAL 7 DAY
GROUP BY ro.IDRoom, ro.Nomeroom; -- Both unaggregated keys should be in the GROUP BY
I have the following query that is quite complex and even though I tried to understand how to do using various sources online, all the examples uses simple queries where mine is more complex, and for that, I don't find the solution.
Here's my current query :
SELECT id, category_id, name
FROM orders AS u1
WHERE added < (UTC_TIMESTAMP() - INTERVAL 60 SECOND)
AND (executed IS NULL OR executed < (UTC_DATE() - INTERVAL 1 MONTH))
AND category_id NOT IN (SELECT category_id
FROM orders AS u2
WHERE executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
GROUP BY category_id)
GROUP BY category_id
ORDER BY added ASC
LIMIT 10;
The table orders is like this:
id
category_id
name
added
executed
The purpose of the query is to list n orders (here, 10) that belong in different categories (I have hundreds of categories), so 10 category_id different. The orders showed here must be older than a minute ago (INTERVAL 60 SECOND) and never executed (IS NULL) or executed more than a month ago.
The NOT IN query is to avoid treating a category_id that has already been treated less than 5 seconds ago. So in the result, I remove all the categories that have been treated less than 5 seconds ago.
I've tried to change the NOT IN in a LEFT JOIN clause or a NOT EXISTS but the switch results in a different set of entries so I believe it's not correct.
Here's what I have so far :
SELECT u1.id, u1.category_id, u1.name, u1.added
FROM orders AS u1
LEFT JOIN orders AS u2
ON u1.category_id = u2.category_id
AND u2.executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
WHERE u1.added < (UTC_TIMESTAMP() - INTERVAL 60 SECOND)
AND (u1.executed IS NULL OR u1.executed < (UTC_DATE() - INTERVAL 1 MONTH))
AND u2.category_id IS NULL
GROUP BY u1.category_id
LIMIT 10
Thank you for your help.
Here's a sample data to try. In that case, there is no "older than 5 seconds" since it's near impossible to get a correct value, but it gives you some data to help out :)
Your query is using a column which doesn't exist in the table as a join condition.
ON u1.domain = u2.category_id
There is no column in your example data called "domain"
Your query is also using the incorrect operator for your 2nd join condition.
AND u2.executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
should be
AND u2.executed < (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
as is used in your first query
I have three tables. customers, DVDs, Movies. This is the last report I need to produce, and it's tripping me up. I currently have a field: dvd.Due_Date, which is the date that the product is due back. I need to retrieve all files where the Due Date is 15 days past the current date.
This is what I have so far:
SELECT customer.customer_id,
customer.customer_fname,
customer.customer_lname,
customer.customer_phone,
customer.customer_email,
dvd.DVD_ID, movie.Movie_Title,
dvd.Rental_Date, dvd.Due_Date
FROM customer INNER JOIN dvd
ON customer.customer_id = dvd.customer_id
INNER JOIN movie ON dvd.Movie_ID = movie.Movie_ID
WHERE DATEDIFF(Due_Date, CURDATE() ) > 15
I'm not getting any errors, I'm just not getting any results back, even though I have multiple items listed as due date of Feb. 10th. I do get all of the information I want if I remove everything past the WHERE statement, so I know that is working at least.
For DATEDIFF if the first item is a smaller date than the second item then it returns a negative number (as such could never be larger than 16) and not a positive one. So flip them, you want the later date as the first argument:
... WHERE DATEDIFF( CURDATE(), Due_Date ) > 15
I'm not sure what you mean by "all files where the Due Date is 15 days past the current date." However, try using logic like this:
SELECT c.customer_id, c.customer_fname, c.customer_lname, c.customer_phone, c.customer_email, d.DVD_ID, m.Movie_Title, d.Rental_Date, d.Due_Date
FROM customer c INNER JOIN
dvd d
ON c.customer_id = d.customer_id INNER JOIN
movie m
ON d.Movie_ID = m.Movie_ID
WHERE due_date >= date_sub(curdate(), interval 15 day);
You might want date_add() instead.
how would i create a "last 24 hour top" table by pageviews if i have the following tables.
movie: id, title
pageview: id, movie, vieweddate
i need the third table like this
top: id, movie, moviecount24
so i would be able to make such query:
select * from top24 order by viewcount24 desc limit 30
i need to create top table using ONE mysql query.
select
pageview.id as pageview_id
,movie.title as move_title
,count(movie.id) as moviecount24
from pageview
inner join movie on (pageview.movie = movie.id)
where pageview.vieweddate between date_sub(now(), interval 1 day) and now()
order by moviecount24 desc
limit 30;
Let me refactor Johan's response a bit:
INSERT INTO top
SELECT movie.id AS id, COUNT(movie.id) as moviecount24
FROM pageview
LEFT JOIN movie ON pageview.movie = movie.id
WHERE vieweddate > NOW() - INTERVAL 1 DAY
LEFT JOIN is important here, since some movies may have zero views, but still should be present in the result.