I am implementing the cashback functionality with expiry feature. I am trying to redeem the partial amount based on early expiry date. I've already ordered the rows based on expiry date with the following mysql command.
SELECT * FROM `cashback` WHERE `user_id` = 1 and `used`= 'NO' AND IF(CONCAT(`point_expiry`) !='0000-00-00 00:00:00', `point_expiry` >= NOW(), NOW()) ORDER BY (case when CONCAT(`point_expiry`) = '0000-00-00 00:00:00' then 9999
else 1
end) ASC, `point_expiry` ASC
And the output for the following will be
id
amount
point_expiry
used
user_id
3
30
2023-02-24 00:00:00
NO
1
1
20
2023-02-25 00:00:00
NO
1
2
50
0000-00-00 00:00:00
NO
1
Now i want to redeem the value based on the above query result
Let say i want to redeem 35$ for the above result and the expected result will be
id
amount
point_expiry
used
used_amount
3
30
2023-02-24 00:00:00
NO
30
1
20
2023-02-25 00:00:00
NO
5
Here used_amount column represent the specific redeem value($35) redeemed based on amount column
Much appreciate your help!
This uses SUM(amount) OVER(ORDER BY ...) to calculate a running total and compares it to the balance -
SELECT *
FROM (
SELECT
`id`,
`amount`,
`point_expiry`,
`used`,
`amount` - GREATEST(SUM(`amount`) OVER (ORDER BY IF(`point_expiry` = '0000-00-00 00:00:00', 1, 0) ASC, `point_expiry` ASC, id ASC) - /* Here is your amount --> */ 35, 0) AS `used_amount`
FROM `cashback`
WHERE (`point_expiry` >= NOW() OR `point_expiry` = '0000-00-00 00:00:00')
AND `used` = 'NO'
AND `user_id` = 1
) t
WHERE `used_amount` > 0;
Related
First question on here, so i will try my best to be clear.
I have 2 tables:
"TABLE1" which contains a record for each stock code and a list of attributes.
In TABLE 1 there is just one record for each stock_code
"TABLE2" which contains a log of changes to attributes of products, over time.
"TABLE2" contains the following fields:.
stock_code
stock_attribute
old_value
new_value
change_date
change_time
TABLE 2 has multiple entries ofr each stock_code.
Every time a stock item is change, another entry is made in Table2, with the attribute that has changed, the change date, time, old value and new value.
I want to create a query which will result in a table that has one record for each stock_code (from TABLE 1), and a column for each week over past year, with the value in each field being the last recorded "new_val" for that week (From TABLE 2)
I have tried
SELECT a.`stcode`, b.`week1`, b.`week2`, b.`week3`, b.`week4` etc. etc.
from (SELECT stcode, )as a
LEFT JOIN (SELECT stcode,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY) AND DATE_SUB(CURDATE(),INTERVAL 357 DAY) THEN newval END)week1,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 356 DAY) AND DATE_SUB(CURDATE(),INTERVAL 350 DAY) THEN newval END)week2,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 349 DAY) AND DATE_SUB(CURDATE(),INTERVAL 343 DAY) THEN newval END)week3,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 342 DAY) AND DATE_SUB(CURDATE(),INTERVAL 336 DAY) THEN newval END)week4,
(etc
etc
etc
FROM (SELECT * from TABLE 2 ORDER BY "chngdate" DESC, "chngtime" DESC )as sub) as b ON b.stcode = s.stcode
ORDER BY stcode ASC
The trouble is with this, i get multiple lines for a stock_code which has mutliple entries....
for example, for stock_code abc123 the result i get is
STCODE WEEK1 WEEK2 WEEK3 WEEK4 WEEK5 WEEK6
abc123 null null 4 null null null
abc123 2 null null null null null
abc123 null null null null 3 null
what i WANT is this:
STCODE WEEK1 WEEK2 WEEK3 WEEK4 WEEK5 WEEK6
abc123 2 null 4 null 3 null
I have also tried the following, but teh query took so long, it never finished (there were 52 derived tables!)
SELECT a.`stcode`, w1.`new_value`, w2.`new_value`, w3.`new_value`, w4.`new_value` etc. etc.
from (SELECT stcode, )as a
LEFT JOIN (SELECT stcode,
LEFT JOIN (SELECT stcode, depot, fieldname, chngdate, chngtime, newval from STDepotAmendmentsLog WHERE chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY) AND DATE_SUB(CURDATE(),INTERVAL 357 DAY) ORDER BY "chngdate" DESC, "chngtime" DESC)as w1 on s.stcode = w1.stcode
etc for week 2, 3, 4 etc etc
You could do the following:
Find the greatest date for each "week"
Find the rows corresponding to those dates
Use conditional aggregation to convert rows into columns
Here is a rough outline of the code. It assumes that e.g. if today is 2020-03-03 then week 52 is from 2020-02-26 to 2020-03-03. Adjust if necessary:
SELECT t.stock_code
, MAX(CASE WHEN weeknum = 51 THEN new_value END) AS week01
, MAX(CASE WHEN weeknum = 50 THEN new_value END) AS week02
, MAX(CASE WHEN weeknum = 1 THEN new_value END) AS week51
, MAX(CASE WHEN weeknum = 0 THEN new_value END) AS week52
FROM table2 AS t
JOIN (
SELECT stock_code
, DATEDIFF(CURRENT_DATE, change_date) div 7 AS weeknum -- count multiples of 7
, MAX(change_date) AS greatest_date
GROUP BY stock_code, weeknum
FROM table2
) AS a ON t.stock_code = a.stock_code AND t.change_date = a.greatest_date
GROUP BY t.stock_code
I need your help with mysql query.
I have one table 'metrics':
create table metrics
(
guid binary(16) not null
primary key,
entry_guid binary(16) not null,
customer_guid binary(16) null,
metrics varchar(30) not null,
value int not null,
`_created` timestamp null,
`_updated` timestamp null
);
So, I'm trying to do something like this:
SELECT t1.entry_guid as entry_guid, SUM(t1.`value`) as last_week, SUM(t2.`value`) as last_month
FROM metrics as t1, metrics as t2
WHERE t1.`_created` > NOW() - INTERVAL 7 DAY OR t2.`_created` > NOW() - INTERVAL 1 MONTH
GROUP BY t1.entry_guid
But in result i get identical strange result of using SUM() function
entry_guid last_week last_month
1 4613 4613
2 207 207
3 6003 6003
4 9108 9108
Moreover result of SUM() func is strange, because I have only 300 rows and 'value' field in each row is equal 1, so max sum must be very little.
So, the query
SELECT t1.entry_guid as entry_guid, SUM(t1.`value`) as sum
FROM metrics as t1
GROUP BY t1.entry_guid
gives me
entry_guid sum
0x34303535636637643538396665633265 21
0x34313830656231666665393131326635 21
0x34336537663033653963303437356165 1
0x34363061653730313738313263386264 44
I need to get SUM('value') from one table, but with different conditions.
Can you show me how? Thank you in advance.
Use conditional aggregation:
SELECT m.entry_guid as entry_guid,
SUM(CASE WHEN m.`_created` > NOW() - INTERVAL 7 DAY THEN t1.`value` ELSE 0 END) as last_week,
SUM(CASE WHEN m.`_created` > NOW() - INTERVAL 1 MONTH THEN t2.`value` ELSE 0 END) as last_month
FROM metrics m
GROUP BY m.entry_guid;
I am trying to calculate data from my database but first I've noticed strange behavior from the results I get, second, I have trouble making a request that take into account refills.
I have a table with :
Name - DateTime - content
I want to group by day the rows and select the difference of the number to have the consumption.
For example :
Name - DateTime - Content
Foo - 22-04-2018 6:00 - 120
Foo - 22-04-2018 10:00 - 119
Foo - 22-04-2018 16:00 - 118
The content has decreased, the result should be -2.
Output of my request = -2
Another example :
Name - DateTime - Content
Foo - 23-04-2018 6:00 - 50
Foo - 23-04-2018 10:00 - 90
Foo - 23-04-2018 16:00 - 120
Here we can notice that the number has increased. It means that instead of a consumption, we have refilled the reserve and the content has increased.
The result should be -70.
Output of my request : 30
My request :
SELECT day,
Abs(Sum(diffn)) AS totN
FROM (SELECT Date(datetime) AS day,
Max(content) - Min(content) AS diffN
FROM logs
WHERE NAME = 'Foo'
AND datetime >= '2018-04-22 00:00:00'
AND datetime <= '2018-04-23 00:00:00'
GROUP BY Date(datetime)) a
GROUP BY day;
But for the second example I have 30 as a result instead of 70, I don't know why...
I would like your help to change my request and take refills into account so that I get the results I want.
Thanks!
You need to determine the Prefix by comparing the highest and the lowest value, the time (hour) included. I'm using the 'CASE' function with two subqueries here.
Maybe you'll need to turn the year-month-day around, because I'm using the german datetime-format.
SET #datetime = '2018-04-22';
SELECT date(datetime) as day
,(CASE WHEN
(SELECT content FROM logs WHERE date(datetime) = #datetime ORDER BY datetime LIMIT 1)
>
(SELECT content FROM logs WHERE date(datetime) = #datetime ORDER BY datetime desc LIMIT 1)
THEN min(content) - max(content)
ELSE max(content) - min(content) END) as diffN
FROM logs
WHERE Name = 'Foo' AND date(datetime) = #datetime
GROUP BY day(datetime)
ORDER BY datetime
;
This should do the job:
SELECT day(datetime) as day, max(content) - min(content) as diffN
FROM logs
WHERE Name = 'Foo'
AND datetime >= '2018-04-23 00:00:00'
AND datetime <= '2018-04-24 00:00:00'
GROUP BY day(datetime)
Also, change the date filters it should be betweeen 23 and 24.
It might be that you need to establish the first and last datetime and their associated content. For example
drop table if exists t;
create table t (name varchar(3), dt datetime, content int);
insert into t values
('Foo' , '2018-04-22 06:00:00', 120),
('Foo' , '2018-04-22 10:00:00', 119),
('Foo' , '2018-04-22 16:00:00', 118),
('Foo' , '2018-04-23 06:00:00', 50),
('Foo' , '2018-04-23 10:00:00', 90),
('Foo' , '2018-04-23 16:00:00', 120);
select s.name,lastinday,firstinday,lastinday - firstinday
from
(
select name,dt, content lastinday
from t
where dt = (Select max(dt) from t t1 where t1.name = t.name and date(t1.dt) = date(t.dt))
) s
join
(
select name,dt, content firstinday
from t
where dt = (Select min(dt) from t t1 where t1.name = t.name and date(t1.dt) = date(t.dt))
) t
on t.name = s.name and date(t.dt) = date(s.dt);
+------+-----------+------------+------------------------+
| name | lastinday | firstinday | lastinday - firstinday |
+------+-----------+------------+------------------------+
| Foo | 118 | 120 | -2 |
| Foo | 120 | 50 | 70 |
+------+-----------+------------+------------------------+
2 rows in set (0.00 sec)
Why are you grouping it second time:
Ideally this should work:
SELECT Date(datetime) AS day,
Max(content) - Min(content) AS diffN
FROM logs
WHERE NAME = 'Foo'
AND datetime >= '2018-04-22 00:00:00'
AND datetime <= '2018-04-23 00:00:00'
GROUP BY Date(datetime)
Result of this query will contain only 2 rows - 1 for 22th and 1 for 23rd day. There is no need of grouping it again by day
I want to get some data from my database for the past 31 days. When column 'finalized' is NULL I want to get data from column 'today', but if column 'finalized' isn't NULL I want to get data from column 'finalized'. I always want to get the last entered row.
TABLE MyEarnings
id INT(11) AI
date datetime
today decimal(4,2) NULL
finalized decimal(4,2) NULL
id date today finalized
-----------------------------------------------
6 2016-02-04 04:52:00 0.39 NULL
5 2016-02-03 12:34:00 NULL 19.74
4 2016-02-03 12:33:00 15.96 NULL
3 2016-02-03 12:32:00 12.32 NULL
2 2016-02-02 15:16:00 NULL 9.16
1 2016-02-02 14:29:00 2.20 NULL
SQL
SELECT
date,
CASE
WHEN finalized=NULL
THEN today
WHEN finalized!=NULL
THEN finalized
END
AS earn
FROM MyEarnings
GROUP BY DATE_FORMAT(date, '%Y-%m-%d')
ORDER BY date ASC
LIMIT 0 , 31
This is what I end up with
date earn
---------------------------
2016-02-02 00:00:00 NULL
2016-02-03 00:00:00 NULL
2016-02-04 00:00:00 NULL
What I'm looking to get
date earn
----------------------------
2016-02-02 00:00:00 9.16
2016-02-03 00:00:00 19.74
2016-02-04 00:00:00 0.39
EDIT
I also want to get a summary for each month of all values in 'finalized'-column with the max 'id' for each day.
You can't compare null with !=, you should use is null and is not null like this:
SELECT
date,
CASE
WHEN finalized is null THEN today
ELSE finalized
END AS earn
FROM MyEarnings
GROUP BY DATE_FORMAT(date, '%Y-%m-%d')
ORDER BY date ASC LIMIT 0 , 31
Also, if your first condition is when its null, you don't need to check if its not null , ELSE will be enough since the opposite of null is not null
use CASE WHEN like this:
CASE
WHEN finalized IS NULL
THEN today
ELSE finalized
END
AS earn
Replace query:
SELECT date, (CASE
WHEN finalized IS NULL THEN today
ELSE finalized
END;
) AS earn
FROM MyEarnings
GROUP BY DATE_FORMAT(date, '%Y-%m-%d')
ORDER BY date ASC LIMIT 0,31
You need conditional aggregation, but it is a bit tricky. I think this does what you want:
SELECT DATE_FORMAT(date, '%Y-%m-%d'),
COALESCE(MAX(finalized), MAX(today)) as earn
FROM MyEarnings
GROUP BY DATE_FORMAT(date, '%Y-%m-%d')
ORDER BY date ASC
LIMIT 0 , 31;
This returns the maximum value of today. You may want the most recent value. If so, the simplest method is probably the GROUP_CONCAT()/SUBSTRING_INDEX() method:
SELECT DATE_FORMAT(date, '%Y-%m-%d'),
COALESCE(MAX(finalized),
SUBSTRING_INDEX(GROUP_CONCAT(today ORDER BY date DESC), ',', 1) + 0
) as earn
FROM MyEarnings
GROUP BY DATE_FORMAT(date, '%Y-%m-%d')
ORDER BY date ASC
LIMIT 0 , 31;
It is a bit yucky to convert numbers to strings and back for this purpose. Alternative methods require additional joins or using variables.
Using greatest id to determine last row added (cons: stretching traditional use of id):
SELECT DATE(me.date) date,
COALESCE(me.finalized,me.today) earn
FROM MyEarnings me
JOIN (
SELECT MAX(id) max_id
FROM MyEarnings
GROUP BY DATE(date)
ORDER BY DATE(date)
LIMIT 31
) o
ON o.max_id = me.id
Using greatest date to determine last row added (cons: possible dupes):
SELECT DATE(me.date) date,
COALESCE(me.finalized,me.today) earn
FROM MyEarnings me
JOIN (
SELECT MAX(date) max_date
FROM MyEarnings
GROUP BY DATE(date)
ORDER BY DATE(date)
LIMIT 31
) o
ON o.max_date = me.date
In SQL I am trying to get a return of 3 categories.
Unique Dates
Number of people that visit on the day in 1.
Number of times visit on the day in 1 AND visit on a specific date
I have this for my SQL
SELECT
DISTINCT DATEADD(dd, 0, DATEDIFF(dd, 0, visit_date)) as theday,
(SELECT count(DISTINCT visit_id)) as uniques,
(SELECT count(DISTINCT visit_id)
FROM visitors
WHERE visit_id IN (
SELECT DISTINCT visit_id
FROM visitors
WHERE (visit_date >= '2014-03-29 00:00:00.000'
AND visit_date <= '2014-03-30 00:00:00.000')
)
) as visited_again
FROM visitors
WHERE visit_date >= '2014-01-01 00:00:00.000'
GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, visit_date))
ORDER BY theday DESC;
And it works great for giving me #1 and #2 but the third one is only giving me one unique number that repeats for each date.
The SQL above returns results that look like:
theday | uniques | visited_again
-------------+---------------+------------------
2014-03-30 | 900000 | 84450
2014-03-29 | 789433 | 84450
2014-03-28 | 823010 | 84450
2014-03-27 | 901101 | 84450
2014-03-26 | 773556 | 84450
2014-03-25 | 345789 | 84450
In the spot where you try to count the repeat visits, the individual visit_id values from the outer query are no longer available, so you can't look at each visit_id value separately. Try this:
DECLARE #d DATETIME;
SET #d = '2014-03-30 00:00:00.000';
WITH V(visit_id,theday,special_date_flag) AS (
SELECT DISTINCT
visit_id,
visit_date,
CASE WHEN visit_id IN (
SELECT visit_id FROM visitors
WHERE visit_date >= #d AND visit_date < #d+1
--note change to select 1, not 2 dates here!
) THEN 1 ELSE NULL END
FROM visitors
)
SELECT
theday,
count(visit_id) as uniques,
count(special_date_flag) as visited_again
FROM V
WHERE thedate >= '2014-01-01 00:00:00.000'
GROUP BY theday
ORDER BY theday DESC
This query first creates a virtual table of (visit_id, theday) values, which has a third column for each row to indicate if the particular visit_id is one of the ones for the special date.
There are other ways to do this - you really only need the flag for each visit_id, not (visit_id,theday) pair, but this was easier to write.