I have three tables:
Items:
Id |Name | Price | Cat_Id
---- |-------|----------------
1 | Item1 | 50.00 | 1
2 | Item2 | 25.20 | 5
Category:
Id |Name |
---- |------|
1 | Cat1 |
2 | Cat2 |
Discount:
Id |Item_Id| Client_Id|Discount
---- |-------|----------------
1 | 1 | 1 | 10
2 | 1 | 2 | 15
3 | 2 | 2 | 6
I am trying to get all items with proper discount, which is different for every customer. In this example i have client with Client_Id 1, which have discount for Item1/10/ and he doesn`t have discount on Item2.
The result should look like this:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
My question is what is the way to build the query. I join first two tables and need to filter the third, but should I use temp table or do query in query?
It's Simple SQL Query.
Select Id, Name, Price, Cat, Discount From Items
left join discount on Items.id=discount.Item_Id
left join category on Items.cat_id=id
Output Like:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
Try this way:
select di.id,di.Client_Id,it.Name,it.Price,ca.Cat,ifnull(di.Discount,0)
from Discount di
right join Items it on di.Item_id=it.Id
left join Category ca on it.Cat_Id=ca.Id
order by di_Id,di.Client_id,It.Name
You get all rows from discount and then get all items and look for the category. If an item has no discount(null) you get a 0
To get the result you want,
following is the query...
select
i.id item_id,
d.id discount_id,
i.name,
i.price,
c.name cat_name,
d.discount
from items i
left join discount d on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 1 or d.client_id is null;
/*where condition added after update as OP required*/
UPDATE
As per OP's comment
select
i.id item_id,d.id discount_id,
i.name,i.price,c.name cat_name,d.discount
from discount d
left join items i on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 2;
Related
I have 2 Tables named Product, Stock as follows
Product Table:-
Id Name Hand
1 A 50
2 B 5
3 C 10
Stock Table:-
Id Pid Qty
1 1 50
I want total stock of every product so hand column showing the opening stock.
I tried:
select product.id, name, hand, count(qty)
from product
left join stock on pid=product.id
but it's giving me only the first product detail even i am doing left join
You must group by product and use sum() instead of count():
select p.id, p.name, p.hand, coalesce(sum(qty), 0) totalstock
from product p left join stock s
on s.pid = p.id
group by p.id, p.name, p.hand
See the demo.
Results:
| id | name | hand | totalstock |
| --- | ---- | ---- | ---------- |
| 1 | A | 50 | 50 |
| 2 | B | 5 | 0 |
| 3 | C | 10 | 0 |
I would like to count the number of rows or in this case the number of products which are in the same category and return zero if there are no products in the category.
The tables in my query looking like this:
category category_lang media
------------ ------------- ---------
category_id | published category_id | name | alias media_id | category_id
----------------------- -------------------------- ----------------------
1 | 1 1 | One | one 1 | 1
2 | 1 2 | Two | two 2 | 2
3 | 1 3 | Three| three 3 | 3
media_lang product_category
------------------------------- ----------------
media_id | url | file_name product_id | category_id
------------------------------- ------------------------
1 | /images/ | file1.jpg 1 | 1
2 | /images/ | file2.jpg 2 | 1
3 | /images/ | file3.jpg 3 | 2
and I would like a result like this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 2
2 | Two | two | 2 | /images/ | file2.jpg | 1
3 | Three | three | 3 | /images/ | file3.jpg | 0
My query currently looks like this
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE c.`published` = 1
ORDER BY ca.`name`
my logic is obviously wrong because this query will return this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 3
How can I achieve the desired result?
I think the most painless (and sane) way to approach this would be to just aggregate the product counts for each category from the product_category table in a separate subquery, and then just join this to what you already have:
SELECT
c.category_id,
ca.name,
ca.alias,
m.media_id,
ma.url,
ma.file_name,
COALESCE(t.cat_count, 0) AS cat_count
FROM category c
LEFT JOIN category_lang ca
ON c.category_id = ca.category_id
LEFT JOIN media m
ON c.category_id = m.category_id
LEFT JOIN media_lang ma
ON m.media_id = ma.media_id
LEFT JOIN
(
SELECT category_id, COUNT(*) AS cat_count
FROM product_category
GROUP BY category_id
) t
ON c.category_id = t.category_id
WHERE c.published = 1
ORDER BY ca.name
Note here that your product_category table does not have any category entries with no products. This is not a problem, because in the LEFT JOIN we can simply treat a NULL count as being zero. A NULL value would occur if a given category did not match to anything in the subquery.
Seems to me that it is where you are doing the count and that you doing have the criteria for doing the count. Adding an extra condition where product category id equals category id in your category table should fix the incorrect count.
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE (c.`published` = 1) and (c.`category_id` = p.`category_id`)
ORDER BY ca.`name`
I currently have a query similar to:
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName
order by boughtTotal desc;
The purpose of the query is to relate customers to items they bought, which may span over many orders, and total the amount of each unique item bought. This being achieved with what I have. I want to take this a step further now and select the most popular item for each customer. Since this is ordered with most bought items at the top, I figured I'd just have to add distinct next to customers.customerId in the select statement to have duplicates removed. However, adding distinct seems to do nothing. I'd appreciate help in knowing why distinct is seemingly doing nothing here, but also how to achieve what I'm trying to do - remove duplicates besides a customer's most popular item.
Tables:
customers
customerId | name
1 | John
2 | Jane
orders
orderId | customerId | quantity | itemId
1 | 1 | 11 | 1
2 | 2 | 13 | 2
3 | 1 | 4 | 2
4 | 2 | 14 | 1
5 | 1 | 1 | 1
items
itemId | itemName
1 | dog
2 | cat
So from this data the current query will return the following:
customerId | itemName | boughtTotal
2 | dog | 14
2 | cat | 13
1 | dog | 12
1 | cat | 4
And what I'd like to have is the following:
customerId | itemName | boughtTotal
2 | dog | 14
1 | dog | 12
Try this;)
select t1.*
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName) t1
inner join (
select max(boughtTotal) as boughtTotal, customerId
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName)t
group by customerId) t2 on t1.customerId = t2.customerId and t1.boughtTotal = t2.boughtTotal
DEMO HERE
I have two tables:
Products:
+-------------------------------------------------+
| id | name | category | price |
+-------------------------------------- ----------+
| 1 | item1 | 1 | 0.99 |
| 2 | item2 | 2 | 1.99 |
| 3 | item3 | 3 | 2.95 |
| 4 | item4 | 4 | 2.50 |
+-------------------------------------------------+
Images:
+--------------------------------------------------+
| id | file_name | p_id | priority |
+-------------------------------------- -----------+
| 1 | image1 | 1 | 0 |
| 2 | image2 | 1 | 1 |
| 3 | image3 | 2 | 2 |
| 4 | image4 | 3 | 2 |
| 5 | image5 | 3 | 3 |
| 11 | image6 | 3 | 5 |
| 16 | image7 | 4 | 1 |
| 19 | image8 | 4 | 7 |
+--------------------------------------------------+
I need to get all of product information, as well as the file name of an image for the product. Notice that a product can have more than one image; I want the one with the lowest priority. Also, I only want results for products that are in a certain category.
So, say I need information for products in categories {1,2,3}, then after the query runs the result should return:
+-----------------------------------------------------------------+
| id | name | category | price | file_name |
+-------------------------------------- ----------+---------------+
| 1 | item1 | 1 | 0.99 | image1 |
| 2 | item2 | 2 | 1.99 | image3 |
| 3 | item3 | 3 | 2.95 | image4 |
+-------------------------------------------------+---------------+
I have tried writing a couple of different join statements, but none of them work; not surprising, since I'm a total novice when it comes to SQL.
Any help would be greatly appreciated!
I will add a step by step tutorial, first getting the join right,
then adding some conditions to filter the category and finally, grouping
and using the having clause with a sub-select. You will need to use the last select
in your code. I also tested this on a mysql instance and it works.
I'm using group by in case you need some other complex stuff. It's good to have an example.
The syntax is ansii sql, it should work on all databases not just mysql
-- get everything by joining
select p.*, i.file_name
from products p
join image i on (p.id = i.p_id)
/* get everything by joining
* + filter by category
*/
select p.*, i.file_name
from products p
join image i on (p.id = i.p_id)
where p.category in (1,2,3)
/* get everything by joining
* + filter by category
* + image is the one with the lowest priority
* note: selecting the priority is not necessary
* but it's good for demonstration purposes
*/
select p.*, i.file_name, i.priority
from products p
join image i on (p.id = i.p_id)
where p.category in (1,2,3)
group by p.id
having i.priority = (select min(priority) from image where p_id = p.id)
This is the answer:
select a.id, a.name, a.category, a.price, b.filename as file_name
from products a left join (
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q
left join images i on q.id = i.id
) b on a.id = b.p_id
where a.category in (1, 2, 3);
EXPLANATION:
First, you need to get a set where for each products with lowest priority, which is from this query:
select id, min(priority) as min_p from images group by p_id;
The result will be:
+----+----------+
| id | lowest_p |
+----+----------+
| 1 | 0 |
| 2 | 2 |
| 3 | 2 |
| 4 | 1 |
+----+----------+
4 rows in set (0.00 sec)
The next step will be to get an outer join, in this case I'd choose (arbitrarily according to my preference), the left join:
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q left join images i on q.id = i.id;
This query produce what you want in short:
+------+----------+
| p_id | filename |
+------+----------+
| 1 | image1 |
| 2 | image3 |
| 3 | image4 |
| 4 | image7 |
+------+----------+
4 rows in set (0.00 sec)
Now you just need to decorate this, again using left join:
select a.id, a.name, a.category, a.price, b.filename as file_name
from products a left join (
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q
left join images i on q.id = i.id
) b on a.id = b.p_id
where a.category in (1, 2, 3);
And you'll get what you want:
+------+-------+----------+-------+-----------+
| id | name | category | price | file_name |
+------+-------+----------+-------+-----------+
| 1 | item1 | 1 | 0.99 | image1 |
| 2 | item2 | 2 | 1.99 | image3 |
| 3 | item3 | 3 | 2.95 | image4 |
+------+-------+----------+-------+-----------+
3 rows in set (0.00 sec)
You can also put the products in the right hand side of the left join, depending on what you expected when there is product without images available. The query above will display the view as above, with the file_name field as "null".
On the other hand, it will not display any if you put products on the right hand side of hte left join.
Building on sarwar026's answer...
SELECT p.id, name, priority, price, file_name
FROM Products p, Images i
WHERE p.id = i.p_id
AND i.priority = (SELECT MIN(priority) FROM Images ii WHERE ii.p_id = p.id)
AND p.category IN (1,2,3)
(tested on a mysql database with copies of your tables)
I have 2 tables in my database: item and category.
Items can be active, or inactive, and have a categoryID that relates to the id of a record in the category table.
i want to perform a query to show all the categories, with the total cost of active items for the category
So my goal is to return something looking like this:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 2 | cat 2 | 0 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
My first query:
SELECT a.*,
SUM(b.cost) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
works ok, but it returns NULL items instead of 0, and uses all items regardless of active/inactive:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
my second query adresses the NULL values:
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
and turns out like so:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
So in my tiny useless brain i try the following query, adding a WHERE clause on table b where active has to = 1 (true)
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
WHERE b.active = 1
GROUP BY a.category_name
and i get the following:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
so as you can se, i would like to return the entire range of categories, even when the right table returns no matching results... Any takes for a million imaginary cool points?
Use:
SELECT c.id,
c.cat_name,
COALESCE(SUM(i.cost), 0) AS total_cost
FROM CATEGORIES c
LEFT JOIN ITEMS i ON i.category_id = c.category_id
AND i.active = 1
GROUP BY c.id, c.cat_name
Try this:
SELECT a.*,
SUM(Case B.Active When 1 Then b.cost else 0 End) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
GROUP BY a.category_name
or this:
SELECT a.*, SUM(b.cost) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
And B.Active = 1
GROUP BY a.category_name