I want to compute scores for some data I have in a MySQL database. The score will be computed as follows:
score = COUNT(purchases MADE BETWEEN NOW() AND (NOW() - 1 WEEK))
+ 0.7 * COUNT(purchases MADE BETWEEN (NOW() - 1 WEEK) AND (NOW() - 2 WEEKS))
+ 0.4 * COUNT(purchases OLDER THAN (NOW() - 2 WEEKS))
I have purchses in a table with a purchase_time column.
Is it possible to do this in MySQL and get output similar to the following?
ORDER_ID SCORE
3 8
4 3
5 15
Thanks
--- EDIT ---
The table structure is:
tblOrder - table
id - primary key
created - time stamp
SELECT orderId,
SUM
(
CASE
WHEN purchase_date > NOW() - INTERVAL 1 WEEK AND purchase_date <= NOW() THEN
1
WHEN purchase_date > NOW() - INTERVAL 2 WEEK AND purchase_date <= NOW() - INTERVAL 1 WEEK THEN
0.7
ELSE
0.3
END
)
FROM mytable
GROUP BY
orderId
Your between and older need to be converted into CASE.
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
At the same time you can rewrite the expression so that specific cases are the factors such as
SELECT SUM(
CASE DATEDIFF(now(),purchase_datetime) DIV 7
WHEN 0 THEN 1
WHEN 1 THEN 0.7
ELSE 0.4
END
)
FROM table
WHERE purchase_datetime < now()
GROUP BY ORDER_ID
SELECT ORDER ID, COUNT(purchases MADE BETWEEN NOW() AND (NOW() - 1 WEEK))
+ 0.7 * COUNT(purchases MADE BETWEEN (NOW() - 1 WEEK) AND (NOW() - 2 WEEKS))
+ 0.4 * COUNT(purchases OLDER THAN (NOW() - 2 WEEKS)) AS SCORE
FROM TABLE
should do the trick, I don't currently have mysql installed so I can't test it.
also, use datediff to find if a date is between the range of dates
SELECT order_id, sum(score) FROM
(
(SELECT Order_id, COUNT(id) AS Score FROM purchases
WHERE purchase_time BETWEEN CURDATE() AND DATE_SUB(CURDATE(),INTERVAL 1 WEEK))
GROUP BY order_id
UNION ALL
(SELECT Order_id, (COUNT(id) * 0.7) AS score FROM purchases
WHERE purchase_time BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 WEEK)
AND DATE_SUB(CURDATE(),INTERVAL 2 WEEK))
GROUP BY order_id
UNION ALL
(SELECT Order_id, (COUNT(id) * 0.4) AS score FROM purchases
WHERE purchase_time < DATE_SUB(CURDATE(),INTERVAL 2 WEEK))
GROUP BY order_id
) s
GROUP BY order_id;
Related
I am using MySQL 5.7 and I need to do queries from a table like
order_id fee created_time
111 10 2020-11-16
222 90 2020-11-01
333 300 2000-10-22
The results should be the total income of last 1 day(yesterday) and last 30 days, like
date_range revenue
1 10
30 400
The column date_range is the last X day before now and I can do this use 'union all':
SELECT 1 AS date_range, SUM(fee) FROM test
WHERE created_time >= SUBDATE(CURRENT_DATE, 1) AND created_time < CURRENT_DATE
UNION ALL
SELECT 30 AS date_range, SUM(fee) FROM test
WHERE created_time >= SUBDATE(CURRENT_DATE, 30) AND created_time < CURRENT_DATE
The queries are quite similar and is it possible to combine them into ONE query instead of using union all?
CREATE TABLE:
CREATE TABLE test (
order_id INT,
fee INT,
created_time DATETIME
)
INSERT VALUES:
INSERT INTO test VALUES (111,10,'2020-11-16'),(222,90,'2020-11-01'),(333,300,'2020-10-22')
SELECT date_range, SUM(fee)
FROM test
CROSS JOIN (SELECT 1 date_range UNION ALL SELECT 30) date_ranges
WHERE created_time >= CURRENT_DATE - INTERVAL date_range DAY
AND created_time < CURRENT_DATE
GROUP BY date_range
UPDATE
You may improve the performance additionally while creating date-generated subquery with interval borders, not interval lengths:
SELECT DATEDIFF(CURRENT_DATE, date_range) date_range, SUM(fee)
FROM test
CROSS JOIN (SELECT CURRENT_DATE - INTERVAL 1 DAY date_range
UNION ALL
SELECT CURRENT_DATE - INTERVAL 30 DAY) date_ranges
WHERE created_time >= date_range
AND created_time < CURRENT_DATE
GROUP BY date_range
DEMO
You can try using case when:
https://dev.mysql.com/doc/refman/5.7/en/case.html
select date_range, sum(fee)
from (
select
case
when created_time between subdate(current_date, 1) and current_date then 1
when created_time between subdate(current_date, 30) and current_date then 30
end case date_range,
fee
from test) t
where date_range is not null
group by date_range
I have tables that contain same field, for example:
p_central_ticket p_south_ticket p_west_ticket
=====================================================================
- t_id - t_id - t_id
- t_open_by - t_open_by - t_open_by
- t_closed_by - t_closed_by - t_closed_by
- t_open_time - t_open_time - t_open_time
- t_closed_time - t_closed_time - t_closed_time
One thing that i expect is output just like this, but definitely for 3 table above in single query:
Name today weekly monthly yearly
=================================================================
test1#random.com 2 10 70 1000
test2#random.com 5 14 60 1234
But, my query right now just for calculate 1 table.
SELECT t_closed_by As Username, ixt_user_type.user_owner As Role,
COUNT( case when t_closed_time > curdate() - interval 1 day THEN 1 END ) as today,
COUNT( case when t_closed_time > curdate() - interval 7 day THEN 1 END ) as weekly,
COUNT( case when t_closed_time > curdate() - interval 1 month THEN 1 END ) as monthly,
COUNT( case when t_closed_time > curdate() - interval 1 year THEN 1 END ) as yearly
FROM p_central_ticket
LEFT JOIN m_event_type ON p_central_ticket.t_req_type = m_event_type.ev_type
LEFT JOIN ixt_user_type ON m_event_type.ev_user_type_target = ixt_user_type.user_type
WHERE t_status = 9
GROUP BY t_closed_by;
My question is, how should i do, to make my query calculate from 3 tables but in single query ?
You can use UNION to merge three tables and apply all your join on that merged table like below -
SELECT t_closed_by As Username, ixt_user_type.user_owner As Role,
COUNT( case when t_closed_time > curdate() - interval 1 day THEN 1 END ) as today,
COUNT( case when t_closed_time > curdate() - interval 7 day THEN 1 END ) as weekly,
COUNT( case when t_closed_time > curdate() - interval 1 month THEN 1 END ) as monthly,
COUNT( case when t_closed_time > curdate() - interval 1 year THEN 1 END ) as yearly
FROM
( select * from p_central_ticket
union
select * from p_south_ticket
union
select * from p_west_ticket
)A
LEFT JOIN m_event_type ON A.t_req_type = m_event_type.ev_type
LEFT JOIN ixt_user_type ON m_event_type.ev_user_type_target = ixt_user_type.user_type
WHERE t_status = 9
GROUP BY t_closed_by
SELECT COUNT(*) FROM `table` WHERE `datetime` > SUBDATE(NOW(), INTERVAL 1 DAY)
This will get number of entries during last day. But is it possible to get number of entries for multiple intervals without having to send variation of this query multiple times (INTERVAL 1 DAY, INTERVAL 1 WEEK, INTERVAL 1 MONTH, ...)?
You need CASE WHEN expression to accomplish that.
SELECT
COUNT(CASE WHEN DATE(`datetime`) >= CURDATE() - INTERVAL 1 DAY AND DATE(`datetime`) < CURDATE() THEN 1 END) AS lastDay,
COUNT(CASE WHEN DATE(`datetime`) >= CURDATE() - INTERVAL 7 DAY AND DATE(`datetime`) < CURDATE() THEN 1 END ) AS lastSevenDays,
COUNT(*) AS lastThirtyDays
FROM `table`
WHERE
DATE(`datetime`) >= CURDATE() - INTERVAL 30 DAY
How to use CASE WHEN expression
Note: If your requirement is to get result of last day, last 7 days and last 30 days then go with this query.
EDIT:
If you have an index on datetime field then the above query will fail to use that index. Please use the query given below in order to utilize the index on datetime.
SELECT
COUNT(CASE WHEN DATE(`datetime`) >= CURDATE() - INTERVAL 1 DAY AND DATE(`datetime`) < CURDATE() THEN 1 END) AS lastDay,
COUNT(CASE WHEN DATE(`datetime`) >= CURDATE() - INTERVAL 7 DAY AND DATE(`datetime`) < CURDATE() THEN 1 END ) AS lastSevenDays,
COUNT(*) AS lastThirtyDays
FROM `table`
WHERE
`datetime` >= (NOW() - INTERVAL 30 DAY - INTERVAL HOUR(NOW()) HOUR - INTERVAL MINUTE(NOW()) MINUTE - INTERVAL SECOND(NOW()) SECOND)
I'm trying to assign a value for events based on it's age using this:
SELECT u.iduser, timeIsImportant
FROM user AS u
LEFT OUTER JOIN(
SELECT action, user_id, action_time,
(IF(bb.action_time < DATE_SUB(CURDATE(), INTERVAL 7 DAY),
(CASE
WHEN bb.action_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 3 MONTH) AND CURDATE() THEN 0.1
WHEN bb.action_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 6 MONTH) AND CURDATE() THEN 0.2
WHEN bb.action_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 12 MONTH) AND CURDATE() THEN 0.4
WHEN bb.action_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 18 MONTH) AND CURDATE() THEN 0.7
WHEN bb.action_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 24 MONTH) AND CURDATE() THEN 1.0
END), 0))
AS timeIsImportant
FROM bigbrother AS bb
ORDER BY bigbrother.uid DESC LIMIT 1)
AS bbb
ON
bbb.user_id = u.iduser AND bbb.action = "C"
WHERE u.iduser = 2;
The idea is that older 'events' on the bigbrother table need to subtract different values from a ranking query calculation. The timeIsImportant value from the query above, would be the agePoints on the following example.
sample data:
row1 row2
------------- -------------
rank: 4.7 rank 4.9
agePoints: 0.1 agePoints 0.4
timedRank: (rank-AgePoints) timedRank: (rank-AgePoints)
-------------------------------------------------------------
SQL: ORDER BY timedRank DESC
row1, row2
SQL: ORDER BY timedRank ASC
row2, row1
I wonder if there's another way to assign the values based on events age, since I'm doing this calculation on every page load in order to rank search results and found that this piece of code slows the overall performance when nested within the search query.
I need to select all rows in my database that were created last month.
For example, if the current month is January, then I want to return all rows that were created in December, if the month is February, then I want to return all rows that were created in January. I have a date_created column in my database that lists the date created in this format: 2007-06-05 14:50:17.
SELECT * FROM table
WHERE YEAR(date_created) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)
AND MONTH(date_created) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
Here's another alternative. Assuming you have an indexed DATE or DATETIME type field, this should use the index as the formatted dates will be type converted before the index is used. You should then see a range query rather than an index query when viewed with EXPLAIN.
SELECT
*
FROM
table
WHERE
date_created >= DATE_FORMAT( CURRENT_DATE - INTERVAL 1 MONTH, '%Y/%m/01' )
AND
date_created < DATE_FORMAT( CURRENT_DATE, '%Y/%m/01' )
If there are no future dates ...
SELECT *
FROM table_name
WHERE date_created > (NOW() - INTERVAL 1 MONTH);
Tested.
Alternatively to hobodave's answer
SELECT * FROM table
WHERE YEAR(date_created) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)
AND MONTH(date_created) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
You could achieve the same with EXTRACT, using YEAR_MONTH as unit, thus you wouldn't need the AND, like so:
SELECT * FROM table
WHERE EXTRACT(YEAR_MONTH FROM date_created) = EXTRACT(YEAR_MONTH FROM CURDATE() - INTERVAL
1 MONTH)
SELECT *
FROM yourtable
where DATE_FORMAT(date_created, '%Y-%m') = date_format(DATE_SUB(curdate(), INTERVAL 1 month),'%Y-%m')
This should return all the records from the previous calendar month, as opposed to the records for the last 30 or 31 days.
Even though the answer for this question has been selected already, however, I believe the simplest query will be
SELECT *
FROM table
WHERE
date_created BETWEEN (CURRENT_DATE() - INTERVAL 1 MONTH) AND CURRENT_DATE();
WHERE created_date >= DATE_ADD(LAST_DAY(DATE_SUB(NOW(), INTERVAL 2 MONTH)), INTERVAL 1 DAY)
AND created_date <= DATE_ADD(LAST_DAY(DATE_SUB(NOW(), INTERVAL 1 MONTH)), INTERVAL 0 DAY)
This worked for me (Selects all records created from last month, regardless of the day you run the query this month)
Alternative with single condition
SELECT * FROM table
WHERE YEAR(date_created) * 12 + MONTH(date_created)
= YEAR(CURRENT_DATE) * 12 + MONTH(CURRENT_DATE) - 1
select fields FROM table
WHERE date_created LIKE concat(LEFT(DATE_SUB(NOW(), interval 1 month),7),'%');
this one will be able to take advantage of an index if your date_created is indexed, because it doesn't apply any transformation function to the field value.
Here is the query to get the records of the last month:
SELECT *
FROM `tablename`
WHERE `datefiled`
BETWEEN DATE_SUB( DATE( NOW( ) ) , INTERVAL 1
MONTH )
AND
LAST_DAY( DATE_SUB( DATE( NOW( ) ) , INTERVAL 1
MONTH ) )
Regards
- saqib
if you want to get orders from last month, you can try using
WHERE MONTH(order_date) = MONTH(CURRENT_DATE()) -1
One more way to do this in:
MYSQL
select * from <table_name> where date_created >= DATE_ADD(NOW(), INTERVAL -30 DAY);
SELECT * FROM table
WHERE YEAR(date_created) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)
AND MONTH(date_created) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)