I am relatively new to programming and databases.
I have a MySQL database with a "sales" table. This table lists all sales in a state (i.e. each record is a particular sale), with fields for sellername, and buyerzip. I would like to have another table "seller" that would include sellername, and also include fields to define the sellers market area by zip code - say 50+ zip codes define a market, and the seller could define multiple markets.
These market areas would be used for future queries: showing all sales in a particular market area.
Where do I start in terms of thinking how to store that "market area" data, and then use it for future queries?
Thanks
Table structure:
Sales table: ID, sellerID, buyerZipcode, amount
Seller table: ID, sellerName
Market table: ID, marketName
SellerMarketLink table: ID, sellerID, marketID
MarketZipCode table: ID, marketID, zipCode
Example data
Encoding the information in the first comment to your question
Market table
1, "Store A, first market"
2, "Store A, second market"
3, "Store B market"
MarketZipCode table
1, 1, 1
2, 1, 2
...
7, 1, 7
8, 1, 8
9, 2, 4
10, 2, 5
...
14, 2, 9
15, 2, 10
16, 3, 1
17, 3, 2
18, 3, 3
19, 3, 8
20, 3, 9
21, 3, 10
22, 3, 11
23, 3, 12
Query
Total sales in a market for each seller. Note that since one zip code can be part of several markets, the total of all values in "Total Sales" can be larger than the sum of all 'amount' values in the Sales table.
select Market.marketName, Seller.sellerName, sum(Sales.amount) as "Total Sales"
from Sales join Seller join SellerMarketLink join Market join MarketZipCode
on (Sales.sellerID = Seller.ID AND Seller.ID = SellerMarketLink.sellerID
AND SellerMarketLink.marketID = Market.ID
AND Market.ID = MarketZipCode.marketID
AND MarketZipCode.zipCode = Sales.buyerZipCode)
group by Market.ID, Market.marketName, Seller.ID, Seller.sellerName
order by Market.marketName, Seller.sellerName
I'm going to make the following assumptions here:
sellers have many markets
A market can span many zipcodes
Only one seller is responsible for a market
Markets can't overlap
Leading to the following structure...
sales table
seller | buyerzip
markets table
market | market_zipcodes_id
zipcodes table
market_id | zipcode
Hope that helps...
Related
I've got 2 tables -> apartments and availabilities.
In table apartments there are all informations about a apartment. In table availabilities there are all informations about the availability for a apartment for the next 365 days.
Table apartments:
id, name
Example:
1, Apartment 1
2, Apartment 2
Table availabilities:
id, apartment_id, date, status
Example:
1, 1, 2017-06-01, free
2, 1, 2017-06-02, booked
3, 1, 2017-06-03, free
4, 1, 2017-06-04, free
5, 1, 2017-06-05, free
...
6, 2, 2017-06-05, free
7, 2, 2017-06-05, free
8, 2, 2017-06-05, free
9, 2, 2017-06-05, booked
10, 2, 2017-06-05, free
...
Now i want search free apartments for a week in a multiple date range like:
SELECT apartments where id in (
SELECT apartment_id
FROM availabilities
WHERE EXISTS (
SELECT apartment_id
FROM availabilities as a
WHERE availabilities.apartment_id = a.apartment_id
AND date >= "2017-06-01"
AND date < "2017-06-08"
AND status = "free"
GROUP BY apartment_id
HAVING COUNT(apartment_id) = DATEDIFF("2017-06-08", "2017-06-01"))
OR EXISTS (
SELECT apartment_id
FROM availabilities as a
WHERE availabilities.apartment_id = a.apartment_id
AND date >= "2017-06-02"
AND date < "2017-06-09"
AND status = "free"
GROUP BY apartment_id HAVING COUNT(apartment_id) = DATEDIFF("2017-06-09", "2017-06-02"))`
This should lookup for all possibilities for a week in a date range (2017-06-01 - 2017-06-30)
My problem here is, we got many apartments and this query has a extreme load time.
Any ideas for a better solution?
Fixed it with (COUNT(*) and filter for status = "booked") = 0
Query is 30% faster.
I got a table named 'objects, events & events_types', and these tables got a structure (not the all structure of it, but the basic information which I need to display from the database) and some information like this:
Objects:
structure:
id(INT), title(TEXT)...
data:
1, House #1
2, House #2
3, House #3
Events:
structure:
id(INT), object_id(INT), event_type(INT)
data:
1, 1, 3
2, 1, 1
3, 2, 4
4, 2, 5
5, 2, 1
6, 1, 1
7, 2, 2
8, 1, 3
Events_Types:
structure: id(INT), name(VARCHAR)
data:
1, Alarm
2, Dis/Arm
3, Fire
4, Alarm button
5, Unknown
I have a page, which loads all data from events table, but I need the page displays me only those objects from objects table which has a data into events table. It won't load me id(3) object from objects table because there is no row into events table which has object_id 3.
I need a html table with the following columns:
objects.id
objects.title
events_types.name
count from events table where object_id of this table is the id of objects table and event_type from events table is got by $_GET['event_type'] from URL which will be defined as variable
Example of the table with information from database where $_GET['event_type'] = 1 and this '1' from events_types table is Alarm:
| 1 | House #1 | Alarm: 2 |
| 2 | House #2 | Alarm: 1 |
but the House #3 won't be displayed because there is no data for it in the events table.
For the information, in the real table I've got around 200 records in the objects table and around 500 records in the events table every month. It's like a statistic system for me.
From what you're describing, this sounds like a pretty straightforward join:
SELECT
objects.id,
objects.title,
events_types.name
FROM
objects INNER JOIN events on events.object_id = objects.id
INNER JOIN events_types on events_types.id = events.event_type;
This should give you one record for each event, which will be labeled with the name of that event type. Any objects that don't have events associated with them won't show up.
See if this query gets you the data you want:
SELECT O.id AS object_id, O.title, T.name, COUNT(*) as event_count
FROM Objects O
JOIN Events E ON O.id = E.object_id
JOIN Events_Types T ON E.id = T.id
GROUP BY O.id, E.event_type
I'm pretty sure I need a LEFT JOIN for this, but I have a snag. I need to pull one column from table B dependant on a column in table A.
TABLE A = list
list_id
user_id
operator_id
operator_name
operator_level
TABLE B = operators
operators_id
type
image
skill1
skill2
skill3
1
2
3
...
10
Here is the SQL Query that I have now:
SELECT * FROM list l
LEFT JOIN operators o ON l.operator_id = o.operators_id
WHERE l.user_id=1
ORDER BY o.10 DESC
It returns all of Table A, which I want, and also returns all of Table B, which I don't need.
The columns 1-10 contain INT values, and those columns correspond to the operator_level in Table A.
So really what I need is to create a temp column, and put whatever INT is in the 1-10 column that corresponds to the operator_level, or only return that column for that row. I have no idea how to do that though.
Here is some sample data, and expected results:
list list_id, user_id, operator_id, operator_name, operator_level
1, 1, 2, Johnson, Bob, 1
2, 1, 3, Mouse, Mickey, 9
3, 1, 2, Duck, Donald, 5
operators operator_id, type, image, skill1, skill2, skill3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
1, pilot, image.jpg, F16, B32, , 50, 60, 70, 80, 90, 100, 110, 120, 130, 140
2, medic, image.jpg, first aid, trauma, general surgery, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145
3, kitchen, image.jpg, knife, soup, , 20, 22, 24, 26, 28, 30, 32, 34, 36, 38
Expected Results list_id, user_id, operator_id, operator_name, operator_level, type, image, skill1, skill2, skill3, op_rate
1, 1, 2, Johnson, Bob, 1, pilot, image.jpg, F16, B32, 50
2, 1, 3, Mouse, Mickey, 9, medic, image.jpg, first aid, trauma, general surgery, 140
3, 1, 2, Duck, Donald, 5, kitchen, image.jpg, knife, soup, 28
Sorry, it lined up well in my editor, but lost the alignment. when I pasted.
Added sample data to sqlfiddle, don't know how to add expected results there.
http://sqlfiddle.com/#!2/390d8/1
You have the basics right withe the outer join, but what you can do is limit the fields you select in your query like this:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
`1`+`2`+`3`+...+`10` as opLevel
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
This is based on the fact that it would seem you have 0 for and or a value for the right operator.
Having said that, why on earth would you have ten fields to store one bit of information? Your table should only have the ONE field called something like "OperatorLevel" and have the value in it - unless I am missing something.
Alternately, you could also use a greatest() function in your query if you have multiple values and you want the highest one:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
greatest(`1`, `2`, ... `10`) as opLevel
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
Edit: Okay, based on additional information you can use the following (ick ick ick) statement:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
case
when l.operator_level=1 then o.`1`
when l.operator_level=2 then o.`2`
when l.operator_level=3 then o.`3`
// etc etc yuck!
when l.operator_level=10 then o.`10`
end as yicky
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
Edit 2:
I would very much suggest a data normalisation.
Given what you have as data, it seems that you are duplicating data to all sorts of users. This kinda sounds like a nightmare to update. From what I understand, it seems that there is a pay scale for each "skill" and it goes up based on the level of the operator?
I would make a table with the following structure (assumptions based on user 1):
skill level value
B32 1 25
B32 2 30
.....
B32 10 75
F16 1 25
F16 2 30
...
F16 10 75
Then you could simply perform a link from the "list" table to the "operators" table, and then link to the "grades" table based on data within then.
This would make for a much simpler query.
Having said ALL THAT, I would actually look at normalizing your "operators" table down to skills only. Move the "type" and "image" into the "list" table (and call it users while you are at it).
Now the "operators" table should be renamed to a skillset table with data like this:
userID skill Level
1 F16 4
1 B32 8
2 F_Aid 2
This would allow you to have users with more than 3 skills as well as allowing you to easily record each skill level of the user. They might be a superhero at B32, but only mediocre at F16.
Assuming only one int column has value maybe something like this?
SELECT l.*, COALESCE(1,2,3,4,5,6,7,8,9,10) opLevel
FROM list l
LEFT JOIN operators o ON l.operator_id = o.operators_id
WHERE l.user_id=1
ORDER BY COALESCE(1,2,3,4,5,6,7,8,9,10) DESC
Edit:
I removed the confusing stuff and I wanted to simplify what I want to accomplish:
Let's say John had 2 baskets and 1 of them contained 1 stone, 2 marbles and 2 sticks, the other one contained 2 stones and 2 sticks.
Eric had 1 basket which contained 3 bottles, 2 caps and 1 stone.
Meanwhile Jack had 1 basket which contained 1 stick. Also, Jack has a stone somewhere but it's not inside his basket yet.
For the purpose of this query, let's say we're focusing on Jack's items. So we would go over everybody's baskets and see which basket looks pretty much the same as all the items he has overall (a) without thinking whether Jack's items are inside a basket or not and (b) only compare to Jack's items with items that are inside other people's baskets, not outside.
This would be the ideal end result when searching which baskets Jack can match the easiest with all his items regardless of where his items are currently:
Fact:
Jack has in total 1 stick and 1 stone.
Result:
1st: John's second basket containing 2 stones and 2 sticks
2nd: John's first basket containing 1 stone, 2 marbles and 2 sticks
3rd: Eric's basket containing 3 bottles, 2 caps and 1 stone
This will do what you asked for a single basket:
SELECT SUM(item.qty/(SELECT sum(qty) total FROM basket WHERE bid = xxx)
* 1/all_item.qty) likeness, basket.bid, all_basket.bid all_bid
FROM basket JOIN item USING (bid)
LEFT JOIN
(basket all_basket JOIN item all_item USING (bid))
USING (iid)
WHERE basket.bid = xxx
GROUP BY basket.bid, all_basket.bid
ORDER BY likeness DESC
It's probably possible to join this to the users table to do all his baskets at once. Then wrap it in yet another query to find the best match.
Try this first, and let me know if it works. If it does I'll see if I can do that part tomorrow.
Whilst I'm not sure that there's a simple way to calculate basket likeness as a percentage using SQL, I assume you are simply trying to product product recommendations.
You could formulate a query to produce product recommendations based on the current basket. You can do this by:
Finding any basket that contains at least one item from the current customer's basket.
Limiting the results to products that are in the same category.
You can fetch the appropriate information with the following query:
SELECT b1.basket_id, b1.product_id, b1.quantity, p1.product_category FROM baskets b1
INNER JOIN products p1 ON p1.product_id = b1.product_id
WHERE b1.product_id IN
(
SELECT b2.product_id FROM baskets b2
INNER JOIN products p2 ON p2.product_id = b2.product_id
WHERE b2.basket_id = $basket
AND p2.product_category = p1.product_category
)
AND b1.basket_id != $basket;
This will return the basket ID, product ID, quantity and product category ID for all items in all other baskets that share at least one product with your selected basket. It also filters the results so that the suggested products are always in the same category as the source product.
Here's a sample:
baskets table:
basket_id | product_id | quantity
1, 2, 1
1, 1, 2
1, 3, 5
2, 5, 1
2, 6, 1
3, 1, 1
3, 2, 1
3, 4, 1
products table:
product_id | product_name | product_price | product_category
1, 'cat1 prod1', 14, 1
2, 'cat1 prod2', 1.5 1
3, 'cat1 prod3', 2, 1
4, 'cat2 prod1', 22, 2
5, 'cat2 prod2', 6, 2
6, 'cat2 prod3', 45, 2
7, 'cat3 prod1', 24, 3
8, 'cat3 prod2', 55.4, 3
9, 'cat3 prod3', 22, 3
result of query:
basket_id | product_id | quantity | product_category
3, 1, 1, 1
3, 2, 1, 1
Hopefully that's of some use to you.
good pm. i was thinking is it possible to show the summation or grand total of a selected field in the table and with relation to date:
for example is i want to know the total beer consumption of my hotel every month.
i have here my table on services:
[services_id[pk],
customer_id[fk],
date_in,date_out,room_type,room_number,
extra_ref,
extra_bed,extra_snack,
extra_beer,extra_softdrinks,
extra_pillows,extra_breakfast,
extra_snack_q,
extra_beer_q,
extra_softdrinks_q,
extra_pillows_q,
extra_breakfast_q]
can you give some advice on how can i get it.
thanks in advance:
-renz
SELECT SUM(beer_amount) as monthly_beer_amount
FROM [DATABASE].[TABLE]
WHERE beer_sold_date BETWEEN '20110201' AND '20110228'
[EXTRA INFO]
Also I believe that the best way to organize this table is to separate out this table into a few other tables. Store customer info in the first table such as
[customer_id, customer_name, date_in, date_out, room_type, room_number].
1, Bob, 20110101, 20110110, big, 200
2, Joe, 20110101, 20110110, small, 202
....
And have another table named something like room_items which would have the following,
[id, item_name]
1, BEER
2, BED
3, SNACK
...
And then another table named room_purchases which will have the following,
[customer_id, purchase_id, amount, date....]
1, 1, 10, 20110101
2, 3, 5, 20110101
3, 1, 9, 20110101
....
This would help you to do a join on all three tables and they would be more normalized in this way.
SELECT SUM(t2.amount) as beer_amount
FROM customers as t1
LEFT JOIN room_purchases as t2 ON t1.customer_id = t2.customer_id
WHERE t2.purchase_id = 1 AND t2.date BETWEEN 20110101 AND 20110131
SELECT DATE_FORMAT(date_in,"%m-%Y") AS month,SUM(extra_beer) AS beers
FROM your_table
GROUP BY month
this will return something like this:
02-2011 | 43
03-2011 | 52
if you want to limit the query depending on the date just add a WHERE constraint before the GROUP BY