Followup: how to model discount on items in a database? - mysql

I am building an ecommerce site and would like to offer discounts on certain items for a limited time. I would like to display how much discount we are offering per product. Hence, I need two values per product, original price and the discounted price for the given duration.
This is in followup to an answer for the question I asked
Schema:
Product
productId
Name
ProductPricing
productId (FK)
startDateTimeStamp
endDateTimeStamp
price
original price only applicable if we use approach A (comes later on)
Data:
Product:
1 | Apple
2 | Banana
T1: Dec 21, 2011: No deals at this time
ProductPricing
1 | Dec 20, 2011, 00:00 | Jan 1, 2038, 00:00 | 10$ | 10$
2 | Dec 20, 2011, 00:00 | Jan 1, 2038, 00:00 | 20$ | 20$
T2: Dec 24, 2011: Deal! Apply discount of 25% on apples from Dec 25, 14:00 - Dec 26, 14:00
Approach A.
- Query updates apple prices for the given duration
ProductPricing
1 | Dec 25, 2011, 14:00 | Dec 26, 2011, 14:00 | 7.5$| 10$
2 | Dec 20, 2011, 00:00 | Dec 25, 2038, 00:00 | 20$ | 20$
Approach B.
- Query adds another record with apple prices for the given duration
ProductPricing
1 | Dec 20, 2011, 00:00 | Jan 1, 2038, 00:00 | 10$ | 10$
2 | Dec 20, 2011, 00:00 | Dec 25, 2038, 00:00 | 20$ | 20$
1 | Dec 25, 2011, 14:00 | Dec 26, 2011, 14:00 | 7.5$| 10$
T3: Dec 27, 2011 - Options
Approach A.
At this time, the deal is expired, should I reset the endTimeStamp using a trigger ?
Approach B.
Should I delete the most recent record for the product for which the deal just expired ?

The design of the ProductPricing table allows us to never have to delete old pricing data (sometimes management wants a report based on that data). With what you have described above, you'd start like this (I changed the starting date just so it's easy to pick out that yes, this was the original price when the system went into place):
ProductPricing
1 | Jan 1, 1970, 00:00:00 | Jan 1, 2038, 00:00:00 | 10$ | 10$
Now let's say you give a discount price on your apples, and you wanted to be proactive and set up the system for when the sale was over:
ProductPricing
1 | Jan 1, 1970, 00:00:00 | Dec 20, 2011, 00:00:00 | 10$ | 10$
1 | Dec 20, 2011, 00:00:01 | Dec 26, 2011, 00:00:00 | 7.5$ | 10$
1 | Dec 26, 2011, 00:00:01 | Jan 1, 2038, 00:00:00 | 10$ | 10$
What we did here was:
Update the existing record with the 2038 timestamp, changing the endDateTimeStamp field to reflect the beginning of the sale
Insert a new record to define the sale
Insert another new record to reflect the normal price again
With no overlapping timestamps, you're guaranteed to get a single record when you query the database for your price. Thus,
SELECT p.Name, pp.price, pp.original_price
FROM Product p
INNER JOIN ProductPricing pp ON pp.productId = p.productId
WHERE NOW() BETWEEN pp.startDateTimeStamp AND pp.endDateTimeStamp
would get you a product list with current pricing.

Related

MySQL Complex Check with dates

I have a table called Offers in which I have several offers:
ID | List Name | Arrival | Depart | Price
1 | Plus | 12 August | 18 August | $ 100.00
2 | Plus | 19 August | 25 August | $ 120.00
3 | Plus | 26 August | 1 September | $ 80.00
4 | Weekend | 11 August | 13 August | $ 50.00
5 | Weekend | 18 August | 20 August | $ 60.00
6 | Weekend | 25 August | 27 August | $ 40.00
Then I have a guest in my hotel which has a check-in and check-out date.
In need to find all the offers with the same List Name that cover all the days of my vacation's guest (from check-in date to check-out date).
Example
If my guest spends his time in my hotel from the 13th of August to the 20th of August I need to be returned only ids 1 and 2.
If my guest spends his time in my hotel from the 13th of August to the 27th of August I need to be returned only ids 1, 2, and 3. That's because the ids 6 and 7 cover only weekend and not all the days between check-in and check-out dates.
If my guest spends his time in my hotel from the 18th of August to the 20th of August I need to be returned only ids 1, 2 and 5.
You are looking for logic like this:
select o.*
from offers o
where o.arrival <= $arrival and o.depart >= $depart;
$arrival and $depart are the dates for your guest.
You need to do something like this :
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE offers
(`ID` int, `Arrival` date, `Depart` date, `Price` float)
;
INSERT INTO offers
(`ID`, `Arrival`, `Depart`, `Price`)
VALUES
(1, '2016-08-12', '2016-08-18', 100.00),
(2, '2016-08-19', '2016-08-25', 120.00),
(3, '2016-08-26', '2016-09-01', 80.00),
(4, '2016-08-11', '2016-08-13', 50.00),
(5, '2016-08-18', '2016-08-20', 60.00),
(6, '2016-08-25', '2016-08-27', 40.00)
;
Query 1:
select *
from offers
where (((arrival >= '2016-08-13' and arrival <= '2016-08-20') or
(depart >= '2016-08-13' and depart <= '2016-08-20'))
and datediff(depart,arrival) = 6)
or
(arrival = '2016-08-13' and depart = '2016-08-20'
and datediff(depart,arrival) = 2)
Results:
| ID | Arrival | Depart | Price |
|----|--------------------------|--------------------------|-------|
| 1 | August, 12 2016 00:00:00 | August, 18 2016 00:00:00 | 100 |
| 2 | August, 19 2016 00:00:00 | August, 25 2016 00:00:00 | 120 |
Query 2:
select *
from offers
where (((arrival >= '2016-08-13' and arrival <= '2016-08-27') or
(depart >= '2016-08-13' and depart <= '2016-08-27'))
and datediff(depart,arrival) = 6)
or
(arrival = '2016-08-13' and depart = '2016-08-27'
and datediff(depart,arrival) = 2)
Results:
| ID | Arrival | Depart | Price |
|----|--------------------------|-----------------------------|-------|
| 1 | August, 12 2016 00:00:00 | August, 18 2016 00:00:00 | 100 |
| 2 | August, 19 2016 00:00:00 | August, 25 2016 00:00:00 | 120 |
| 3 | August, 26 2016 00:00:00 | September, 01 2016 00:00:00 | 80 |
Query 3:
select *
from offers
where (((arrival >= '2016-08-18' and arrival <= '2016-08-20') or
(depart >= '2016-08-18' and depart <= '2016-08-20'))
and datediff(depart,arrival) = 6)
or
(arrival = '2016-08-18' and depart = '2016-08-20'
and datediff(depart,arrival) = 2)
Results:
| ID | Arrival | Depart | Price |
|----|--------------------------|--------------------------|-------|
| 1 | August, 12 2016 00:00:00 | August, 18 2016 00:00:00 | 100 |
| 2 | August, 19 2016 00:00:00 | August, 25 2016 00:00:00 | 120 |
| 5 | August, 18 2016 00:00:00 | August, 20 2016 00:00:00 | 60 |

Rearrange the position of the values in the table

I am using MYSQL. I have a table containing a start date and an end date for a schedule.
| StartDate | FinishDate |
|Feb 1, 2016 11:50 AM |Feb 2, 2016 3:37 PM |
|Feb 2, 2016 4:29 PM |Feb 3, 2016 8:16 PM |
|Feb 3, 2016 8:17 PM |Feb 3, 2016 8:34 PM |
What I was hoping to do is to get the dates where there are no schedule entries.
The easiest way I thought of is to generate a new table wherein the values in the FinishDate and StartDate will be rearranged in this format (The FinishDate value in the first row, second column will be the value in the first row, first column, while the StartDate value in the second row, first column will be transported to first row, second cloumn, and so on):
Desired Output
Without allocation:
| StartDate | FinishDate |
|Feb 2, 2016 3:37 PM |Feb 2, 2016 4:29 PM |
|Feb 3, 2016 8:16 PM |Feb 3, 2016 8:17 PM |
|Feb 3, 2016 8:34 PM | - |
How can I achieve the desired output? Thank you very much in advance.
MySql doesn't have window functions, so generating this kind of result is always tricky. One possibility is:
SELECT
t1.FinishDate StartDate
, MIN(t2.StartDate) FinishDate
FROM table t1
LEFT JOIN table t2 ON t2.StartDate > t1.FinishDate
GROUP BY 1
This will work well if your date columns are indexed and you have relatively small number of rows to process.

php mysql command for daily report

I have a mysql table with some entries. sample data
nid | news_date
1 | 16 July 2015, 2:31 pm
2 | 16 July 2015, 2:31 pm
3 | 17 July 2015, 12:31 pm
4 | 18 July 2015, 4:28 pm
5 | 20 July 2015, 12:31 pm
I want daily report, and i tried with this sql command
SELECT count(nid), DATE(news_date)
FROM tbl_news
GROUP BY DATE(tbl_news.news_date);
But i am getting output as
count(nid) | DATE(news_date)
5 | NULL
But i want daily record count report, Anybody help.
Please try this query:-
SELECT count(nid), news_date
FROM tbl_news
GROUP BY (STR_TO_DATE(tbl_news.news_date, '%d %M %Y')) ;
I have removed the date keyword.

MySql Daily Report

I am trying to get some help fixing my complex query. I am explaining below my situation, thanks.
I have the following two tables:
ACTIVITY TABLE:
ID USER_ID CARD_ID CLOCK
1 123 04675545 4/3/2013 1:07:06 PM
2 123 04675545 4/3/2013 2:08:06 PM
3 124 04675550 4/3/2013 2:07:06 PM
4 124 04675550 4/3/2013 4:07:06 PM
5 124 04675550 4/4/2013 10:07:06 AM
6 124 04675550 4/4/2013 2:00:00 PM
7 124 04675550 4/5/2013 4:07:06 PM
8 124 04675550 4/7/2013 8:00:00 AM
9 124 04675550 4/7/2013 5:00:00 PM
PRICE TABLE:
ID FROMTIME TOTIME PRICEPERHOUR
1 08:00:00 19:59:59 50.00
2 20:00:00 07:59:59 75.00
And the following query:
select a.user_id, date(a.clock), ABS(TIME_TO_SEC(TIMEDIFF(a.clock, b.clock))/3600)*c.PRICEPERHOUR as total from
(Select if((#rn:=#rn+1)%2=0,#rn,#rn-1) As rId, act.* from act
join (select #rn:=-1)a
order by user_Id, clock) a
inner join
(Select if((#rn1:=#rn1+1)%2=0,#rn1,#rn1-1) As rId, act.* from act
join
(select #rn1:=-1)b
order by user_Id, clock) b
ON a.rid=b.rid AND a.id <> b.id
inner join
price c
on
TIME_TO_SEC(a.clock) between TIME_TO_SEC(c.FROMTIME)
AND
TIME_TO_SEC(c.TOTIME)
group by a.user_id, date(a.clock)
And I am getting the following result:
USER_ID DATE(A.CLOCK) TOTAL
123 April, 03 2013 00:00:00+0000 50.8333
124 April, 03 2013 00:00:00+0000 100
124 April, 04 2013 00:00:00+0000 194.0833
124 April, 05 2013 00:00:00+0000 1,994.0833
124 April, 07 2013 00:00:00+0000 1,994.0833
However, I am trying to get this result instead:
USER_ID DATE(A.CLOCK) TOTAL
123 April, 03 2013 00:00:00+0000 50.8333
124 April, 03 2013 00:00:00+0000 100
124 April, 04 2013 00:00:00+0000 194.0833
124 April, 05 2013 00:00:00+0000 50
124 April, 07 2013 00:00:00+0000 450
This is part of a clock system. Each time the user check-in, one entry gets recorded on the database. A correct user behavior will be that it has always a pair record recorded. For example user_id 123 clocks at 1:07:06pm and clocks again at 2:08:06pm. However, in some situations, the user may do it just once (unpaired record on the database) and therefore it should only be charged that particular hour from the record. As an example, user 124 on day 4/5/2013.
I am trying all weekend to get this query working :(. Once I get the correct result, I will add a condition to get only one user_id also, (e.g. where user_id=124).
I think even if you manage to do this there are are some potential design pitfalls:
Can people clock in for more than 1 period per day? If so then 2 records for example
10am and 2pm could total 2hours or 4hours.
What happens if people clock in at 11pm and again at 2am?
From a quick glance I don't think your sql takes into account time periods that span across the 2 different pay rates? You should definitely include this scenario in your test data.
If I was going to implement this I would probably move the logic into code, and simplify the price table by only having one time column like:
TIME, PRICE
00:00, 75.00
08:00, 50.00
20:00, 75.00
Also if a user only has one card you may not need to have card_id and user_id in the activity table.

SSRS: Pivoting columns

I've got a list of raw data which is passed to SSRS from a stored procedure. I have a matrix which then pivots the data.
For example:
Raw data
WeekNumber Date
1 Mon 10th Dec
1 Tue 11th Dec
1 Wed 12th Dec
2 Mon 17th Dec
When pivoted, it becomes the following for the column names
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec | Mon 17th Dec
Is it possible to have a pivot with a where condition? In this example say,
I'd want it to look like
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec
and then another column with Mon 17th Dec since the WeekNumber is 2
I'm not sure I understand your question. But anyway, perhaps you could consider doing the pivot in your stored procedure as per :
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
My secret to success when using reporting tools is to solve complex problems at the data level, rather than trying to get the reporting tool to do it.
Yes, this is not difficult.
What you are calling a pivot in SSRS is really just a column group. You can add either a filter or a parent group to your column group to filter out WeekNumber <> 2 or group above by WeekNumber. With a parent group you could get results like:
WeekNum: 1 | Total for week | |WeekNum: 2 | Total for week |
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec | | |Mon 17th Dec
20 | 25 | 10 | 55 | | 15 | 15