How to calculate a sum of entries (balance) efficiently in SQL? - mysql

We have a table "bookings":
id int
amount float
user_id
A users "balance" is the sum of all bookings. After years and some users with 100k+ entries that calculation is getting slow.
I have 2 solutions in mind:
Create a "balance" column in "users" table and add or substruct amounts each time a booking is added
Create a "last_balance" column in "users" table that is updated once a year with the users balance on the 01.01. 00:00:00, then balance is "last_balance" + sum(all-bookings-since-01.01.)
The issue with 1) is that any bug or delay in the db might result in a wrong balance. Therefore I think the balance should rather be the sum of bookings than an individual number.
Is there a "right" way to calculate a users balance in any system where users hold money?

Related

How to create a double graphic using two tables in Tableau

I am working with Tableau in order to obtain a report for my company. The report is a sales based one that should include both booking total amount and total number of booking that have been sold.
So, as an example, we have 2 tables right now:
Bookings.
ID -- primary key, unique.
date -- date of booking creation
Payment.
ID -- primary key, payment id. Unique
bookingId -- FK. booking Id. Can be repeated.
Amount -- Amount paid.
Date. -- Date of payment. We can pay a booking in different dates. We can refund (so every time we refund we will have one line for this payment in negative).
What we need:
We would like to display a graphic report with both data. Price & number of bookings filtering by both dates.
We tried to do that using countD and filtering by date from payment table but we get different number of bookings because we can have 1-N payments of 1 booking.
If we filter by booking date, we won't have the same values so it should be nice to combine both filters.
I don't know the viability of this...

Unify records in one row

I have 2 tables in my DB
Table1 has
sales_ID
date
calls_volume
Table 2 has
sales_ID
date
sales_volume
skills_volume (the skills where the sales was logged in)
What makes things harder, is that in Table 1, there's one record per sales and date (in other words, every sales has 1 record in every day).
However table2 has one record per sales, date and skills.
When I join the two tables in a query (date and sales_id) I get the calls volume multiplied by skills volume).
can any one help me on that?

Selecting average prices for each of the last 30 days in SQL

Have this query that I use to get the average price of the products in a product category for each of the last 30 days:
SELECT DATE(bsbp.date) AS pricedate, UNIX_TIMESTAMP(DATE(bsbp.date)) AS unixdate,
ROUND(AVG((bsbp.price / 100) * (bc.exchangerate / 100)), 0) AS avgprice
FROM bd_shopbikesprices bsbp, bd_categoriesshopbikes bcsb, bd_shopbikes bsb,
bd_shops bs, bd_currencies bc
WHERE bsbp.shopbikeid = bcsb.shopbikeid AND bcsb.categoryid = 94
AND bsbp.shopbikeid = bsb.id AND bsb.shopid = bs.id AND bs.feedcurrencyid = bc.id
AND bsbp.price > 0
GROUP BY DATE(bsbp.date) ORDER BY pricedate DESC LIMIT 30
Problem is that the table with the prices (bsbp) only contains price changes, i.e. the last price of each product where the price was different than the previous price of the product (or where the product was new and therefore didn't have a previous price).
Like this:
shopbikeid|date|price
890061|2016-07-27 02:50:01|29999
890061|2016-07-21 03:21:51|49999
890061|2016-07-17 21:20:55|29999
890061|2016-06-30 04:41:36|49999
Currently the query takes the average new prices for each day, which isn't the actual average price since the average new prices only covers the products where the price was changed/new products.
My question is how the query should be rewritten so each daily average is the average price of all products on that day, including products where the prices was changed before that day.
Can it be done somehow with a nice query? (the database is a MySQL database)
I had a similiar case and solved it using the following approach:
Create a temporary table tmpLatestDates from your price table, in which you group by the product and use MAX(date)
Create another temporary table tmpLatestPrices: Join tmpLatestDateswith your price table on product and date, only keeping the rows from tmpLatestDates. This gives you the latest price for each product.
Run your original query on tmpLatestPrices
When you do this with large datasets you'll want to add indexes to the temporary tables after you created them. Also don't forget to drop the temporary tables after you're done.
The most practical way of handling it would be to put all queries in a stored procedure.
Edit: You can follow the same logic using subqueries, but I find the temp. table approach easier to follow plus it simplifies maintenance later on.

Trying to think of the best database schema for storing dates ranges that will require joins

(Table names in quotes)
Let's say there are "users" that try to sells "products". They earn a commission on all "product_sales" (id, product_id, user_id, total, sale_date). I want to somehow store their commission rate based on certain dates. For example, a user will earn 1% from 2015-01-01 to 2015-01-15, 2% from 2015-01-16 to 2015-01-28, and 3% from 2015-01-29 onwards.
I want to run a query that calculates the total commissions for a user in January.
I want to run a query that calculates daily earnings in January.
How do I store the commission rates? One idea was having a table "user_commissions" that has (id, user_id, commission_rate, from_date, to_date). It would be easy to calculate the rate for (1) if commissions stayed the same, in which case I'd do this:
SELECT (sum(total) * 0.01) as total_commissions FROM product_sales WHERE user_id = 5 and sale_date between '2015-01-01' and '2015-01-31'
But with commission rates variable this is more complex. I need to somehow join the commissions table on each sale to get the right totals.
Another question would be:
How do I store the users' current commission rate that doesn't have an expiration date and include that in the reports? In my example, "3% from 2015-01-29 onwards". This has no end date.
Your table structure is a very reasonable structure and often used for slowly changing dimensions. Storing the effective and end dates in the structure is important for efficiency.
One way to store to_date for the most recent commission is to use NULL. This allows you to do:
select *
from commissions
where to_date is null
to get the most recent record.
However, a better way is to use some far distant date, such as '9999-12-12'. This allows you get the most recent commission using:
where curdate() between from_date and to_date
This is an expression that can also make use of an index on from_date, to_date.
Honestly, I would store user commission percentages and the effective dates of those commissions in one table.
TABLE: COMMISSION
user_id, date_effective, commission
In the other table I would store sales data. With each sale, I would keep the commission the salesperson got on the sale. (Added bonus, you can change the commission on any sale, like an override of sorts.)
TABLE: SALE
sale_id, sale_date, user_id, sale_amount, commission
When you create the row in your program, you can grab the correct commission rate using the following query:
SELECT commission from commission WHERE user_id=[user's id] AND date_effective<=[sale date, today] ORDER BY date_effective ASC;
MySQL Left Joins, and SQL in general, can get really tricky when trying to join on dates that don't exactly match up. (Looking back, basically.) I am struggling with the same problem right now without the luxury of the solution I just suggested.
(This whole post is based on the assumption that you aren't going to be directly interacting with this database through a DBMS but instead through an application.)

Access: Auto-Fill in values in a table based on certain criteria. (Payment schedule)

I basically want to create a payment schedule in a separate table based on 4 values that a user would select. The payment schedule is very basic and the table only needs 2 columns, 1) Date of payment, 2) payment amount.
The 4 criteria values that are used to fill out this simple table would be: 1) the total amount of money, 2) number of payments, 3) the frequency of the payments (monthly, quarterly, semi-annually, annually), 4) the date of the first payment.
The way that I envision this is having a Form where these 4 values will be selected. On that form there can be a button to execute the command to fill in a datasheet with the appropriate values.
The first entry would obviously be on the date of the first payment, and the amount for that entry would be the total amount divided by the number of payments. For the second record dollar amount would be the same and the date would be the first payment date + the frequency. So if the first payment date is 1/1/2000 and the frequency annually, then the second entry date would be 1/1/2001. Etc.. until the last payment is made.
While it is a pretty simple payment schedule, I'm not sure how to best approach this in Access and if it's even possible. Would appreciate some input and direction. Thank you!
You will need
-- A numbers table with integers from 0 to the highest number of payments possible, indexed on the number.
-- A combobox called Frequency on forms payments with the following properties:
Row source: q;quarter;m;month;ww;week
Row source type : value list
Bound column : 1
Column count : 2
Column widths : 0, 1
-- A query
INSERT INTO Payments ( UserID, PaymentDate, Payment )
SELECT [Forms]![Payments]![UserID],
DateAdd([Forms]![Payments]![Frequency],
[Number],
[Forms]![Payments]![Startdate]) AS Expr2,
[Forms]![Payments]![LoanAmount]/
[Forms]![Payments]![NumberOfPayments] AS Expr3
FROM Numbers
WHERE (((Numbers.Number)<[Forms]![Payments]![NumberOfPayments]));
-- A button to run the query.