I have an AngularJS front-end app that sends requests to a Rails API back-end. When a user search for items, so far the query is limited to 20 elements and is always ordered by popularity (a field stored in database). After the results are retrieved from DB, there is a complex process that calculates the item prices iterating one by one (remember only 20 elements). After that, the results are served to the user. Note: As told, item prices cannot be calculated directly in the query because an item can have different prices according to dates and also discounts can be applied.
This is how it´s working so far.
Now, I would like to introduce in the search results page an innocent order by: Price functionality. So, the array of items should come ordered by price.
As, I can´t get the prices directly with just one query, I see two choices:
To keep it as it is right now, I mean, making the query ordered by popularity and order the results after the prices are calculated. But I see a problem, If I get 20 elements each time ordered by popularity, then I can calculate prices and order by price these 20 elements, so I assume I´m not ordering correctly by price. This case, I would need to query without limit, to get all items, calculate prices, order them by price and return to user. I think I would also to develop a home-made pagination functionality.
Develop some kind of stored procedure in the database to provide the results with the complex prices calculations. I don´t know if I can order them easily. I´m worried because I don´t know stored procedures in MySQL and not sure if it´s possible to do what I need.
But, from the performance point of view, I guess the second choice should be better, right? I´d appreciate comments or any other options?
UPDATE:
According to comments, I detail how to calculate prices functionality.
A user can rent an item for many days (a week i.e.). So, there is a check-in and check-out dates.
Also, prices changes according to seasons. This means days can have different prices in a selected week. So, in order to calculate the total price, you have to get the daily price matching each selected day in the week and add it to the total price.
Once, the base price is calculated, there can be discounts. Same as prices, discounts can be applied only for some days, so first, it must be checked if there is any discount for the selected week. If so, the discount is applied to base price to get the total final price.
Please, let me know if you need the code.
This should be done on the backend (MySQL).
Like you mentioned, if you want to sort by price on the front-end, you'll have to replicate the entire database into Angular. Which is probably a bad idea.
This is the approach I would take in MySQL:
Set up a table with item_id, date, and price (or perform the joins necessary to get this table).
Apply discounts for each date. This step yields an interim table with updated prices.
Build your final query. Your SELECT clause should SUM(price). You should GROUP BY item_id. And you should ORDER BY SUM(price) DESC.
Depending on the size of your database, this query may require a lot of fine-tuning in order to return results quickly. But it definitely can (and should) be done in the backend.
Good luck!
EDIT: With a really big set of items, running this query through MySQL may become too slow, regardless of how much time you spend tuning performance. If MySQL doesn't cut it, you may need to rely on an auxiliary database like Elasticsearch.
But before turning to Elasticsearch/Hadoop/etc, you should think carefully about how "complex" your pricing algorithm really is. In all likelihood you can optimize the MySQL query to the point where it performs just fine.
This is a classical search listing problem. If you want to perform sort based on price, obviously you need the required inputs. it is worth mentioning that stored procedure won't magically improve the performance. The best what you can do is run recurring CRON job every hour/day ( bonus tip : use upset and perform mass operation) which updates the price. Now perform join and do the query and order it with "price". Also make sure to put indexes on required fields. After grabbing the result apply discount and show the result. If query is complex the pagination won't work. You have to do all sorts of operation manually. If you are using will_paginate , it has some function which also support Array instead of active record. Hope this helps.
So you have the following factors then:
User Inputs Beginning Date, End Date and sees list of items.
The Beginning - End Date period (selected period) is consistent for all items in the query.
To sort by price you must, for each item in the table (not just those retrieved)
Get Item Price for each day of the selected period.
Get available discounts for each day of the selected period.
Calculate Total Price for selected period.
Because user input is involved you will not be able to definitively calculate this and store the final product. The best you could do is calculate the gross and net prices per day and cache that, then performing the final calculation based on user input (or sending that data to browser for calculation there)
You could also use a first order approximation and just go with the current gross value, and then tweak from there. That assumes that the relationship between item prices remains more or less consistent.
Related
I need to know how many times a page is viewed in a specific timeframe, say the last 30 days. In the database there is a PageViewCount field so every time the page is viewed, the PageViewCount increases, but there is no timestamp in the database of when this occurs.
I know there has to be a simple solution to this, but I am having a real hard time wrapping my brain around it.
As it is stated, it is impossible to query for such a value. Consider the following equivalent example: I have some coins, for a total of $3.50 and I don't track the movements of the coins. How much money did I had yesterday?
You should have a daily procedure that stores the current PageViewCount with the current date in another table and then simply query that table.
I have an SSRS report with a dataset that queries AS400. I am trying to print a "Version number" on the top of my report. Due to several complications, I cannot really print a version number better than last update time.
This is my what my data looks like:
The query takes a parameter that is a PackingDate. As in, the ShopOrders were written to pack on this date.
The query returns anywhere from 10-25 shop orders for each PackingDate.
Each of these shop orders have 4 columns: DateCreated, TimeCreated, DateModified, TimeModified.
Shop orders go through changes and revisions frequently. Every time a shop order is changed, the DateModified, TimeModified field changes.
I want to look at each of these shop orders, look at the DateModified, get the maximum date, then look at the TimeModified, get the maximum time, and add a concatenated form of that as the version number on top of my report. For instance:
Date fields are in yyyyMMdd format and time fields are in mmhhss format.
ShopOrder: 65642
DateModified: 20180118
TimeModified: 124500
ShopOrder: 65643
DateModified: 20180117
TimeModified: 142000
Since the MAX(DateModified) in these two shop orders is 20170118, I want the TimeModified for that corresponding date: 124500.
So the version number would look like this: v0118.1245.
I would like to, if possible, have this done in SSRS and not have to do much in my dataset, but that is not written in stone. I just want the MAX(Time) for THE MAX(Date).
EDIT 1:
This is what I've already tried:
LOOKUP(MAX(Fields!DateModified.Value), Fields!DateModified.Value, MAX(Fields!TimeModified.Value), "ShopOrders")
I was pretty proud of myself for thinking of this, but that burned down quickly when I got an error that said I cannot use Aggregate functions in Lookup.
I'm not sure if you can use lookups in this case although I could be very wrong as I don't use lookups enough to know their limitations.
The way I would approach it would be to simply add a new column in your query results that combines your date and time columns. Then you could simply get the Max of that new column.
I apologize in advance that I don't know the terminology of the tools I'm trying to use.
I have a table of events with a startdate field (among others) and a related repeats table with a reference to the event id. The repeats table stores the days of the week on which the event repeats and whether it's monthly, weekly, etc. What I'm hoping to do is duplicate the repeating events within the SQL query so my final result will have the the same event in different places when ordered on start date, so I can limit the results for proper pagination.
I'm looking at creating virtual tables and cloning tables documentation, but I'm having trouble applying the examples to my situation.
Update:
Hopefully I can elaborate on this.
The basics of what I have now is SELECT * FROM 'events' WHERE 'start_date' >= TODAY() ORDER BY 'start_date LIMIT 20 which gets me every event from today on, but I'm paginating the results so only 20 are displayed at a time.
What I would like to do is create a temporary 'virtual' table with the events which have an associated repeat entry, on which I will change the start_date based on the repeat information. So if it's a weekly repeat, this second table would be filled with identical events except that each start_date would be 7 days from the last. Then I could do a join on these two tables, limit those results to the 20 pagination limit I want, and have a query result with the events in the correct place and easy to perform pagination on.
I understand that creating a function in mySQL might be on the right track as I imagine I would have to loop through some information for adding to dates. I only know the level of SQL one picks up by writing in PHP, so functions are a bit out of my scope, though it doesn't seem that it'll be too hard to pick up with a little reading. I'm more confused about how I would create a fake table, add entries to it in a loop and then use a join on it to merge it with the first query.
I'm also beginning to wonder about the overhead for doing this in mySQL and, should I be successful in getting this to work, how I might cache these results, though it's only an afterthought right now.
Thanks to those who are trying to help me, I'm having trouble getting this question into words for some reason.
I'm trying to help a taxi company. The problem is that they have a credit card machine that takes payments, and has its own database entries, and there is a completely separate database that has a list of entries such as pick up time, and drop off time.
I need to match the database of trip entries to the credit card purchases, and the only way to do this is by matching which vehicle is running the transaction, and looking for a time CLOSE TO the DROP OFF time and see if it's a match. It's not ideal.
So, I am trying to compare two times in yy-mm-dd hh:mm:ss format. They need to be plus or minus 5 minutes of each other. How do I do this in MYSQL?
I thought SubTime and Addtime would work, and it seemed to, but then I got wierd results.
SELECT * FROM completedtrans WHERE DrivID = 128 AND TransTime BETWEEN SUBTIME('2013-06-20 16:53:06', '0 00:05:00') AND ADDTIME('2013-06-20 16:53:06', '0 00:05:00')
Here's an example of one of my searches. Can anyone tell me what's wrong with it? It's supposed to search 5 minutes before and after that particular given time. I can't simply write the time, because the query is automatically generated through php code.
I'm currently working on writing report generators. For one report I need to do a breakdown by a given characteristic (supplier, logging user, language, etc which for each row includes the name of the characteristic I'm interested in, the number of items that match that characterastic, and the percentage of total items this figure represents. The first two aren't a problem, the third is.
For example, to get a breakdown by language I'd be using a query like this.
SELECT lang_id,
COUNT(IF(open=TRUE,1,NULL)) AS lang_total
FROM table
GROUP BY lang_id;
This gives me the number of items per language.
I can get the total number of items in the table and store it in a variable simply enough with a plain count.
SELECT #totalOpen:=COUNT(*) FROM table WHERE open = TRUE;
Now I want to add a third column, which is the figure in lang_total divided by the value in #totalOpen multiplied by 100 (in other words, the percentage of all items that fit the criteria). Something along the lines of the following:
This is the bit I'm having trouble with, as because as far as I can tell you can't use aggregate columns in calculations.
SELECT lang_id,
COUNT(IF(open=true,1,NULL)) AS lang_total
(lang_total/#totalOpen)*100 as lang_percent
FROM table
GROUP BY lang_id;
I'm sure that there must be a way of doing this in MySQL, but I've not been able to track it down. Can anyone help out with this?
I read this question now for the first time. I know that probably it's too late to be useful for you but I would have solved in this way.
select lang_id,
sum(if(open= true,1,0)) as lang_total,
coalesce(sum(if(open= true,1,null)) / #r,0) as percentage
from table,(select #r:=count(*) from table where open = TRUE) as t
group by lang_id;