I am trying to use NOT IN statement with MySQL. However, I get 0 row with code below (no syntax error). I am sure there should be more than 0 row with the statement. What syntax should I adjust?
SELECT DISTINCT member_id
FROM client_payments
INNER JOIN client_purchase_records ON client_purchase_records.id = client_payments.purchase_record_id
WHERE status = 1
AND client_payments.created_at > '2021-10-28 00:00:00'
AND client_payments.created_at < '2021-10-31 23:59:00'
NOT IN(
SELECT DISTINCT member_id
FROM client_payments
INNER JOIN client_purchase_records ON client_purchase_records.id = client_payments.purchase_record_id
WHERE status = 1
AND client_payments.created_at > '2020-9-30 00:00:00'
AND client_payments.created_at < '2021-10-27 23:59:00'
);
Difference about two query is mainly about created_at column, I want to do "set difference operation" with period A(2021-10-28 00:00:00 - 2021-10-31 23:59:00 )and period B(2020-9-30 00:00:00 - 2021-10-27 23:59:00)
I want to query out member_id who pay during 2020-9-30 00:00:00 - 2021-10-27 23:59:00
Subtract with member_id who pay during 2021-10-28 00:00:00 - 2021-10-31 23:59:00
Finally I get member_id who pay during 2021-10-28 00:00:00 - 2021-10-31 but not pay during 2020-9-30 00:00:00 - 2021-10-27 23:59:00 ( new member_id never show before)
no syntax error
The error is in the logic.
Your condition, after adding the parenthesis according to operators priority, looks like
AND ( (client_payments.created_at < '2021-10-31 23:59:00') NOT IN ( {subquery} ) )
I.e. the result of comparing client_payments.created_at < '2021-10-31 23:59:00' (which is 0, 1 or NULL) is searching in the subquery output which is obviously illogical.
You presumably want to be saying AND member_id NOT IN (...your subquery...); as is, it is using the previous condition instead of member_id.
Also, it looks like you want >= and <=, not > and <.
I would do this like so instead:
SELECT member_id
FROM client_payments
INNER JOIN client_purchase_records ON client_purchase_records.id = client_payments.purchase_record_id
WHERE status = 1
AND client_payments.created_at >= '2021-09-30 00:00:00'
AND client_payments.created_at <= '2021-10-31 23:59:00'
GROUP BY member_id
HAVING MIN(client_payments.created_at) >= '2021-10-28 00:00:00'
Related
I need to count all articles between dates. I used IFNULL(COUNT(*), 0) which returns null.
SELECT posts_count, dates.fulldate
FROM dates
LEFT JOIN
(SELECT IFNULL(COUNT(post_id), 0) AS posts_count, DATE_FORMAT(post_created_at, "%Y-%m-%d") AS pdate
FROM posts
WHERE DATE_FORMAT(post_created_at, "%Y-%m-%d") BETWEEN NOW() - INTERVAL 31 DAY AND NOW()
GROUP BY DATE_FORMAT(post_created_at, "%Y-%m-%d")) t
ON t.pdate = dates.fulldate
WHERE dates.fulldate BETWEEN CURDATE() - INTERVAL 31 DAY AND CURDATE() ORDER BY dates.fulldate ASC;
The result is:
NULL | 2020-05-20
NULL | 2020-05-21
NULL | 2020-05-22
7634 | 2020-05-23
51224 | 2020-05-24
The result I want its:
0 | 2020-06-16
1233 | 2020-06-15
4354 | 2020-06-14
0 | 2020-06-13
0 | 2020-06-12
Your query seems more complicated than it needs to be. I don't see the point for the subquery that groups post by date and for filtering twice on the dates.
I think the logic you want is:
select count(p.post_id) post_count, d.fulldate
from dates d
left join posts p
on p.post_created_at >= d.fulldate
and p.post_created_at < d.fulldate + interval 1 day
where d.fulldate >= current_date - interval 1 month
group by d.fulldate
order by d.fulldate
I would expect that this should be much more efficient than you original code, especially because not date function is applied on the post date column. I would recommend an index on posts(post_created_at) to take full advantage of this code.
You need to do the IFNULL() in the main query, not the subquery. The subquery isn't returning any rows for the missing dates, so there isn't even a null count to convert to 0.
SELECT IFNULL(posts_count, 0) AS posts_count, dates.fulldate
FROM dates
LEFT JOIN
(SELECT COUNT(*) AS posts_count, DATE_FORMAT(post_created_at, "%Y-%m-%d") AS pdate
FROM posts
WHERE DATE_FORMAT(post_created_at, "%Y-%m-%d") BETWEEN NOW() - INTERVAL 31 DAY AND NOW()
GROUP BY DATE_FORMAT(post_created_at, "%Y-%m-%d")) t
ON t.pdate = dates.fulldate
WHERE dates.fulldate BETWEEN CURDATE() - INTERVAL 31 DAY AND CURDATE()
ORDER BY dates.fulldate ASC;
I am confused with a basic query, I have 2 tables employees and employee_time_off. I wanted to fetch all those employees who are not off on any specific date.
What I have tried is,
SELECT
employees.id,
employees.FIRST_NAME
FROM `employees`
LEFT JOIN `employee_time_off` ON `employees`.`id` = `employee_time_off`.`employee_id`
AND `START_DATE` < '2019-06-30 00:00:00' AND `END_DATE` > '2019-06-30 12:59:59'
START_DATE is actually when the leaves start, END_DATE is when the leave ends.
So, in this case, employee with id=1 shouldn't be in the result set. But the result set gets all the employees from 1 to 6.
How about NOT EXISTS?
SELECT e.*
FROM employees e
WHERE NOT EXISTS (SELECT 1
FROM employee_time_off eto
WHERE eto.employee_id = e.id AND
eto.start_date <= '2019-06-30' AND
eto.end_date >= '2019-07-01'
);
Note: I'm not sure if the end_date should be '2019-06-30' or '2019-07-01'. Your question doesn't have enough information (off the whole day or any part of the day).
Your version returns all employees because you are using a LEFT JOIN.
from the SQL, we can't tell which table has the start_date and end_date columns. we suspect its the employee_time_off table. to reduce ambiguity, qualify all column references
We can add a condition to the WHERE clause, to throw out the rows where we find a matching row in time off. Also looks like we need to do <= and >= comparisons to get the result we're after.
SELECT e.id
, e.first_name
FROM employees e
LEFT
JOIN employee_time_off o
ON o.employee_id = e.id
AND o.start_date <= '2019-06-30 00:00:00'
AND o.end_date >= '2019-06-30 23:59:59'
WHERE o.employee_id IS NOT NULL
Note that with that query, we would get a different result if we had these rows in the time_off table:
employee_id start_date end_date
----------- ---------------- -------------------
1 2019-06-30 00:00:00 2019-06-30 07:24:59
1 2019-06-30 07:25:00 2019-06-30 23:59:59
What should the query return when time off table contains a row like this?
employee_id start_date end_date
----------- ---------------- -------------------
2 2019-06-29 00:00:00 2019-06-30 11:14:59
The specification isn't clear.
Also, we tend to do datetime overlap checks with conditions like
WHERE foo >= '2019-06-30 00:00'
AND foo < '2019-06-30 00:00' + INTERVAL 1 DAY
I have a list of dates that have gaps for an execution of a promotion.
id promotion_name period value
8495115 Cash_Discount1 2016-11-01 10.00
8495116 Cash_Discount1 2016-12-01 20.00
8491724 Cash_Discount1 2017-01-01 10.00
8479109 Cash_Discount1 2017-02-01 20.00
8459125 Cash_Discount1 2017-03-01 40.00
8491649 Cash_Discount1 2017-06-01 30.00
8491648 Cash_Discount1 2017-07-01 50.00
8491647 Cash_Discount1 2017-08-01 70.00
8491646 Cash_Discount1 2017-09-01 80.00
The period in the above table denotes the start date of the execution of the promotion and it runs for a month.
Hence, the first row means the cash discount runs from 1-11-2016 till
30-11-2016.
I need the same data in the below mentioned format.
promotion_name start_date end_date value
Cash_Discount1 2016-11-01 2017-03-31 100.00
Cash_Discount1 2017-06-01 2017-09-30 230.00
Whenever there is a gap, that has to appear as a separate row. can somebody please help me with this as any number of self joins is not giving me the result.
i have tried using this for starters but, i am nowhere close to the result.
SELECT p.id
, p.promotion_name
, p.period AS start_date
, q.period AS end_date
, p.value AS spend
FROM table p
LEFT
JOIN table q
ON p.id = q.id
AND p.promotion_name = q.promotion_name
AND p.period = DATE_SUB(q.period,INTERVAL 1 MONTH)
I am in a situation where i do not know what to search for.
Any Help is appreciated.
select
ts.promotion_name,
min(t.period) as date_start,
max(t.period) as date_end,
sum(t.value) as spend
from mytable ts
left join mytable t1
on t1.promotion_name = ts.promotion_name
and t1.period = ts.period - interval 1 month
join mytable t
on t.promotion_name = ts.promotion_name
and t.period >= ts.period
and t.period <= (
select min(t2.period)
from mytable t2
left join mytable t3
on t3.promotion_name = t2.promotion_name
and t3.period = t2.period + interval 1 month
where t2.promotion_name = ts.promotion_name
and t2.period >= ts.period
and t3.period is null
)
where ts.promotion_name = 'Cash_Discount1'
and t1.period is null
group by ts.id
order by date_start
Demo: https://www.db-fiddle.com/f/7Xx5bSQmRUGzUCLyB4rKBf/1
The idea of that query is first to find the first row of a consecutive group with
select ts.*
from mytable ts
left join mytable t1
on t1.promotion_name = ts.promotion_name
and t1.period = ts.period - interval 1 month
where ts.promotion_name = 'Cash_Discount1'
and t1.period is null
Demo: https://www.db-fiddle.com/f/fKaNQPkgZqbTfY3Je6Kz4/0
and get the last date of the group with a subquery
select min(t2.period)
from mytable t2
left join mytable t3
on t3.promotion_name = t2.promotion_name
and t3.period = t2.period + interval 1 month
where t2.promotion_name = ts.promotion_name
and t2.period >= ts.period
and t3.period is null
Demo: https://www.db-fiddle.com/f/hSNZwjDuL1nf2NzPoms5JG/0
Then you need another join with a range condition
and t.period >= ts.period -- start_date
and t.period <= (<subquery>) -- end_date
and use aggregation over the matching rows.
I have two dates that property has been blocked on that two days like start date 2017-01-20 and end end 2017-01-25. Between these two dates property has been blocked. When I search with these two dates except these two dates I want to get the remaining dates.
Here I have tried by searching the web in MySQL like
select `pd`.*, `pa`.*
from `property_details` as `pd` left join
`property_gallery` as `pg`
on `pg`.`property_id` = `pd`.`property_id` left join
`property_preblock_details` as `pbd`
on `pbd`.`property_id` = `pd`.`property_id` left join
`property_amenity` as `pa`
on `pa`.`property_id` = `pd`.`property_id`
where `pbd`.`start_date` >= '2017-01-20' and `pbd`.`end_date` <= '2017-01-25'
and
select pd.* from
`property_details` as `pd` left join
`property_gallery` as `pg` on `pg`.`property_id` = `pd`.`property_id`
left join `property_preblock_details` as `pbd`
on `pbd`.`property_id` = `pd`.`property_id`
left join `property_amenity` as `pa`
on `pa`.`property_id` = `pd`.`property_id`
where `pbd`.`start_date` NOT BETWEEN CAST('2017-01-20' AS DATE) and CAST('2017-01-25' AS DATE)
AND `pbd`.`end_date` NOT BETWEEN CAST('2017-01-20' AS DATE) and CAST('2017-01-25' AS DATE)
But I'm getting results as between dates.
I don't want to get between dates results, I want to get not between dates results.
Property_rate
id start_date end_date prop_id
1 2017-01-20 2017-01-25 1
2 2017-01-26 2017-01-27 1
3 2017-01-26 2017-01-28 2
Property
prop_id prop_name
1 test1
2 test2
If I select 2017-01-20 and 2017-01-25, I want to get 'test2' property details only. Not 'test1' details.
You've very close. Your query says this:
where `pbd`.`start_date` >= '2017-01-20' /* close, but wrong */
and `pbd`.`end_date` <= '2017-01-25'
What you want is this:
where ( `pbd`.`start_date` < '2017-01-20'
or `pbd`.`end_date` > '2017-01-25')
It can also be written
where not ( `pbd`.`start_date` >= '2017-01-20'
and `pbd`.`end_date` <= '2017-01-25')
BEWARE Your query may have another mistake in it. If your start_date and end_date columns have the DATE data type, you're all set. But if they have another data type, such as DATETIME or TIMESTAMP, your query has another problem. To catch all the rows with date/time values on the days 20-Jan-2017 -- 25-Jan-2017 you need this
where `pbd`.`start_date` >= '2017-01-20' /*dates IN range */
and `pbd`.`end_date` < '2017-01-26'
Notice I moved the desired end date one day later and used < instead of <=. This works correctly for
2017-01-25 23:59 should be in range
2017-01-26 00:00 should be out of range
2017-01-26 00:01 should be out of range
Try:
SELECT *
FROM `Table`
WHERE `DATECOLUMN` NOT
IN (
SELECT 'ID'
FROM `Table`
WHERE `DATECOLUMN`
BETWEEN (
'2017-02-10 00:00:00'
)
AND (
'2017-09-14 00:00:00'
)
)
LIMIT 0 , 30
I have a query in which I would like to return the number of users who have logged in for the month without repeating the record in the next month.
If a user has logged in April and May, it only shows one record for April. This is what I have so far.
SELECT DISTINCT (a.userid), EXTRACT(MONTH FROM a.loginTime) as month
FROM login_audit a LEFT JOIN user u on u.userid = a.userid
WHERE a.loginTime <= '2012-12-31 11:59:59'
AND a.loginTime >= '2012-01-01 00:00:00'
GROUP BY month
So far the records are returning
userid month
1 1
2 1
1 2
3 2
In this scenario, user 1 is coming up for both January and Februray. I would like it to ommit that record. Either that or have it accumulated. Like so:
Either
userid month
1 1
2 1
3 2
Or
userid month
1 1
2 1
1 2
2 2
3 2
I hope this made sense. Please ask me anything if you'd like any further clarifications. Thanks a lot!
Don't see where you need table user...
For first "wanted scenario" :
SELECT
a.userid,
MIN(EXTRACT(MONTH FROM a.loginTime)) as month
FROM login_audit a
WHERE a.loginTime <= '2012-12-31 11:59:59' AND a.loginTime >= '2012-01-01 00:00:00'
GROUP BY a.userid
I would use this approach.
SELECT DISTINCT (a.userid), EXTRACT(MONTH FROM a.loginTime) as month
FROM login_audit a
WHERE a.loginTime <= '2012-12-31 11:59:59'
AND a.loginTime >= '2012-01-01 00:00:00'
and not exists
(select userid
from login_audit
where login_audit.user_id = a.user_id
and carry on with date range for the following month
)