MySQL Database Design for Prices / Specials - mysql

Below is an example of what I need to design in the database:
There will be an option for a price, and then a special if it has one, and then I was wondering how I could do this if I wanted one of the options to be "FREE".
See also there will be different currencies based on their country. Here is what I had in mind:
id - service_name - price - special_price - eco_friendly - free
1 - Rotate Tires - 9.00 - NULL - 1 - 1
2 - Service - 120.00- 100.00 - NULL - NULL
Would that work or is there a better way to do this? The eco-friendly will just display an icon in the HTML if it is set.
Thanks!

Set the special_price to 0 and let the presentation layer print FREE if the price is 0.
That way you can also do discount calculations and other things so that the price actually drops to 0 and then becomes "FREE" to the customer.

Related

Select equal values from column with inner join (sql)

I am pretty new to SQL, so please dont go too hard on me :(
I have got two tables - "cart" and "orders". In the "cart" table are all the things a random user has ordered - in this case these few rows (with the column names above):
session_id - product_id - product - product_name - price - quantity
1 - 1 - Table - Something - 10.00 - 1
1 - 2 - Chair - SomethingNew - 20.00 - 1
2 - 2 - Chair - SomethingNew - 20.00 - 1
I want to write a statement that selects all the orders with the same session_id - in this case the number 1 - and copy it into the "orders" table, because all the items with the same session id should be labeled as one big order - but i don't want to hard code it (with select and session_id=1 for instance)...
This is what i have got so far for my selection:
I tried using a inner/self join, so that i can check if the values are the same
SELECT * from cart JOIN cart AS cartB
WHERE cart.session_id = cartB.session_id
GROUP BY cart.session_id;
But this currently gives me the following rows as solution:
session_id - product_id - product - product_name - price - quantity
1 - 1 - Table - Something - 10.00 - 1
2 - 2 - Chair - SomethingNew - 20.00 - 1
Instead of (what i would have wanted):
session_id - product_id - product - product_name - price - quantity
1 - 1 - Table - Something - 10.00 - 1
1 - 2 - Chair - SomethingNew - 20.00 - 1
EDIT: I know, the 2nd and 3rd rows are pretty similar. But thats exactly why the session id is very important in this case, and why i'd like to make it work.
What am i doing wrong? Is the inner join even needed? I am really having some problems with the statment since i am a huge beginner here.. but my code would make sense to me.. :(
Big thanks to everybody who takes his time to read this (and maybe even help)!
At the moment I see here 2 options:
Either your PROGRAM passes to SQL the session IDs for orders that you want to copy to orders table, -- which would be normal in any webshop.
In which case you have to get the correct session_id= from your program and pass it to your SQL query as a parameter, eg. in PHP.
Or you want to copy ALL session_id'd cart contents into orders. In which case it is even simpler, without any WHERE: INSERT INTO order SELECT * FROM cart;
If you want something else, you should specify, how it is determined WHICH session_id's need to be copied from cart to orders.
UPD. And you are right, the JOIN in your example doesn't appear to do anything useful.

Database design will not support entry of products.

I have the database design below:
table design
basically, I need to be able to input products that have the following columns:
id, area, category, name
The problem is that if I create the database to the spec in the image above, then wouldn't that still cause repeats in the table catalogue? which would be against the normal forms.
Also it means that the data in catalogue would just be numbers aka references to the other tables. Is there a solution to this issue? as I imagine any catalog site that has a db back-end would have the same issue.
Here is an example that is the same issue just different sinorio if it helps:
I have a product catalog that sells laptops and TVs, do I make a table that has the type of device? because that would look like this:
id - name - type - etc
1 - acerP - laptop - null
1 - acerP - laptop - null
1 - acerP - TV - null
Notice how 'laptop' could get repeated 100s of times, normally you would create a new table that has just:
u_id - type
101 - laptop
102 - TV
Then simply join then in the query, but then you would have in the device table:
id - name - type - etc
1 - acerP - 101 - null
1 - acerP - 101 - null
1 - acerP - 102 - null
but now you are repeating the number references.
Am I missing something here, or is this normal?
This is norma, only foreign keys would be repeated in your proposed structure, which is allowed by the 1NF:
First normal form enforces these criteria:
- Eliminate repeating groups in individual tables.
...
Groups means a combination of columns. The same category may appear more than once in catalogue, but not, for example the couple (category, area).

MYSQL Query matching column names to rows in another table

I have two tables in MYSQL, one contain a list of options a customer can pick from and the other has the cost of the options. I would like a view that returns the cost of each customers option set. For my example I'll use the analogy of buying a new car.
Customers table
customer - sunroof - mag_wheels - spoiler
--------------------------------------------------
John - true - true - false
Steve - false - true - false
Lucy - false - false - false
Options table
option - price
-----------------
sunroof - 100
mag_wheels - 150
spoiler - 75
Desired results
customer - cost
-----------------
John - 250
Steve - 150
Lucy - 0
or this would do, as I good easily multiply selected by price and then group by customer
customer - option - selected - price
------------------------------------------
John - sunroof - true - 100
John - mag_wheels - true - 150
John - spoiler - false - 75
Steve - sunroof - false - 100
Steve - mag_wheels - true - 150
Steve - spoiler - false - 75
Lucy - sunroof - false - 100
Lucy - mag_wheels - false - 150
Lucy - spoiler - false - 75
I've been puzzling over this for hours now and I can't even figure out where to start, a join seems out of the question as there are no common elements to match. I wonder if using UNION is the answer but I can't figure out how to combine row values with column headings.
If anyone could point me in the right direction I'd be ever so grateful, double points if you come up with a solution that dynamically picks up the different options so I could add more in the future without rewriting the query.
Many thanks in advance.
The reason I wanted to have all the options as a single row is I was hoping to use Access to make a form for picking the options and I couldn't figure out how a single form could create multiple rows.
This is a horrible data layout. You should have an association table, with one row per customer and option.
But, you can do it:
select c.customer, sum(o.cost) as cost
from customers c left outer join
options o
on (c.sunroof = true and o.option = 'sunroof' or
c.mag_wheels = true and o.option = 'mag_wheels' or
c.spoiler = true and o.option = 'spoiler'
)
group by c.customer;
EDIT:
You do not want all options in a single record. Instead, you need an association table:
create table customer_options (
customer_optionid unsigned auto_increment,
customer varchar(255) references customer(name),
option varchar(255) references option(option)
);
Actually you should really have integer primary keys for all the tables, and use them for the foreign key references. If you need data in the output in the question, then just write a query to return it in that format.
Looking at the table structure, even I think it will not be possible to write joins because as you mentioned, the table structure doesn't have a relation between them.
I am assume you have just started the project, so it's time you first re-visit your DB structure and correct it.
Ideally, you should have a customer table with a customer id.
Then you should have products table with product id.
One table which will have data on what products customers have purchased - something like customer_products. This will be a one to many relation. So customer 1 can have product 1,3 and 5. Which would means in customer_product there will be three entries.
And then when you want to do a sum total, you can first join the table customer, product based on customer_product and then do a sum of the price also to get the total amount for individual customer.
Bad design. You must a have a customers table like this:
custumer_id
customer_name
other fileds...
On the other hand you should have an accesories table, where you usually describe each item-
accesory_id
accesory_name
supplier_id
country_of_origin
other stuff
Also an accesory_price table, where prices are added due the fact that prices change.
accesory_id
price
active_price
date_price_added
An finally you should relate all in a customer_accesory table:
customer_id
accesory_id
By having this, you can join tables and select both customer basket size and customer preferences of accesories. Basket size, or the amount purchased by each customer can be summarized SUM, AVG, COUNT or you can pivot data using GROUP_CONCAT in order to generate high quality reports.

Adding or subtracting values based on another field's contents

I have a table with transactions. All transactions are stored as positive numbers, if its a deposit or withdrawl only the action changes. How do i write a query that can sum up the numbers based on the action
-actions-
1 Buy 2 Sell 5 Dividend
ID ACTION SYMBOL PRICE SHARES
1 1 AGNC 27.50 150
2 2 AGNC 30.00 50
3 5 AGNC 1.25 100
So the query should show AGNC has a total of 100 shares.
SELECT
symbol,sum(shares) AS shares,
ROUND(abs(sum((price * shares))),2) AS cost,
FROM bf_transactions
WHERE (action_id <> 5)
GROUP BY symbol
HAVING sum(shares) > 0
I was originally using that query when i had positive/negative numbers and that worked great.. but i dont know how to do it now with just positive numbers.
This ought to do it:
SELECT symbol, sum(case action
when 1 then shares
when 2 then -shares
end) as shares
FROM bf_transactions
GROUP BY symbol
SQL Fiddle here
It is however good practice to denormalize this kind of data - what you appear to have now is a correctly normalized database with no duplicate data, but it's rather impractical to use as you can see in cases like this. You should keep a separate table with current stock portfolio that you update when a transaction is executed.
Also, including a HAVING-clause to 'hide' corrupted data (someone has sold more than they have purchased) seems rather bad practice to me - when a situation like that is detected you should definitely throw some kind of error, or an internal alert.

Database Design: how to model generic price factors of a product/service?

I'm trying to create a generic data model that will allow for a particular product (indicated by the FK product_id in the sample table below) to specify 0 or more price "factors" (I define "factor" as a unit of price added or subtracted in order to get the total).
So say there is this table:
===============================
price
===============================
price_id (PK)
product_id (FK)
label
operation (ENUM: add, subtract)
type (ENUM: amount, percentage)
value
A book's price might be represented this way:
====================================================================
price_id | product_id | label | operation | type | value
====================================================================
1 | 10 | Price | add | amount | 20
2 | 10 | Discount | subtract | percentage | .25
3 | 10 | Sales Tax | add | percentage | .1
This basically means:
Price: $20.00
Discount: - $5.00 (25%)
--------------------
Sub Total: $15.00
Sales Tax: $1.50 (10%)
------------------------
Total: $16.50
A few questions:
Is there anything obviously wrong with the initial design?
What if I wanted to create "templates" (e.g. "general merchandise" template that has "price", "discount" and "sales tax" fields; a "luxury merchandise" that has "price", "discount", "luxury tax" fields) - how would I model that?
The above model works if each record applies to the total of the preceeding record. So, in the example, "sales tax" applies to the difference of "price" and "discount". What if total was not computed that simply? For example: A + B + (A + 10%) - (B - 5%). How would I model that?
Also, what if the "percentage" type doesn't apply to the immediately preceeding row (as implied by question #3) and applied to more than 1 row? Do I need another table to itemize which price->price_id the percentage applies to?
First of all you need a model of price labels, which is simple:
price_labels
id | label
1 | Price
2 | Discount
3 | Tax
Then a slightly modified version of the sample table that you've given:
products_prices
price_id|product_id|label_id|divider|value
1 10 1 1 20
2 10 2 100 -25
3 10 3 100 10
Here I just substituted the label with the corresponding id from the price_labels table as a foreign key. Additionally, I omitted the type field which is trivial since value can be positive or negative float number. I added the divider column to enable the percentage parameter. I think it is more easily read this way as well, since you say (and think) "minus twenty-five percent" not 0.25 .
Now the expression "abstraction" part is a bit more complicated and there could be a lot of solutions.
price_expressions
product_id | date_from | date_until | expression
10 |2011-11-02 04:00:00 |2011-11-12 04:00:00 | (SELECT divider*value from
products_prices
WHERE product_id=%PRODUCT_ID%
AND label_id=1)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=2)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=3)
In the expression field you can store a complex SQL statement in which you can just replace the %PRODUCT_ID% placeholder with the product_id value from the same row:
SELECT REPLACE(expression,'%PRODUCT_ID%',CAST(product_id AS char))
AS price_expression FROM price_expressions
WHERE product_id = 10 AND date_from>=DATE_OF_PURCHASE
AND date_until<=DATE_OF_PURCHASE
There are two possible variations of this the way I see it:
You can change the product_id=%PRODUCT_ID% and label_id=N condition with just a price_id=N since you already have it stored in the products_prices table
You can use another expression format e.g. %PRICE_ID_1%*%PRICE_ID_2 and perform substitutions and calculations on the application level not directly in SQL
Hope this helps.
This seems a little over-engineered.
1) Wouldn't the sales tax percentage be a factor of where the item was purchased and not which item was purchased? I could see a field for "IsTaxable", but specifying the rate for each items seems incorrect.
2) Are you sure you need to incur the cost of making this generic? Are you already fairly certain there will be more factors in the future? If not, don't overcomplicate it.
Suggested Design:
- Add columns to the products table for IsTaxable, DiscountPct, and Unit Price.
- Store the Sales tax percentage in another table. Probably the invoice table.
Regarding your question 1:
There is a potential functional dependency between label, operation and type. For example, a discount might always imply subtraction and percentage. If so, the data model can be normalized by moving these fields to a separate table with label as a PK.
BTW, a de-normalized data model may be a legitimate tool for improving performance and/or simplicity.
Regarding your question 2:
Here is a model that allows easy "templating":
The final price of a product is calculated by applying the series of steps on PRICE, in order defined by STEP_NO. Multiple products can easily share the same "template" (i.e. the same PRICE_ADJUSTMENT_ID).
Regarding your questions 3 and 4:
You'd need to model a full expression tree, not just a "linear" series of steps. There are several ways to do that, most of them fairly complicated in relational paradigm. Perhaps the simplest one is to keep the data model similar to above, but treat it as Reverse Polish Notation.
For example...
A + B + (A + 10%) - (B - 5%)
...could be represented as:
OPERATION TYPE VALUE
---- ---- -----
value A
value B
add
value A
percentage 10
add
add
value B
percentage 5
subtract
subtract
Are you sure you actually need this kind of functionality?
If some price factors are dependent on the type of the item, then you'd have a set of price factors linked to entities in an ItemType table, and ItemType would be a property of the item entity (foreign key referencing ItemType). If other price factors are linked to the locale in which the item is being sold or shipped (e.g. sales tax), then those factors would be linked to Locale and would be invoked based on the customer's address. You would typically apply item-type factors at the line-item level, and locale-driven factors to the invoice total. Sin-tax would be linked to an ItemTypeLocale dyad, and applied at the line-item level.
1/ I think you also need to consider sequence
e.g. Price - discount + sales tax is obviously acceptable but Price +sales tax - discount is not nor is Price - (discount + sales tax)
2/ I would consider having price in another table. Is this not a detail of the item being sold? E.g. Widget, blue, $20.00. Whereas your factors are a detail of sales type. Presumably you could have one set of factors for a walk-in retail sale, another for a on-line sale and a third for a wholesale sale. You could calculate the actual price for these three sale types from the base price * factors.
3/ I think you need more tables; e.g. maybe Item, Sale type, and factor_details and factor_rules. It may be that your sale type is covered by your example of Luxury item in which case (if an item is only ever one sale type) this could be in the item table. Factor_rules would detail the calculation formula and factor_details the values.
I find this quite interesting. I would appreciate you updating this question with your experiences once you have worked this through.