I have a table of PRODUCTS
+------------+---------------+---------------+
| ProductCod | unitPrice | name |
+------------+---------------+---------------+
| 1 | 30 | some |
| 2 | 20 | poor |
| 3 | 10 | example |
+------------+---------------+---------------+
Another of SALES (which I believe it's not needed) and some other to register a n..m relationship
+------------+----------+------------+
| quantity | sellCode | productCod |
+------------+----------+------------+
| 3 | 1 | 1 |
| 5 | 1 | 2 |
| 4 | 2 | 2 |
| 4 | 2 | 3 |
| 4 | 2 | 3 |
+------------+----------+------------+
How can I select a list of products and how many were sold at all registers?
I would like something like:
+---------+------+
| name | sold |
+---------+------+
| some | 3 |
| poor | 9 |
| example | 8 |
+---------+------+
Using a INNER JOIN is more exact here.
Query
SELECT
products.name
, SUM(sales.quantity) AS sold
FROM
products
INNER JOIN
sales
USING(productCod)
GROUP BY
products.name
ORDER BY
products.ProductCod ASC
Or
SELECT
products.name
, SUM(sales.quantity) AS sold
FROM
products
INNER JOIN
sales
ON
products.productCod = sales.productCod
GROUP BY
products.name
ORDER BY
products.ProductCod ASC
Results
| name | sold |
|---------|------|
| some | 3 |
| poor | 9 |
| example | 8 |
see demo http://sqlfiddle.com/#!9/10cd3e/5
select p.Name, sum(s.Quantity) as sold
from Products p
left join Sales s on p.ProductCod = s.ProductCod
group by p.ProductCod, p.Name;
What we are doing is first to join two tables using productCod common field. We are using LEFT join, because there might be products that are not sold at all yet. Then we sum the quantities grouping by productCod (and Name. We had to include it in the list because it is not an aggregation expression - and there is a single Name per productCod). This works right, because there is a 1 to many relation between products and sales. If there were a many to many relation then the result would be wrong.
EDIT: Check this SQLFiddle sample for a good formatting.
Related
I'm currently learning the ropes of SQL and i have an tutorial from school that goes like this:
All stores (storeid) sells (productid, storeid) some products (productid)
A store is considered a monopoly if every product they sell is not sold by any other store.
How do I find the monopolies?
I was thinking of selecting the storeid from 2 of the same tables, but I'm not sure how to continue from there on.
Tables are below:
Store:
+-----------+
| storeid |
+-----------+
| --------- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+-----------+
Products:
+-------------+
| productid |
+-------------+
| --------- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+-------------+
Sells:
+--------------------------+
| productid | storeid |
+--------------------------+
| -----------+------------ |
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 3 | 2 |
| 1 | 2 |
| 3 | 3 |
| 2 | 4 |
| 4 | 4 |
| 5 | 5 |
| 6 | 5 |
+--------------------------+
So by my count, only store 5 is considered a monopoly, because they sell products that are not available in other stores.
We can try a self join approach combined with aggregation:
SELECT t1.storeid
FROM yourTable t1
LEFT JOIN yourTable t2
ON t2.productid = t1.productid AND
t2.store_id <> t1.storeid
GROUP BY t1.storeid
HAVING COUNT(t2.storeid) = 0;
The approach here is to try to match each row in Sells to some other row on the condition that it is the same product, but is being sold by some other store. A matching store is one for which none of its products are being sold by other stores, so the count of the second table column in the join should be zero.
Use window functions and aggregation:
select s.storeid
from (select s.*,
count(*) over (partition by productid) as num_stores
from sells s
) s
group by s.storeid
having max(num_stores) = 1;
This should be much faster than a self-join. It is also almost a direct translation of your question. The subquery counts the number of stores where each product is sold. The outer query selects stores where all products are sold in one store.
I have two tables product and review, some products have reviews, others do not. I want to display the products and for products which have reviews I want to calculate their average rating, if a product has no reviews the average rating should be displayed as null.
Example:
Product:
| Id | Name |
| 1 | dog |
| 2 | cat |
Review:
| Id | Rating|
| 1 | 1 |
| 1 | 5 |
What should be displayed:
| Id | Name | Avg_rating |
| 1 | dog | 3 |
| 2 | cat | NULL |
How can this be achieved?
You can left join, aggregate and round():
select p.id, p.name, round(avg(r.rating)) avg_rating
from product p
left join review r on r.id = p.id
group by p.id, p.name
I have a set of MySQL three tables in a "has many" relationship: deals, orders, and coupons.
Deals
|----|--------------|
| id | title |
|----|--------------|
| 1 | Some deal |
| 2 | Another deal |
|----|--------------|
Orders
|----|---------|-----------|
| id | deal_id | state |
|----|---------|-----------|
| 1 | 1 | purchased |
| 2 | 1 | purchased |
| 3 | 1 | expired |
| 4 | 2 | purchased |
|----|---------|-----------|
Coupons
|----|----------|
| id | order_id |
|----|----------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 4 |
|----|----------|
So, deals have many orders, which have many coupons.
What I'd like to do is select on the deals table while counting the number of purchased orders and coupons.
I know how to get a count on paid orders already:
SELECT deals.*, count(orders.id) AS orders_purchased_count FROM deals
LEFT JOIN orders ON deals.id=orders.deal_id AND orders.state='purchased'
WHERE deal_id < 3
GROUP BY deals.id
Deals
|----|--------------|------------------------|
| id | title | orders_purchased_count |
|----|--------------|------------------------|
| 1 | Some deal | 2 |
| 2 | Another deal | 1 |
|----|--------------|------------------------|
Similarly, I can get a count of coupons for orders:
SELECT orders.*, count(coupons.id) AS coupons_count FROM orders
LEFT JOIN coupons ON orders.id=couoons.orders_id
WHERE orders.state='purchased'
GROUP BY orders.id
Orders
|----|-----------|---------------|
| id | state | coupons_count |
|----|-----------|---------------|
| 1 | purchased | 3 |
| 2 | purchased | 2 |
| 4 | purchased | 1 |
|----|-----------|---------------|
My question is: How do I combine these so that I can add coupons_count next to orders_purchased_count?
Deals
|----|--------------|------------------------|---------------|
| id | title | orders_purchased_count | coupons_count |
|----|--------------|------------------------|---------------|
| 1 | Some deal | 2 | 5 |
| 2 | Another deal | 1 | 1 |
|----|--------------|------------------------|---------------|
The tricky thing, in my case, will be to run the WHERE deal_id < 3 filter when selecting from deals before I join on orders and to run the WHERE orders.state='purchased' filter when selecting from orders before I join on coupons. It's a large dataset and I don't want to load all my orders and coupons into memory for the purpose of joining.
At a loss for how to do this.
Does this work? I couldn't understand your concerns which part was going to be tricky.
SELECT deals.*, COUNT(DISTINCT(orders.id)) AS orders_purchased_count, COUNT(coupons.id) AS coupons_count
FROM deals
LEFT JOIN orders
ON deals.id=orders.deal_id
AND orders.state='purchased'
LEFT JOIN coupons
ON orders.id=coupons.orders_id
WHERE deal_id < 3
GROUP BY deals.id;
Try this one with a co related subquery and join
SELECT deals.*, count(orders.id) AS orders_purchased_count
(
SELECT count(id) FROM coupons WHERE orders_id = orders.id
) AS coupons_count
FROM deals
LEFT JOIN orders ON deals.id=orders.deal_id AND orders.state='purchased'
WHERE deal_id < 3
GROUP BY deals.id
I have a jobs table which looks something like this:
**jobs**
job_id | customer_id | date
------------------------------------
| 1 | 1 | 2012-01-03 |
| 2 | 2 | 2013-02-04 |
| 3 | 1 | 2013-03-05 |
| 4 | 3 | 2013-05-04 |
Then I have a products table which looks something like this:
**products**
product_id | description | price
-----------------------------------
| 1 | prod_1 | 25.50 |
| 2 | prod_2 | 34.95 |
And finally when prices are changed I have a product_price_changes table like this:
**product_price_changes**
price_change_id | product_id | price_change_date | old_price
---------------------------------------------------------------
| 1 | 1 | 2013-01-01 | 20.00 |
| 2 | 1 | 2013-02-05 | 23.00 |
with a UNIQUE INDEX on (product_id,price_change_date)
I want to create a VIEW which grabs the product pricing reflecting the prices from the date the job was done.
which, with the data above, should create a table like this:
**view_job_pricing**
job_id | product_id | price
------------------------------
| 1 | 1 | 20.00 |
| 1 | 2 | 34.95 |
| 2 | 1 | 23.00 |
| 2 | 2 | 34.95 |
| 3 | 1 | 25.50 |
| 3 | 2 | 34.95 |
| 4 | 1 | 25.50 |
| 4 | 2 | 34.95 |
So it should select the product price change with the highest date, which is still less than the job date, if it exists, otherwise it should grab the current product price.
I have this which works:
CREATE VIEW view_job_pricing
AS
SELECT j.job_id, p.product_id, MAX(price_change_date),
IFNULL(ppc.old_price,p.price) AS price
FROM products p
JOIN jobs j
LEFT JOIN product_price_changes ppc
ON p.product_id = ppc.product_id AND date < price_change_date
GROUP BY job_id, product_id;
But it is a pretty slow on the real database (far more jobs and products). Just wondering if there is a better way. Thanks!
Try doing this as a correlated subquery. You are allowed subqueries in the select clause of a view, and the correlated subquery should use the index on product.
CREATE VIEW view_job_pricing
AS
SELECT j.job_id, p.product_id, MAX(price_change_date),
coalesce((select ppc.price
from product_price_changes ppc
where p.product_id = ppc.product_id AND j.date < ppc.price_change_date
order by ppc.price_change_date desc
limit 1
),
p.price) AS price
FROM products p cross join
jobs j
You can simplify the processing and improve the performance by changing the structure of the product_price_changes table. Instead of having only an effective_date, also have an end_date. With effdate and enddate columns, the query would be much faster and simpler.
In addition to indexing all the involved columns, and checking your indexing results using EXPLAIN EXTENDED (which you can post here for review), you can try speeding up the view by defining it as CREATE ALGORITHM = MERGE VIEW. There are a number of restrictions on when this can be done, so YMMV
[http://dev.mysql.com/doc/refman/5.0/en/create-view.html][1]
I am trying to get a result from two tables without having a second query nested inside the first query loop.
I have a table products:
product_code_1234 | product_name | otherfields...
And a table categories, where a product can have multiple categories:
category_name_1 | product_code_1234
category_name_2 | product_code_1234
category_name_3 | product_code_1234
Is there a query to get the following result?
product_code_1234 | product_name | ... | category_name_1 | category_name_2 | category_name_3
select * from a,b
will give you all data from table a ,combined with all data from table b.
However if you don't want repetition of data and there isn't any connection between table a and b it can't be done without some union or similar
Assume you have these tables:
+----------------------------+
| PRODUCTS |
+------+-------------+-------+
| code | name | price |
+------+-------------+-------+
| 1 | Bike Helmet | 99.99 |
| 2 | Shirt | 19.99 |
+------+-------------+-------+
+-------------------+
| CATEGORIES |
+------+------------+
| code | category |
+------+------------+
| 1 | Sports |
| 1 | Cycling |
| 1 | Protection |
| 2 | Men |
| 2 | Clothing |
+------+------------+
Here is a query based on another SO answer, that would match your desired result, if my interpretation of it is correct:
SELECT p.code, p.name, p.prize,
(SELECT category FROM categories c WHERE c.code = p.code LIMIT 0, 1) as category1,
(SELECT category FROM categories c WHERE c.code = p.code LIMIT 1, 1) as category2,
(SELECT category FROM categories c WHERE c.code = p.code LIMIT 2, 1) as category3,
FROM products p
This is the result:
+------+-------------+-------+-----------+-----------+------------+
| code | name | price | category1 | category2 | category3 |
+------+-------------+-------+-----------+-----------+------------+
| 1 | Bike Helmet | 99.99 | sports | cycling | protection |
| 2 | Shirt | 19.99 | men | clothing | NULL |
+------+-------------+-------+-----------+-----------+------------+
It is not possible, though, to have a dynamic number of categories in the result. In this case I limited the number to 3, like in your question. There might still be a solution with better performance. Also, this is obviously a nested query and therefore probably not suited for your needs. Still, it's the best I could come up with.
JOIN
There is also the SQL JOIN clause, which might be what you are looking for:
SELECT *
FROM products
NATURAL JOIN categories
You would end up with this result:
+------+-------------+-------+------------+
| code | name | price | category |
+------+-------------+-------+------------+
| 1 | Bike Helmet | 99.99 | sports |
| 1 | Bike Helmet | 99.99 | cycling |
| 1 | Bike Helmet | 99.99 | protection |
| 2 | Shirt | 19.99 | men |
| 2 | Shirt | 19.99 | clothing |
+------+-------------+-------+------------+
I guess that you will have to do two separate queries.
One to retrieve products, one to retrieve the product categories.
Then use any scripting language (like PHP) to achieve what you want with the results (display, export, whatever).