MYSQL Query matching column names to rows in another table - mysql

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.

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 Customers Who Have Not Bought

Background:
We are setting up a promotions system to give away free products to registered customers. We're trying to design a database which is flexible enough to handle multiple products, and giveaways. The requirements are that products may be given away on a drip basis on a first come basis to qualified customers.
Example:
Apple wants to give away 1000 ipads in March.
They want to give away maximum of 1 per hour.
They want to give it to customers who are in California or New York.
They want to limit how many free ipads a customer can get (limit 1 per 15 days).
Data Structure:
Products - 1 entry per unique product. e.g. Apple iPad
ProductGiveAways
ProductID: AppleIpad
Quantity:1000
StartDate: 03/01/2014
End Date 03/31/2014
CustomerState: California,NewYork
PurchaseLimitDays: 15
Problem:
With the above structure we are able to do a query against our customers table and find out which are qualified for the promotion.
What I cannot figure out is the best way to:
Query customers in California or New York (is this a good use case for a join and another table?)
When a customer logs in to see what free items are not available to him, how can I exclude the Apple iPad if the customer has already gotten this freebie?
In other words:
Say amazon.com wants to show me DVDs which I have not already bought. What is the proper way to query that?
Is the right approach to first get a list of previously bought products and then Query with a NOT clause?
I'm assuming you'll have a table for what has been given away. In this table I would include a column for recipient id which can map back to the customer table. You can then create queries to find eligible recipients by searching for customers who have not met disqualifying conditions.
select customerid
from customer
where customerid not in (
select recipientid
from givenaway
where ..... and ....
)
Because there's not a definitive data structure defined, I'm going to use the following which you can tailor to whatever data structure you have designed yourself:
Product
ProductId - INTEGER (IDENTITY and PRIMARY KEY)
ProductName - VARCHAR
States
StateId - INTEGER (IDENTITY and PRIMARY KEY)
StateName - VARCHAR
Customer
CustomerId - INTEGER (IDENTITY and PRIMARY KEY)
StateId - INTEGER (FOREIGN KEY)
Promotion
PromotionId - INTEGER (IDENTITY and PRIMARY KEY)
ProductId - INTEGER (FOREIGN KEY)
Quantity - INTEGER
StartDate - DATETIME
End Date - DATETIME
PurchaseLimitDays - INTEGER
PromotionState
PromotionId - INTEGER (FOREIGN KEY)
StateId - INTEGER (FOREIGN KEY)
So in answer to your questions:
Query customers in California or New York (is this a good use case for a join and another table?)
Personally I would join to a centralized state table (PromotionState) in my above example, I'm sure there's a better way but you could do a condition such as:
WHERE
(SELECT COUNT * FROM PromotionState x WHERE x.PromotionId = p.PromotionId) = 0
OR NOT(ps.PromotionId IS NULL)
Alternatively you could do a GROUP BY and HAVING, using all the other columns as the items to GROUP BY and something like HAVING COUNT * = 0 OR HAVING SUM CASE WHEN (Conditions met) THEN 1 ELSE 0 END = 0
When a customer logs in to see what free items are not available to him, how can I exclude the Apple iPad if the customer has already gotten this freebie?
Say amazon.com wants to show me DVDs which I have not already bought. What is the proper way to query that?
As I've said you could use GROUP BY and HAVING to determine whether an item has been previously "won" by either using COUNT or SUM
Is the right approach to first get a list of previously bought products and then Query with a NOT clause?
There are probably better ways, sub queries can get very heavy and sluggish, I'd recommend trying some of the above techniques and then using a profiler to hopefully make it more efficient.
Some database design
First, when you set the CustomerState to California,NewYork you are violating the First Normal Form of database design.
So let's reorganize your domain model.
State - 1 Entry per unique state
...
Customer - 1 Entry per unique customer
StateId: (California|NewYork|...)
...
Product - 1 Entry per unique product
...
ProductGiveAways - Many entries per product
ProductID
Quantity
StartDate
End Date
PurchaseLimitDays
...
ProductGiveAways_State
ProductGiveAwaysId
StateId
...
Customer_Product - 1 Entry per bought product by customer
CustomerId
ProductId
PurchaseDate
...
Technical issues
When you want to query custoners in California or New York, all you have to do now is :
// This is just an example, you have to change the 'California', 'New York' with their ids
SELECT * FROM Customer WHERE StateId IN ('California', 'New York')
When a customer logs in to see what free items are available to him :
// It's not an accurate sql, just an example
SELECT Product.*
FROM Product
JOIN ProductGiveAways ON ProductId
JOIN ProductGiveAways_State ON ProductGiveAwaysId
WHERE ProductId NOT IN (
SELECT ProductId FROM Customer_Product JOIN ProductGiveAways ON ProductId
WHERE CustomerId = /* the customer id */
AND ( TO_DAYS(now()) - TO_DAYS(PurchaseDate) ) < PurchaseLimitDays
)
AND StateId = /* customer StateId */
AND StartDate < now() < End Date // Elligible ProductGiveAways
For Laravel We Use Something Like this, i hope you can relate to this query or you can use online laravel query converter for using it in mysql ( orator )
$user_id = auth()->user()->id;
Product::where('status', 'active')->whereNotIn('id', function($query) use ($user_id) { $query->select('product_id')->from(new OrderProduct->getTable())->where('user_id', $user_id)->where('status', 'delivered'); });

What table structure would best fit this scenario?

I am developing an evaluation system for different programs that needs a lot of flexibility. Each program will have different things to track, so I need to store what data points they want to track, and the corresponding data for the person being evaluated on the particular data point. I am guessing several tables are appropriate. Here is a general outline:
Table: accounts
- unique ID assigned to each account. We'll call this 'aid'
Table: users
- each user with unique ID.
Table: evaluation
- each program will enter in the metrics they want to track into this table (i.e attendance)
- column 'aid' will correspond to 'aid' in account table
Table: evaluation_data
- data (i.e attendance) entered into this database
- column 'aid' will correspond to 'aid' in account table
- column 'uid' will correspond to 'uid' in user table
The input form for evaluation_data will be generated from what's in the evaluation table.
This is the only logical way I can think of doing this. Some of these tables will be growing quite large over time. Is this the most optimal way of doing this?
I'm a little confused about how accounts, users and programs all relate to each other and whether or not account and program are the same thing and that you used the terms interchangeably. I'm going to use different terms which are just easier for me to understand.
Say you have a website that allows freelancers to keep track of different projects and they can create their own data to track. (Hope you see the similarity)
Tables...
freelancers
id title etc
projects
id freelancer_id title description etc
data_options
id freelancer_id title
You can even add other columns like data_type and give options like URL, email, text, date, etc which can be used for validation or to help format the input form.
example data:
1 5 Status
2 5 Budget
3 5 Customer
4 99 Job Type
5 99 Deadline
6 102 Price
7 102 Status
8 102 Due By
This display 3 different freelancers tracking data, freelancers with the id's 5, 99, and 102. Deadline and Due By are essentially the same but freelancers can call these whatever they want.
data_values
id project_id option_id option_value
a column freelancer_id as you would be able to to a join and get the freelancer_id from either the project_id or the option_id
example data:
1000 1 2 $250
1001 1 1 Completed
1002 1 3 Martha Hayes
This is only showing information freelancer with the id 5 has input because option_id's 1-3 belong to that user.

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.