I have table orders:
id
login_name
success
order_date
1
login1
0
2021-01-05
2
login2
0
2021-01-06
3
login3
0
2021-01-08
4
login1
1
2021-01-04
5
login2
0
2021-01-01
I need to select id, login_name with success=0 for which exist another order with order_date older or younger than 60 days.
The result should be:
1 - login1, 2 - login2, 5 - login2
I have this, but I think that is not a right way:
SELECT id, login_name, COUNT(*)
FROM orders
WHERE success=0
GROUP BY login_name
HAVING COUNT(*) > 1
You can use the EXISTS as follows:
SELECT id, login_name, COUNT(*)
FROM orders r
WHERE success=0
and exists
(select 1 from orders rr
where rr.login = r.login
and abs(datediff(rr.order_date, r.order_date)) <= 60
and rr.id <> r.id
)
If you want orders that appear within 60 days of each other, you can use lag() and lead():
select o.*
from (select o.*,
lag(order_date) over (partition by login_name order by order_date) as prev_order_date,
lead(order_date) over (partition by login_name order by order_date) as lead_order_date
from orders o
) o
where prev_order_date > dateadd(day, -60, order_date) or
next_order_date < dateadd(day, 60, order_date);
Related
Case:
I select an initial date and an end date, it should bring me the movements of all the products in that date range, but if there were movements before the initial date (records in table), I want to obtain the previous sum (prevData)
if the first move is exit 5 and the second move is income 2.
I would have in the first row (prevData-5), second row would have (prevData-5 + 2) and thus have a cumulative.
The prevData would be calculated as the sum of the above, validating the product id of the record, I made the query but if the product has 10 movements, I would do the query 10 times, and how would I identify the sum of another product_id?
SELECT
ik.id,
ik.quantity,
ik.date,
ik.product_id,
#balance = (SELECT SUM(quantity) FROM table_kardex WHERE product_id = ik.product_id AND id < ik.id)
from table_kardex ik
where ik.date between '2021-11-01' and '2021-11-15'
order by ik.product_id,ik.id asc
I hope you have given me to understand, I will be attentive to any questions.
#table_kardex
id|quantity|date|product_id
1 8 2020-10-12 2
2 15 2020-10-12 1
3 5 2021-11-01 1
4 10 2021-11-01 2
5 -2 2021-11-02 1
6 -4 2021-11-02 2
#result
id|quantity|date|product_id|saldo
3 5 2021-11-01 1 20 (15+5)
5 -2 2021-11-02 1 18 (15+5-2)
4 10 2021-11-01 2 18 (8+10-4)
6 -4 2021-11-02 2 14 (15+5-2)
Use MySQL 5.7
If you're using MySQL 8+, then analytic functions can be used here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY date) rn,
SUM(quantity) OVER (PARTITION BY product_id ORDER BY date) saldo
FROM table_kardex
WHERE date BETWEEN '2021-11-01' AND '2021-11-15'
)
SELECT id, quantity, date, product_id, saldo
FROM cte
WHERE rn > 1
ORDER BY product_id, date;
MySQL 5.7
Try this:
SELECT *
FROM (
SELECT product_id,
t1.`date`,
SUM(t2.quantity) - t1.quantity cumulative_quantity_before,
SUM(t2.quantity) cumulative_quantity_after
FROM table t1
JOIN table t2 USING (product_id)
WHERE t1.`date` >= t2.`date`
AND t1.`date` <= #period_end
GROUP BY product_id, t1.`date`, t1.quantity
) prepare_data
WHERE `date` >= #period_start;
The easiest solution is to use the window function SUM OVER to get the running total. In the second step reduce this to the date you want to have this started:
SELECT id, quantity, date, product_id, balance
FROM
(
SELECT
id,
quantity,
date,
product_id,
SUM(quantity) OVER (PARTITION BY product_id ORDER BY id) AS balance
from table_kardex ik
where date < DATE '2021-11-16'
) cumulated
WHERE date >= DATE '2021-11-01'
ORDER BY product_id, id;
UPDATE: You have changed your request to mention that you are using an old MySQL version (5.7). This doesn't support window functions. In that case use your original query. If I am not mistaken, though, #balance = (...) is invalid syntax for MySQL. And according to your explanation you want id <= ik.id, not id < ik.id:
SELECT
ik.id,
ik.quantity,
ik.date,
ik.product_id,
(
SELECT SUM(quantity)
FROM table_kardex
WHERE product_id = ik.product_id AND id <= ik.id
) AS balance
FROM table_kardex ik
WHERE ik.date >= DATE '2021-11-01' AND ik.date < DATE '2021-11-16'
ORDER BY ik.product_id, ik.id;
The appropriate indexes for this query are:
create index idx1 on table_kardex (date, product_id, id);
create index idx2 on table_kardex (product_id, id, quantity);
I have data like this:
select * from date_table;
startdate
2018-08-22
2018-08-24
2018-08-27
2018-08-29
2018-08-31
2018-09-05
2018-09-07
2018-09-10
I have written this query, which only gives days difference
CREATE temporary TABLE if not exists results AS (
select t.startdate, datediff(
(select min(t1.startdate)
from
date_table t1 where
t1.startdate>t.startdate),
t.startdate ) days_diff
from
date_table t) ;
select * from results:
Above query gives result as:
startdate days_diff
2018-08-22 2
2018-08-24 3
2018-08-27 2
2018-08-29 2
2018-08-31 5
2018-09-05 2
2018-09-07 3
2018-09-10
But I want result like:
startdate enddate days_diff
2018-08-22 2018-08-24 2
2018-08-27 2018-08-29 2
2018-08-31 2018-09-05 5
2018-09-07 2018-09-10 3
I'm using MySQL(version 5.6). Kindly let me know if there is any solution for this problem.
Thanks in advance.
You appear to want to enumerate the rows and then aggregate. In pre-8.0 versions, you can use variables. The rest is then aggregation:
select min(start_date) as start_date, max(start_date) as end_date
from (select (#rn := #rn + 1) as seqnum, t.*
from (select t.* from date_table t order by start_date) t cross join
(select #rn := 0) params
) t
group by floor((seqnum - 1) / 2);
I have a table with sell orders and I want to list the COUNT of sell orders per day, between two dates, without leaving date gaps.
This is what I have currently:
SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;
The result I'm getting is as follows:
6 May 1
14 May 4
1 May 5
8 Jun 2
5 Jun 15
But what I'd like to get is:
6 May 1
0 May 2
0 May 3
14 May 4
1 May 5
0 May 6
0 May 7
0 May 8
.....
0 Jun 1
8 Jun 2
.....
5 Jun 15
Is that possible?
Creating a range of dates on the fly and joining that against you orders table:-
SELECT sub1.sdate, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY), "%M %e") as sdate
FROM (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)units
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)tens
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)hundreds
WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%M %e")
GROUP BY sub1.sdate
This copes with date ranges of up to 1000 days.
Note that it could be made more efficient easily depending on the type of field you are using for your dates.
EDIT - as requested, to get the count of orders per month:-
SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth
FROM (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11)months
WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth
You are going to need to generate a virtual (or physical) table, containing every date in the range.
That can be done as follows, using a sequence table.
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
Then, you join this virtual table to your query, as follows.
SELECT IFNULL(orders.Norders,0) AS Norders, /* show zero instead of null*/
DATE_FORMAT(alldates.orderdate, "%M %e") as sdate
FROM (
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq
ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
) AS alldates
LEFT JOIN (
SELECT COUNT(*) as Norders, DATE(date) AS orderdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
) AS orders ON alldates.orderdate = orders.orderdate
ORDER BY alldates.orderdate ASC
Notice that you need the LEFT JOIN so the rows in your output result set will be preserved even if there's no data in your ORDERS table.
Where do you get this sequence table seq_0_to_999999? You can make it like this.
DROP TABLE IF EXISTS seq_0_to_9;
CREATE TABLE seq_0_to_9 AS
SELECT 0 AS seq UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9;
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
You can find an explanation of all this in more detail at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
If you're using MariaDB version 10+, these sequence tables are built in.
First create a Calendar Table
SELECT coalesce(COUNT(O.*),0) as Norders, DATE_FORMAT(C.date, "%M %e") as sdate
FROM Calendar C
LEFT JOIN ORDERS O ON C.date=O.date
WHERE O.date <= NOW() AND O.date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;
I'm calculating
the number of days a reservation took place over every month (for every month since the first record)
A total price based on the total # of days and rate.
INSERT INTO `reservations`
(`id`, `user_id`, `property_id`, `actual_check_in`,`actual_check_out`)
VALUES
(5148, 1, 2, '2014-01-01', '2014-01-10'),
(5149, 1, 2, '2014-02-03', '2014-02-10'),
(5151, 1, 2, '2014-02-02', '2014-02-15'),
(5153, 1, 2, '2014-03-05', '2014-03-10'),
(5153, 1, 2, '2014-02-20', '2014-03-30'),
SELECT
YEAR(month.d),
MONTHNAME(month.d),
r.property_id,
SUM(
DATEDIFF(LEAST(actual_check_out, LAST_DAY(month.d)), GREATEST(actual_check_in, month.d))
) AS days,
SUM(days*p.rate),
MIN(r.actual_check_in) as firstDate,
MAX(r.actual_check_out) as lastDate
FROM reservations as r
LEFT JOIN property as p on r.property_id=p.id
RIGHT JOIN (
select
DATE_FORMAT(m1, '%Y%m%d') as d
from
(
select
(firstDate - INTERVAL DAYOFMONTH(firstDate)-1 DAY)
+INTERVAL m MONTH as m1
from
(
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
where m1<=lastDate
order by m1
) AS month ON
actual_check_in <= LAST_DAY(month.d)
AND month.d <= actual_check_out
GROUP BY user_id, month.d
Troubles I'm having:
getting MySQL to accept a variable for firstDate & lastDate in the joined query
I want to sum the monthly number of days together, for reservations by the same user, for the same month. I'm trying to turn the proper parts into a subquery to calculate that but having trouble..
http://sqlfiddle.com/#!9/71e34/1
I would like to have the results like (if the property rate is 150/day):
DATE | USER | #Days | Total Rate
--------------------------------------
01/2014 | 1 | 9 | 1350
01/2014 | 2 | 0 | 0
02/2014 | 1 | 30 | 4500
02/2014 | 2 | 0 | 0
03/2014 | 1 | 35 | 5250
03/2014 | 2 | 0 | 0
04/2014 | 1 | 0 | 0
04/2014 | 2 | 0 | 0
* # days can be more than the # of days in a month because there might be multiple reservations existing during that month
UPDATE---- This almost solved the problem, but I'm having trouble in the second large select statement to actually calculate the prices properly. The query is only taking in to account the first property rate, and not selecting them as per the join statement. Any help?
select
r.user_id,
DATE_FORMAT(m1, '%b %Y') as date,
(SELECT
SUM(
DATEDIFF(LEAST(actual_check_out, LAST_DAY(m1)), GREATEST(actual_check_in, m1))
) AS numdays
FROM reservations
where actual_check_in <= LAST_DAY(m1)
AND m1 <= actual_check_out
AND user_id=r.user_id
GROUP BY m1) as days,
(SELECT
SUM(
DATEDIFF(LEAST(r.actual_check_out, LAST_DAY(m1)), GREATEST(r.actual_check_in, m1))
) *p.rate
FROM reservations as r
left join property as p
on r.property_id=p.id
where actual_check_in <= LAST_DAY(m1)
AND m1 <= actual_check_out
AND user_id=r.user_id
GROUP BY m1) as price
from (
select ('2015-01-01' - INTERVAL DAYOFMONTH('2015-01-01')-1 DAY) +INTERVAL m MONTH as m1 from (
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
cross join reservations as r
where m1<=CURDATE() group by user_id, m1 order by m1
http://sqlfiddle.com/#!9/36035/21
Still not sure of your request, but the query below may point you to right direction:
SELECT DATE_FORMAT(r.actual_check_in, '%m/%Y') AS mnth, r.user_id,
DATEDIFF(MAX(r.actual_check_out),MIN(r.actual_check_in)) AS days,
DATEDIFF(MAX(r.actual_check_out),MIN(r.actual_check_in))*p.rate AS totalRate
FROM reservations r
JOIN property p ON r.property_id=p.id
GROUP BY DATE_FORMAT(r.actual_check_in, '%m/%Y'), r.user_id;
This returns data like below:
mnth user_id days totalRate
------- ------- ------ -----------
01/2014 1 9 1350
02/2014 1 56 8400
03/2014 1 5 750
http://sqlfiddle.com/#!9/36035/36
select
r.user_id as userId,
DATE_FORMAT(m1, '%b %Y') as date,
(SELECT
SUM(
DATEDIFF(LEAST(actual_check_out, LAST_DAY(m1)), GREATEST(actual_check_in, m1))
) AS numdays
FROM reservations
where actual_check_in <= LAST_DAY(m1)
AND m1 <= actual_check_out
AND user_id=userId
GROUP BY m1) as days,
(SELECT
sum(DATEDIFF(LEAST(r.actual_check_out, LAST_DAY(m1)), GREATEST(r.actual_check_in, m1))*p.rate)
FROM reservations as r
left join property as p
on r.property_id=p.id
where r.actual_check_in <= LAST_DAY(m1)
AND m1 <= r.actual_check_out
AND r.user_id=userId
GROUP BY m1) as price
from (
select ('2015-01-01' - INTERVAL DAYOFMONTH('2015-01-01')-1 DAY) +INTERVAL m MONTH as m1 from (
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
cross join reservations as r
where m1<=CURDATE() group by user_id, m1 order by m1
select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
sponsor_id sum(points) created_at
1 30 2014-12-08 10:54:59
2 25 2014-12-09 05:43:11
3 20 2014-12-09 06:58:40
1 5 2014-12-09 05:56:12
1 34 2014-08-23 10:42:32
here I want to calculate rank of particular sponsor using sponsor_id on daily basis .. I want to build a query that can return me something like as displayed below:
sponsor_id rank created_at
1 1 2014-12-08 10:54:59
1 3 2014-12-09 05:56:12
1 1 2014-08-23 10:42:32
I think I can use sub query like
select *
from (select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
) as t
where t.sponsor_id = 1
but how to calulate rank here.
Try this:
SELECT sponsor_id, points, created_at,
IF(#dte=#dte:=DATE(created_at), #rank:=#rank+1, #rank:=1) AS rank
FROM (SELECT user_id AS sponsor_id, SUM(points) points, created_at
FROM points_history
WHERE created_at BETWEEN '2014-08-12' AND '2015-08-12' AND
transaction_type = "debit"
GROUP BY user_id, DATE_FORMAT(created_at,"%d %M %Y")
ORDER BY DATE(created_at), SUM(points) DESC
) AS A, (SELECT #rank:=0, #dte:='') AS B
ORDER BY DATE(created_at), points DESC;