Here's a fiddle:
http://sqlfiddle.com/#!2/7f479a/2
What I am trying to achieve is the Inventory from this query:
SELECT
di.distributors_inventory_stock,
op.op_products_id,
p.products_mfr_part_number,
op.op_products_name,
p.products_brand,
op.op_products_qty,
SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o
ON op.op_order_id = o.orders_id
JOIN products p
ON p.products_id = op.op_products_id
JOIN distributors_inventory di
ON di.distributors_inventory_product_ID = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
With the TotalSold and product list from this query:
SELECT
op.op_products_id,
p.products_mfr_part_number,
op.op_products_name,
p.products_brand,
SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o ON op.op_order_id = o.orders_id
JOIN products p ON p.products_id = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
Because the distributors data is driving the list of products, that needs to be the first table queried, then left join to the other tables, putting all conditions on the other tables in the join condition:
SELECT
di.distributors_inventory_stock stock,
di.distributors_inventory_product_ID product_id,
p.products_mfr_part_number mfr_part_number,
op.op_products_name order_product_name,
p.products_brand brand,
p.products_supplier_name supplier_name,
SUM(op.op_products_qty) / count(o.orders_id) AS TotalSold
FROM distributors_inventory di
LEFT JOIN orders_products op ON di.distributors_inventory_product_ID = op.op_products_id
LEFT JOIN orders o ON op.op_order_id= o.orders_id
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
LEFT JOIN products p ON p.products_id = di.distributors_inventory_product_ID
WHERE di.distributors_inventory_distributor_ID = '90'
GROUP BY 1, 2, 3, 4, 5, 6
ORDER BY TotalSold DESC
See SQLFiddle
The key points here are:
select first from the table that controls what rows are returned
left join to the sales tables
move the conditions on left joined tables, specifically the date range, from the where clause into the join condition
removed column that was both summed and selected (makes no sense)
corrected group by clause problem (you must list all non-aggregated selected columns for the grouping to work as you expect)
join to product table directly from distributors table so product info is available for products without sales in the period
give columns friendlier names
added supplier product name (because we can)
modified sum() due to account for multiple joined rows from the order table (from comment)
Of all the tips, the third is the most critical. Because left joined rows have nulls in their columns, putting a condition on them in the where clause will result in only rows that actually join being returned - effectively making the joins inner joins. Conditions in the join condition still restrict rows returned, while allowing the join to be an outer join
Your First Query is Good ... just add LEFT OUTER JOIN To distributors_inventory table.. and you should get your expected result set.
Modified Query: (check the comment in below query for modification)
SELECT di.distributors_inventory_stock, op.op_products_id, p.products_mfr_part_number, op.op_products_name, p.products_brand, op.op_products_qty, SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o ON op.op_order_id = o.orders_id
JOIN products p ON p.products_id = op.op_products_id
-- Comment: Modification in below Line, inner join changed to left outer join)
LEFT OUTER JOIN distributors_inventory di ON di.distributors_inventory_product_ID = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
You will get null values under distributors_inventory_stock column if corresponding row is not found in this table.. if you want zero instead of null then change it as below in select clause
Select di.distributors_inventory_stock
To
Select ifnull(di.distributors_inventory_stock,0) AS distributors_inventory_stock
Related
Hello I am trying to add a Translated_Name column from the Product_descriptions table to my current query that is already joining two tables however the translated_name column is a type NVARCHAR2. Should I be using Inner Join for it or am I completely wrong?
select order_mode,customer_id,product_id from ORDERS
inner join ORDER_items on order_items.ORDER_ID=Orders.ORDER_ID
where exists(select customer_id from customers where orders.customer_id=customers.customer_id)
inner join product_descriptions on product_descriptions.translated_name = Orders.Customer_id
The where clause goes after the joins:
select
order_mode,
customer_id,
product_id
from orders o
inner join order_items oi
on oi.order_id = o.order_id
inner join product_descriptions pd
on pd.translated_name = o.customer_id
where exists(
select 1
from customers c
where o.customer_id = c.customer_id
)
Notes:
table aliases make the query easier to read and write
you should qualify the columns the the from clause with the alias of the table they belong to
I am quite suspicious about the join condition on product_descriptions, which involves customer_id; you might need to review that (without knowing your table structures, it is not possible to tell what the correct condition is)
I THINK what is happening with this query is if there are no records in the GenericAttribute table associated with the Product, then that product is not displayed. See line below in WHERE clause: "AND GenericAttribute.KeyGroup = 'Product'"
Is there a way to reword so that that part of the WHERE is ignored if no associated record in the GenericAttribute table?
Also, looking at my ORDER BY clause, will a record from the product table still show up if it has no associated record in the Pvl_AdDates table?
Thanks!
SELECT DISTINCT Product_Category_Mapping.CategoryId, Product.Id, Product.Name, Product.ShortDescription, Pvl_AdDates.Caption, Pvl_AdDates.EventDateTime, convert(varchar(25), Pvl_AdDates.EventDateTime, 120) AS TheDate, Pvl_AdDates.DisplayOrder, Pvl_Urls.URL, [Address].FirstName, [Address].LastName, [Address].Email, [Address].Company, [Address].City, [Address].Address1, [Address].Address2, [Address].ZipPostalCode, [Address].PhoneNumber
FROM [Address]
RIGHT JOIN (GenericAttribute
RIGHT JOIN (Pvl_Urls RIGHT JOIN (Pvl_AdDates
RIGHT JOIN (Product_Category_Mapping
LEFT JOIN Product
ON Product_Category_Mapping.ProductId = Product.Id)
ON Pvl_AdDates.ProductId = Product.Id)
ON Pvl_Urls.ProductId = Product.Id)
ON GenericAttribute.EntityId = Product.Id)
ON Address.Id = convert(int, GenericAttribute.Value)
WHERE
Product_Category_Mapping.CategoryId=12
AND GenericAttribute.KeyGroup = 'Product'
AND Product.Published=1
AND Product.Deleted=0
AND Product.AvailableStartDateTimeUtc <= getdate()
AND Product.AvailableEndDateTimeUtc >= getdate()
ORDER BY
Pvl_AdDates.EventDateTime DESC,
Product.Id,
Pvl_AdDates.DisplayOrder
I strongly encourage you to not mix left join and right join. I have written many SQL queries and cannot think of an occasion when that was necessary.
In fact, just stick to left join.
If you want all products (or at least all products not filtered out by the where clause), then start with the products table and go from there:
FROM Products p LEFT JOIN
Product_Category_Mapping pcm
ON pcm.ProductId = p.Id LEFT JOIN
Pvl_AdDates ad
ON ad.ProductId = p.id LEFT JOIN
Pvl_Urls u
ON u.ProductId = p.id LEFT JOIN
GenericAttribute ga
ON ga.EntityId = p.id LEFT JOIN
Address a
ON a.Id = convert(int, ga.Value)
Note that I added table aliases. These make queries easier to write and to read.
I would add a caution. It looks like you are combining data along different dimensions. You are likely to get a Cartesian product of the dimension attributes for each dimension. Perhaps that is what you want or the WHERE clause takes care of the additional rows.
Yes put constraints (restrictions) on tables on the outer side of outer joins in the on conditions of the outer join, not in the where clause. Conditions in where clauses are not evaluated and applied until after the outer joins are evaluated, so where there is not record in the outer table, the predicate will be false and entire row will be eliminated, undoing the outer-ness. Conditions in the join are evaluated during the join, before the rows from the inner side are added back in, so the result set will still include them.
Second, formatting formatting, formatting! Stick to one direction of join (left is easier) and use Aliases for tables names!
SELECT DISTINCT m.CategoryId, p.Id,
p.Name, p.ShortDescription, d.Caption, d.EventDateTime,
convert(varchar(25), d.EventDateTime, 120) TheDate,
d.DisplayOrder, u.URL, a.FirstName, a.LastName,
a.Email, a.Company, a.City, a.Address1, a.Address2,
a.ZipPostalCode, a.PhoneNumber
FROM Product_Category_Mapping m
left join Product p on p.Id = m.ProductId
and p.Published=1
and p.Deleted=0
and p.AvailableStartDateTimeUtc <= getdate()
and p.AvailableEndDateTimeUtc >= getdate()
left join Pvl_AdDates d ON d.ProductId = p.Id
left join Pvl_Urls u ON u.ProductId = p.Id
left join GenericAttribute g ON g.EntityId = p.Id
and g.KeyGroup = 'Product'
left join [Address] a ON a.Id = convert(int, g.Value)
WHERE m.CategoryId=12
ORDER BY d.EventDateTime DESC, p.Id, d.DisplayOrder
I'm looking to see a breakdown of the total dollar business that each vendor has done (indirectly via the distributor) with each customer, where I'm trying not to use the Inner Join Syntax. I basically don't understand the difference between the two outputs produced by the two queries shown below:
Query1
select customers.cust_id, vendors.vend_id, sum(OrderItems.item_price*OrderItems.quantity) as total_business from
(((Vendors left outer join products
on vendors.vend_id = products.prod_id)
left outer join OrderItems
on products.prod_id = OrderItems.prod_id)
left outer join Orders
on OrderItems.order_num = Orders.order_num)
left outer join Customers
on Orders.cust_id = Customers.cust_id
group by Customers.cust_id, vendors.vend_id
order by total_business
I get the following output:
Query2
select customers.cust_id, Vendors.vend_id, sum(quantity*item_price) as total_business from
(((Vendors left outer join Products
on Products.vend_id = Vendors.vend_id)
left outer join OrderItems --No inner joins allowed
on OrderItems.prod_id = Products.prod_id)
left outer join Orders
on Orders.order_num = OrderItems.order_num)
left outer join Customers
on Customers.cust_id = Orders.cust_id
where Customers.cust_id is not null -- THE ONLY DIFFERENCE BETWEEN QUERY1 AND QUERY2
group by Customers.cust_id, Vendors.vend_id
order by total_business
I don't understand how there are only NULL cust_id's associated with the 1st Output when in the 2nd Output we get some non-NULL cust_ids. Why doesn't the 1st Output include these non-NULL cust_id's
Thank You
Query One is joining Vendors and Products incorrectly:
on vendors.vend_id = products.prod_id -- Vend_ID = Prod_ID
Query Two is joining Vendors and Products correctly:
on Products.vend_id = Vendors.vend_id -- Vend_ID = Vend_ID
Once that is fixed, you'll get the same IDs in both queries. Then I suggest you read Dan's answer to understand why what you were trying to do in eliminating INNER JOIN from the query is cancelled out by adding a WHERE filter to a column from the last table in the chain.
When you left join to a table, then filter on that table in the where clause, the join effectively changes to an inner join. The workaround is to apply the filter as a joining condition.
In your second query, all you have to do is is change the word "where" to "and".
I'm trying to display the most popular product (format_id) in a given month (JAN-14) and group them by a count of each format_id.
Here is my Query :
select PRODUCT, AMOUNT
from (Select Order_108681091.Order_Date, order_line_108681091.Format_id as Product,
COUNT(*) AS AMOUNT FROM order_line_108681091
Inner Join order_108681091
On order_108681091.order_id = order_line_108681091.order_id
Where order_108681091.Order_Date like '%JAN-14%'
group by Format_id
order by AMOUNT desc);
How can i do this ?
You said you have the condition, so let's start with adding it.
There is no need to ORDER BY, so remove it.
Remove Order_date from the subquery SELECT
Use aliases
The subquery itself would be enough.
SELECT l.Format_id as PRODUCT,
COUNT(*) AS AMOUNT
FROM order_line_108681091 l
INNER JOIN order_108681091 o
ON o.order_id = l.order_id
WHERE o.Order_Date LIKE '%JAN-14%'
GROUP BY
l.Format_id;
You have to specify the inner join:
inner join order_108681091 ON order_1078681091.ID = order_line_018681091.ID
or something like that. Also your where clause probably wont work unless you're storing that date as a string and not a datetime datatype.
You don't need the subquery at all. And, I'm very uncomfortable using like on a date directly. Explicitly convert the date to a string:
select ol.Format_id as Product, COUNT(*) AS AMOUNT
from order_line_108681091 ol Inner Join
order_108681091 o
ON o.order_id = ol.order_id
where to_char(o.Order_Date, 'MMM-YYYY') = 'JAN-2014'
group by ol.Format_id
order by count(*) desc;
Actually, if you have in index on OrderDate, you can use the following (to take advantage of the index):
select ol.Format_id as Product, COUNT(*) AS AMOUNT
from order_line_108681091 ol Inner Join
order_108681091 o
ON o.order_id = ol.order_id
where o.Order_Date >= to_date('2014-01-01', 'YYYY-MM-DD') and
o.Order_Date < to_date('2014-02-01', 'YYYY-MM-DD')
group by ol.Format_id
order by count(*) desc;
Moving the function from the column to the constant allows the use of an index on the column.
I'm using SQL Server. This statement lists my products per menu:
SELECT menuname, productname
FROM [web].[dbo].[tblMenus]
FULL OUTER JOIN [web].[dbo].[tblProductsRelMenus]
ON [tblMenus].Id = [tblProductsRelMenus].MenuId
FULL OUTER JOIN [web].[dbo].[tblProducts]
ON [tblProductsRelMenus].ProductId = [tblProducts].ProductId
LEFT JOIN [web].[dbo].[tblOrderDetails]
ON ([tblProducts].Id = [tblOrderDetails].ProductId)
GROUP BY [tblProducts].ProductName
Some products don't have menus and vice versa. I use the following to establish what has been sold of each product.
SELECT [tblProducts].ProductName, SUM([tblOrderDetails].Ammount) as amount
FROM [web].[dbo].[tblProducts]
LEFT JOIN [web].[dbo].[tblOrderDetails]
ON ([tblProducts].ProductId = [tblOrderDetails].ProductId)
GROUP BY [tblProducts].ProductName
What I want to do is complement the top table with an amount column. That is, I want a table with the same number of rows as in the first table above but with an amount value if it exists, otherwise null.
I can't figure out how to do this. Any suggestions?
If I am not missing anything, the second query could be simplified, then incorporated into the first query like this:
SELECT
m.menuname,
p.productname,
t.amount
FROM [web].[dbo].[tblMenus] m
FULL JOIN [web].[dbo].[tblProductsRelMenus] pm ON m.Id = pm.MenuId
FULL JOIN [web].[dbo].[tblProducts] p ON pm.ProductId = p.ProductId
LEFT JOIN (
SELECT ProductId, SUM(Amount) as amount
FROM [web].[dbo].[tblOrderDetails]
GROUP BY ProductId
) t ON p.ProducId = t.ProductId