Need help on mysql date range query - mysql

Below query I am using in my search
I have date range in search, when I searching data between '01/05/2012' AND '31/05/2012' date range but query return all record set.
SELECT t.created date, t.saleid sale_id, u.id user_id, NULL merchant_name, cheque_number cheque_number, u.first_name name, (
SELECT company_name
FROM users
WHERE id = u.my_charity_id)charity_name, t.campaignname website, t.campaignid campaign_id, t.mysoko_discount discount_percentage, t.discount, t.salecommission sale_commission, t.salevalue total_sale_value, t.salestatus sale_status
FROM `transaction` AS `t` , `users` AS `u`
WHERE DATE_FORMAT( t.created, '%d/%m/%Y' )
BETWEEN '01/05/2012' AND '31/05/2012'
AND t.user_id = u.id
LIMIT 0 , 30
I am looking for date range search data but result return all data from table.
date sale_id user_id
2012-04-19 00:00:00 20253305 45
2012-04-11 00:00:00 20253306 68
2012-04-23 00:00:00 20253307 68
2012-04-25 00:00:00 20253308 45
2012-04-27 00:00:00 20253309 45
2012-04-29 00:00:00 20253310 68
2012-04-30 00:00:00 20253311 45
2012-05-01 00:00:00 20253312 45
2012-05-03 00:00:00 20253313 68
2012-04-18 00:00:00 20253314 4
My search is from date range txn search (from / to date)
INPUT date format: DD/MM/YYYY

Do this:
WHERE t.created BETWEEN '2012-05-01 00:00:00' AND '2012-05-31 23:59:59' ...
MySQL will interpret '2012-05-31 23:59:59' as the appropriate DATETIME or TIMESTAMP type, allowing you to take advantage of an index on t.created.
Your problem is that your current query specifies that the string representation of t.created be between the strings '01/05/2012' and '31/05/2012'. Since you string format day-month-year, any date whose day component is between '02' and '30', inclusive, will match. (And the first of any month of May or later will match, etc.)

You could try filtering rows like this:
WHERE t.created >= '2012-05-01'
AND t.created < '2012-06-01'
If the month is specified as an argument, you could use the following calculations in the WHERE clause:
WHERE t.created >= #monthdate
AND t.created < #monthdate + INTERVAL 1 MONTH

One option:
WHERE MONTH(t.created) = 5 AND YEAR(t.created) = 2012

SELECT t.created date, t.saleid sale_id, u.id user_id, NULL merchant_name, cheque_number cheque_number, u.first_name name, (
SELECT company_name
FROM users
WHERE id = u.my_charity_id)charity_name, t.campaignname website, t.campaignid campaign_id, t.mysoko_discount discount_percentage, t.discount, t.salecommission sale_commission, t.salevalue total_sale_value, t.salestatus sale_status
FROM `transaction` AS `t` , `users` AS `u`
WHERE DATE_FORMAT( t.created, '%d/%m/%Y' )
BETWEEN STR_TO_DATE('01/05/2012','%d,%m,%Y') AND STR_TO_DATE('31/05/2012','%d,%m,%Y')
AND t.user_id = u.id
LIMIT 0 , 30

Related

Find gaps in SQL between datetime Ranges within a startdatetime and enddatetime value

im looking for a SQL Query that can deliver me all of the free time Intervals between a given Range for a Table with two datetime Columns (DATE_FROM, DATE_TILL). As a Requirement: All other entries are not overlapped and the only acceptable distance between each interval is 1 Second.
I have found a Solution but this doesnt fill all my Requirements, specially the one where i want to put a given start and end datetime to calculate the missing Intervals if given.
Here is my datatable:
ROW_ID LOCATION_ID DATE_FROM DATE_TILL
1 193 2019-02-01 00:00:00 2019-12-31 23:59:59
2 193 2020-02-01 00:00:00 2020-12-31 23:59:59
3 193 2021-01-01 00:00:00 2021-12-31 23:59:59
4 193 2022-01-01 00:00:00 2022-12-31 23:59:59
5 204 2020-01-01 00:00:00 2021-12-31 23:59:59
And this is my SQL Query, which is from another Solution in this Plattform where i made some requirements changes.
SELECT DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) AS GAP_FROM, DATE_SUB(DATE_FROM,INTERVAL 1 SECOND) AS GAP_TILL
FROM
(
SELECT DISTINCT DATE_FROM, ROW_NUMBER() OVER (ORDER BY DATE_FROM) RN
FROM overlappingtable T1
WHERE
LOCATION_ID = 193 AND
NOT EXISTS (
SELECT *
FROM overlappingtable T2
WHERE T1.DATE_FROM > T2.DATE_FROM AND T1.DATE_FROM < T2.DATE_TILL
)
) T1
JOIN (
SELECT DISTINCT DATE_TILL, ROW_NUMBER() OVER (ORDER BY DATE_TILL) RN
FROM overlappingtable T1
WHERE
LOCATION_ID = 193 AND
NOT EXISTS (
SELECT *
FROM overlappingtable T2
WHERE T1.DATE_TILL > T2.DATE_FROM AND T1.DATE_TILL < T2.DATE_TILL
)
) T2
ON T1.RN - 1 = T2.RN
WHERE
DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) < DATE_FROM
This Query delivers me this result:
GAP_FROM GAP_TILL
2020-01-01 00:00:00 2020-01-31 23:59:59
Which is great, this is the free Interval that i have to deliver between entries that have their ranges and dont overlap.
But I want to set in this Query two Parameters for The Main Range for this entries. One for the startdate and the other for enddate. For this example:
startdate = '2019-01-01 00:00:00'
enddate = '9999-12-31 23:59:59'
For LOCATION_ID = 193 i am missing the gap between the startdate('2019-01-01 00:00:00') and the first DATE_FROM for the first entry('2019-02-01 00:00:00').
The result that i would like to deliver should look like this for LOCATION_ID = 193:
GAP_FROM GAP_TILL
2019-01-01 00:00:00 2019-01-31 23:59:59
2020-01-01 00:00:00 2020-01-31 23:59:59
2023-01-01 00:00:00 9999-12-31 23:59:59
Im really new at SQL and could understand this Query, but i can't develop this further to set these Main Ranges and deliver the missing gaps.
Thanks in Advance
For clarity I would recommend to find the initial gaps, the middle ones, and the ending ones in separate CTEs, as shown below in the b, m, and e CTEs. Then, a simple UNION ALL can combine all of them:
with
p (loc_id, start_date, end_date) as (
select 193, '2019-01-01 00:00:00', '9999-12-31 23:59:59'
),
r as (
select location_id, date_from,
date_add(date_till, interval 1 second) as date_till,
lead(date_from) over(partition by location_id order by date_from) as next_from
from overlappingtable t
cross join p
where t.location_id = p.loc_id
),
b as (
select p.start_date as gap_from, r.date_from as gap_till
from (select * from r order by date_from limit 1) r
cross join p
where p.start_date < r.date_from
),
m as (
select date_till, next_from
from r
where date_till < next_from
),
e as (
select r.date_till, p.end_date
from (select * from r order by date_till desc limit 1) r
cross join p
where r.date_till < p.end_date
)
select * from b
union all select * from m
union all select * from e
order by gap_from
Result:
gap_from gap_till
-------------------- -------------------
2019-01-01 00:00:00 2019-02-01 00:00:00
2020-01-01 00:00:00 2020-02-01 00:00:00
2023-01-01 00:00:00 9999-12-31 23:59:59
See running example atDB Fiddle.
The initial CTE p includes the parameters of the query (loc_id, start_date, end_date) and is added for clarity.
You could join to a sub-query with the start & end datetimes.
Then compare to the previous & next datetimes per location_id.
The previous or next datetimes can be found via the LAG & LEAD functions.
WITH CTE_UNDERLAPS AS
(
SELECT t.*
, LAG(DATE_TILL) OVER (PARTITION BY LOCATION_ID ORDER BY DATE_FROM, DATE_TILL) AS PREV_DATE_TILL
, LEAD(DATE_FROM) OVER (PARTITION BY LOCATION_ID ORDER BY DATE_FROM, DATE_TILL) AS NEXT_DATE_FROM
, l.*
FROM overlappingtable t
JOIN (
SELECT
CAST('2019-01-01 00:00:00' AS DATETIME) AS START_DATETIME
, CAST('9999-12-31 23:59:59' AS DATETIME) AS END_DATETIME
) l ON DATE_FROM >= START_DATETIME AND DATE_TILL <= END_DATETIME
)
SELECT LOCATION_ID
, COALESCE(DATE_ADD(PREV_DATE_TILL,INTERVAL 1 SECOND), START_DATETIME) AS DATE_FROM
, DATE_SUB(DATE_FROM,INTERVAL 1 SECOND) AS DATE_TILL
FROM CTE_UNDERLAPS
WHERE COALESCE(DATE_ADD(PREV_DATE_TILL,INTERVAL 1 SECOND), START_DATETIME) < DATE_FROM
UNION
SELECT LOCATION_ID
, DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) AS DATE_FROM
, COALESCE(DATE_SUB(NEXT_DATE_FROM,INTERVAL 1 SECOND), END_DATETIME) AS DATE_TILL
FROM CTE_UNDERLAPS
WHERE DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) < COALESCE(NEXT_DATE_FROM, END_DATETIME)
ORDER BY LOCATION_ID, DATE_FROM, DATE_TILL
LOCATION_ID
DATE_FROM
DATE_TILL
193
2019-01-01 00:00:00
2019-01-31 23:59:59
193
2020-01-01 00:00:00
2020-01-31 23:59:59
193
2023-01-01 00:00:00
9999-12-31 23:59:59
204
2019-01-01 00:00:00
2019-12-31 23:59:59
204
2022-01-01 00:00:00
9999-12-31 23:59:59
Demo on db<>fiddle here

How should I use NOT IN statement in MySQL

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'

Trying to group query by hours

Trying to group my query by hours and have a '0' if nothing is found.
SELECT
tmhours.hours_value,
COALESCE(cc.countingSheep,0) AS countingSheep
FROM time_hours as tmhours
LEFT JOIN (
SELECT count(*) as countingSheep, company_id, `sales_date`
FROM tbl_cc
WHERE `sales_date` BETWEEN '2019-05-01 00:00:00' AND '2019-05-01 23:59:59' AND company_id = '12345' ) as cc on date_format(sales_date, '%H') = tmhours.hours_value
GROUP BY tmhours.hours_value
The time_hours table just contains 01,02,03,04 .... 22, 23
Based on the above query, I am just getting 0's until 07
So:
01 0
02 0
03 0
04 0
05 0
06 0
07 - 57 (the first match in the DB is 07:14:35) - the 57 is the total count, it's not grouping results
08 0
09 0
...
...
22 0
23 0
I've tried removing the group by inside the inner select, tried moving the date_format = hours_value.
Your problem is that you're not grouping the subquery data by the hour, so your subquery is only returning one row (since it has a COUNT in it). Add grouping to the subquery and it should work fine. Note that you don't need grouping in the outer query as you're not doing any aggregation. Also, since you only want one day's data, you can simplify your WHERE condition using the DATE function.
SELECT
tmhours.hours_value,
COALESCE(cc.countingSheep,0) AS countingSheep
FROM time_hours as tmhours
LEFT JOIN (
SELECT count(*) as countingSheep, date_format(sales_date, '%H') AS sales_hour
FROM tbl_cc
WHERE DATE(`sales_date`) = '2019-05-01' AND company_id = '12345'
GROUP BY sales_hour) as cc ON sales_hour = tmhours.hours_value
You have not aggregated function in the outer query so If you need distinct result use DISTINCT (group by can produce unexpected result ) but in your case seems not necessary
insteadd you missed the group by based on the hour in the inner join
SELECT
tmhours.hours_value,
COALESCE(cc.countingSheep,0) AS countingSheep
FROM time_hours as tmhours
LEFT JOIN ( SELECT count(*) as countingSheep, company_id, date_format(sales_date, '%H')
FROM tbl_cc
WHERE `sales_date` BETWEEN '2019-05-01 00:00:00' AND '2019-05-01 23:59:59'
AND company_id = '12345'
GROUP BY company_id , date_format(sales_date, '%H')
) as cc on date_format(sales_date, '%H') = tmhours.hours_value

How to find gaps in Date of the same group of data - mySQL

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.

How to get results not between two dates?

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