Find two entries in a database table with different criteria - mysql

I have a membership mySQL database. One of the tables is for membership history and records each time a member renews. There is a unique auto-increment field, one for the member's login name (which is the constant for each member) and one for expiry date.
I'm looking for a way to search for all members who, for instance, expired in Nov 2014 and then renewed in Jan 2015 (their new expiry date would be Jan 2016). The data for each one would be in two separate rows in the table.
So I'm looking to find the login_name where expiry = Nov 14 and also find the same login name if there's another entry of expiry = Jan 16 then display the result as a list.
I've been working along these lines but I'm not there yet...
SELECT * FROM `membership_history`
WHERE `login_name` IN (
SELECT `login_name`
FROM `membership_history`
WHERE `membership_enddate` BETWEEN '2014-11-01' AND '2014-11-30'
OR `membership_enddate` BETWEEN '2016-01-01' AND '2016-01-31'
GROUP BY `login_name` HAVING count(`login_name`) > 1
)
ORDER BY `login_name`
Any tips or help would be appreciated - thanks

First, I'd be inclined to write this as a join rather than in. Then, you can eliminate the where clause, unless needed for performance reasons (it filters the data but is not needed to get the same results):
SELECT mh.*
FROM `membership_history` mh JOIN
(select login_name
from membership_history mh2
group by login_name
having sum(membership_enddate BETWEEN '2014-11-01' AND '2014-11-30') > 0 and
sum(membership_enddate BETWEEN '2016-01-01' AND '2016-01-31') > 0
) ml
on mh.login_name = ml.login_name;

Related

MySQL question about using between operators

I am learning MySQL and saw a project related to e-commerce and customer behaviour and want to follow it along.
However, when calculating the number of unique customers retained on the second day, the original author used a different approach and got a different result.
The code is below:
select count(distinct user_id) as first_day_customer_num from userbehavior
where date = '2017-11-25';-- 359 unique customers counted and retained on the first day
select count(distinct user_id) as second_day_customer_num from userbehavior
where date = '2017-11-26' and user_id in (SELECT user_id FROM userbehavior
WHERE date = '2017-11-25');-- 295 unique customers counted and retained on the second day
I used the between method for date and here is my code below to calculate the number of unique customers retained on the second day:
select count(distinct user_id) as trial from userbehavior
where date between '2017-11-25' and '2017-11-26'; -- 450 unique customers counted
Could I ask why is our result different and which part did I do wrong?
Thank you so much for your help and support, really appreciate it.

Join on Date Ranges

I've looked at other answers to this question but haven't found a solution.
I have two tables with a tracking number, one has status history and several records per tracking with different date times for each status. The other table is a cost table that has one record per tracking with a date time that is in the same general time period of the status table but never exact.
I cannot join just on the tracking number itself due to the duplication of the tracking number in the data from months prior. Ex. a tracking number may appear in March of 2019 and again in January of 2020 even though they are very different parcels being shipped. However if you concatenate the tracking with the orderid on the status table you do get a unique value. That orderid number though is not in the cost table so you cannot join the two tables on that value either. It has to be tracking and a date range of some sort.
So I am looking to join the two tables using the tracking number and a date range of +- 30 days from the date provided on the cost table and the final date for that tracking number on the status table.
So something like this without the "is in a 30 day window" part clearly.
SELECT C.cost
, S.trackingnumber
From UPSCost C
join UPSStatus S
ON C.trackingnumber = S.trackingnumber
WHERE MAX(S.date_time) is in a 30 day window of C.event_date_time
You could expand your join and add the date condition to it. Something like this.
SELECT
C.cost,
S.trackingnumber
From UPSCost C
join UPSStatus S
ON(
-- Same tracking number
C.trackingnumber = S.trackingnumber AND
-- status updated within -+30 days from the date found in cost table
s.date_time between DATE_SUB(C.event_date_time, interval 30 day) AND DATE_ADD(C.event_date_time, interval 30 day)
)
Order by S.date_time desc -- latest status first?

A query to calculate registered users per day in a range of 3.5 months

I have a table called "users" where users register and the date of registration is saved as "created_on".
I am looking to write a query that displays how many users have registered on our website daily from 1st of March 2017 till 15th of June 2017.
Unfortunately I can not.
A result that shows like this:
Date Count
01-03-2017 232
02-03-2017 422
03-03-2017 531
...
This problem is a good example of where a calendar table would come in handy. In the query below, I assume that there exists a table containing all the dates from 1st March 2017 until and including 15 June 2017. An inline form of this table would look something like this:
SELECT '2017-03-01' AS Date UNION ALL
SELECT '2017-03-02' UNION ALL
-- ...
SELECT '2017-06-15'
Assuming you have a calendar table, you can just left join it to your current table to get the counts:
SELECT
c.Date, COUNT(t.created_on) AS signup_count
FROM calendar c
LEFT JOIN yourTable t
ON c.Date = t.created_on
GROUP BY Date
Note that a calendar table may be necessary here, because there could be days where no one signed up. In this case, your original table itself does not contain enough information to generate the output you expect.
SELECT created_on,COUNT(*)
FROM users
WHERE created_on BETWEEN '2017-03-01' AND '2017-06-15'
GROUP BY DATE(created_on)
Try above query.
Hope this will help you.

MySQL - Chaining multiple SQL commands

I've looked upon multiple threads but can't seem to find a desirable answer to my question. I am creating a system with a scheduler in it and I need multiple chains in order for the query to return an answer. So here is the scenario. A user needs to register and upon registration, the user is presented with a date.
I have users table where users(obviously) are listed. One column here has the date.
There is also a date table where the dates are stored. Each date can only occupy 30 persons.
The date table also has the availability column. If the date is available, it is labeled 1. If the date has expired (the current date is higher than this date), it is labeled 0.
for example, i have dates Jan 1, Jan 2 and Jan 3 and the current date is Jan 2. Obviously, Jan 1 should be expired. That wouldn't be included in the list so I will set the availability to 0 (yes, manually). There is only Jan 2 and Jan 3. I also need to find if Jan 2 has accommodated 30 people. Else, I need to put him to Jan 3. I got a bunch of parts of the codes that I don't know how to chain.
Expected Output :
query1 (Jan 2 and Jan 3 should be the result)
SELECT * FROM rooms WHERE availability = 0
RoomID Room Date Room Availability
1 Jan 1 1
2 Jan 2 0
3 Jan 3 0
query2 - (count people assigned in specific rooms)
SELECT COUNT(RoomAssigned) FROM users
Users RoomAssigned
Jack 2
Eddie 2
query3 - (system should be able to locate the first room that is available)
if (query2 results<30)
put new user to rooms from result in query1
If ever the chaining I was looking for would possibly be not advisable, I am open for different suggestions. Thanks. :)
Your question seems to be very confused about what tables you have and what's in them, and I don't see how your sample queries can produce the output you showed. But it seems like this query will do what you want:
SELECT *
FROM rooms
WHERE availability = 1
AND roomID NOT IN (
SELECT roomAssigned
FROM users
WHERE availability = 1
GROUP BY roomAssigned
HAVING COUNT(*) >= 30)
ORDER BY roomDate
LIMIT 1
The subquery finds all the rooms that are filled, and then we exclude those from the main query. Then we sort the remaining rooms by date, and select the first one with LIMIT 1.
I think you're going to want something like this:
SELECT r.[RoomID],
r.[Room Date]
FROM rooms r
LEFT JOIN users u
ON r.[RoomID] = u.[RoomAssigned]
WHERE r.[Room Date] >= CURDATE()
GROUP BY r.[RoomId], r.[Room Date]
ORDER BY r.[Room Date], r.[Room Id]
HAVING COUNT(u.[RoomAssigned]) < 30
LIMIT 1
I haven't tested it, so it may require some tweaking. It's like #Barmar's answer, only using a join instead of a nested select. I also checked the availability based on the current date, not the availability column, which I don't think should be stored in the database, because it can be determined based on the Room Date.

Apply right join on a derived table and a calendar table to get all dates between two days

I'm not a db expert. I'm just working on a project where we need to show page views on daily basis between two dates selected by a user from a calendar. I have the following
SQL query that brings brings total number of page views like
SELECT DATE_FORMAT(created_at,'%d %b %Y') as this_date, count(*) as Views
from promotion_insights WHERE f_id = '2' AND p_id = '12'
AND created_at BETWEEN '2012-08-15' AND '2012-08-19'
GROUP BY this_date
Result comes like
----------------------
this_date View
---------------------
15 Aug 2012 3
16 Aug 2012 2
----------------------
I have also a calendar table and a store procedure.calendar table has one column of dates named (datefield). I have already generated dates by calling a stored procedure so don't worry about that. Now what I want, is to make a right join on date basis to above table having (this_date and view columns) with calendar table to show all_dates between selected by the user and put 0 for the dates having 0 views.
count(*) is also making trouble by returing 1 instead 0
Expected output by the join I want is like this:
----------------------
this_date View
---------------------
15 Aug 2012 3
16 Aug 2012 2
17 Aug 2012 0
18 Aug 2012 0
19 Aug 2012 0
----------------------
Any help would be highly highly appreciated.
You can readily do this with a calendar table -- a good idea in most databases.
SELECT DATE_FORMAT(c.date,'%d %b %Y') as this_date, count(*) as Views
from calendar c left outer join
promotion_insights pi
on c.date between '2012-08-15' AND '2012-08-19' and
c.date = pi.created_at
WHERE f_id = '2' AND p_id = '12'
GROUP BY c.date
Note: this assumes that created_at is stored as a date, not a date time. Having a time component could throw off the comparisons.
If you want to do this in SQL you need a row generator to generated a row for each day in your date range, and then join your query to the generated rows to get results for each day in you date range (even if there is no data present for some days in the range).
If you are using Oracle you can use ALL_OBJECTS or DUAL to generate rows. As you are using DATE_FORMAT in your query you seem to use MySQL. A question on how to make a row generator in MySQL was posted before. It states:
Hate to say this, but MySQL is the only RDBMS of the big four that doesn't have this feature.