SQL query all products including those without descriptions - sql

I have the following query to get a product ID, product name, price, description and ingredients from multiple tables.
SELECT TP.intProductID AS ProductID, TP.strName AS Name, TPP.decPrice AS Price,
TD.strDescription AS Description, GROUP_CONCAT( TRH.strName SEPARATOR ', ' ) AS Ingredients
FROM TProducts TP JOIN
TProductsPrices TPP
on TP.intProductID=TPP.intProductID JOIN
TProductsDescriptions TPD
on TP.intProductID=TPD.intProductID JOIN
TDescriptions TD
on TPD.intDescriptionID=TD.intDescriptionID JOIN
TProductsIngredients TPI
on TPD.intProductID=TPI.intProductID JOIN
TRawHerbs TRH
on TPI.intIngredientID=TRH.intRawHerbID
GROUP BY TPD.intProductID;
The query finds all the product info the way it should, but I want to be able to include products in my results that do not have a description in the description table (and maybe return null or an empty string instead). How can I do such a thing?

Change the applicable inner joins to left outer joins. Something like this:
SELECT TP.intProductID AS ProductID, TP.strName AS Name, TPP.decPrice AS Price,
TD.strDescription AS Description,
GROUP_CONCAT( TRH.strName SEPARATOR ', ' ) AS Ingredients
FROM TProducts TP JOIN
TProductsPrices TPP
on TP.intProductID=TPP.intProductID
left JOIN TProductsDescriptions TPD
on TP.intProductID=TPD.intProductID
left JOIN TDescriptions TD
on TPD.intDescriptionID=TD.intDescriptionID
JOIN TProductsIngredients TPI
on TPD.intProductID=TPI.intProductID
JOIN TRawHerbs TRH
on TPI.intIngredientID=TRH.intRawHerbID
GROUP BY TPD.intProductID;

You have structured your query in a typical way, where you have a product table and then bring things in from it. To keep everything in the product table, all the joins should be left outer joins. Also, the joins on productId should go back to the original table:
SELECT TP.intProductID AS ProductID, TP.strName AS Name, TPP.decPrice AS Price,
TD.strDescription AS Description,
GROUP_CONCAT( TRH.strName SEPARATOR ', ' ) AS Ingredients
FROM TProducts TP left outer JOIN
TProductsPrices TPP
on TP.intProductID=TPP.intProductID left outer JOIN
TProductsDescriptions TPD
on TP.intProductID=TPD.intProductID left outer JOIN
TDescriptions TD
on TPD.intDescriptionID=TD.intDescriptionID left outer JOIN
TProductsIngredients TPI
on TP.intProductID=TPI.intProductID left outer JOIN
TRawHerbs TRH
on TPI.intIngredientID=TRH.intRawHerbID
GROUP BY TPD.intProductID;
This will keep all the products in TProducts, whether or not they have matches in other tables.
In general (and especially if you are learning joins), I would recommend that you stick to one type of join in the from clause. This structure, where everything is a left outer join says "keep everything in the first table".

Related

Left Outer Join Without Duplicate Rows

After a lot of searching I could not solve my problem. I have the following tables:
I want to select all records from my 'product' table. but I have a problem. I got multiple rows from 'product' table when I execute the following query:
SELECT dbo.product.id, dbo.product.name, dbo.product_price.value
dbo.product_barcode.barcode
FROM dbo.product LEFT OUTER JOIN
dbo.product_price ON dbo.product.id = dbo.product_price.product_id LEFT OUTER JOIN
dbo.product_barcode ON dbo.product.id = dbo.product_barcode.product_id
My problem was solved with the following query:
SELECT dbo.product.id, dbo.product.name, dbo.product_price.value
dbo.product_barcode.barcode
FROM dbo.product LEFT OUTER JOIN
dbo.product_price ON dbo.product.id = dbo.product_price.product_id LEFT OUTER JOIN
dbo.product_barcode ON dbo.product.id = dbo.product_barcode.product_id
WHERE (dbo.product_price.id IN
(SELECT MIN(id) AS minPriceID
FROM dbo.product_price AS product_price_1
GROUP BY product_id)) AND (dbo.product_barcode.id IN
(SELECT MIN(id) AS Expr1
FROM dbo.product_barcode AS product_barcode_1
GROUP BY product_id))
Now I have just one problem. if the 'product_price' table or 'product_barcode' table does not have any record, No records will be returned. I mean if no similar record is found in 'product_price' or 'product_barcode' table, we will not have any record. while we should have records from the 'product' table with null columns of other tables.
Please help me Thanks.
I think the problem is that the conditions are in the WHERE clause - try moving the conditions from the WHERE clause and into each join:
SELECT dbo.product.id, dbo.product.name, dbo.product_price.value,
dbo.product_barcode.barcode
FROM dbo.product
LEFT OUTER JOIN dbo.product_price ON dbo.product.id = dbo.product_price.product_id
--moved from WHERE clause
AND dbo.product_price.id IN (SELECT MIN(id) AS minPriceID FROM dbo.product_price AS product_price_1 GROUP BY product_id)
LEFT OUTER JOIN dbo.product_barcode ON dbo.product.id = dbo.product_barcode.product_id
--moved from WHERE clause
AND dbo.product_barcode.id IN (SELECT MIN(id) AS Expr1 FROM dbo.product_barcode AS product_barcode_1 GROUP BY product_id)
SELECT dbo.product.id, dbo.product.name, MIN(dbo.product_price.value),MIN( dbo.product_barcode.barcode)
FROM dbo.product
LEFT OUTER JOIN dbo.product_price ON dbo.product.id = dbo.product_price.product_id
LEFT OUTER JOIN dbo.product_barcode ON dbo.product.id = dbo.product_barcode.product_id
GROUP BY dbo.product.id, dbo.product.name
While the above query should achieve your desired results - one entry for product with the minimun product_price (if one exist) and minimum product_barcode (if one exist).
I only assumed you wanted this based on the query you were writing. You need to spend more time thinking about the question you are trying to answer.
Joining will multiply your results if more than one entry per join key is present in one of the tables.
Just remember this diagram:
When you are calling LEFT JOIN and applying filtering like in the second picture from the top left, you will get only items belonging to the A table.
I want to select all records from my 'product' table. but I have a problem. I got multiple rows from 'product' table when I execute the following query:
Can you try calling DISTINCT after SELECT, like this:
SELECT DISTINCT dbo.product.id, dbo.product.name, dbo.product_price.value
dbo.product_barcode.barcode
...

Is it true that all joins following a left join in a SQL query must also be left joins? Why or why not?

I remember this rule of thumb from back in college that if you put a left join in a SQL query, then all subsequent joins in that query must also be left joins instead of inner joins, or else you'll get unexpected results. But I don't remember what those results are, so I'm wondering if maybe I'm misremembering something. Anyone able to back me up on this or refute it? Thanks! :)
For instance:
select * from customer
left join ledger on customer.id= ledger.customerid
inner join order on ledger.orderid = order.id -- this inner join might be bad mojo
Not that they have to be. They should be (or perhaps a full join at the end). It is a safer way to write queries and express logic.
Your query is:
select *
from customer c left join
ledger l
on c.id = l.customerid inner join
order o
on l.orderid = o.id
The left join says "keep all customers, even if there is no matching record in ledger. The second says, "I have to have a matching ledger record". So, the inner join converts the first to an inner join.
Because you presumably want all customers, regardless of whether there is a match in the other two tables, you would use a left join:
select *
from customer c left join
ledger l
on c.id = l.customerid left join
order o
on l.orderid = o.id
You remember correctly some parts of it!
The thing is, when you chain join tables like this
select * from customer
left join ledger on customer.id= ledger.customerid
inner join order on ledger.orderid = order.id
The JOIN is executed sequentialy, so when customer left join ledger happens, you are making sure all joined keys from customer return (because it's a left join! and you placed customers to the left).
Next,
The results of the former JOIN are joined with order (using inner join), forcing the "the first join keys" to match (1 to 1) with the keys from order so you will end up only with records that were matched in order table as well
Bad mojo? it really depends on what you are trying to accomplish.
If you want to guarantee all records from customers return, you should keep "left joining" to it.
You can, however, make this a little more intuitive to understand (not necessarily a better way of writing SQL!) by writing:
SELECT * FROM
(
(SELECT * from customer) c
LEFT JOIN
(SELECT * from ledger) l
ON
c.id= l.customerid
) c_and_l
INNER JOIN (OR PERHAPS LEFT JOIN)
(SELECT * FROM order) as o
ON c_and_l.orderid (better use c_and_l.id as you want to refer to customerid from customers table) = o.id
So now you understand that c_and_l is created first, and then joined to order (you can imagine it as 2 tables are joining again)

Duplicate results due to multiple left outer joins

My query is as below
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM
The result given by my query has duplicate results.Please do help me eliminate the redundant/repeating outputs.
When I put distinct this is what happens, https://i.stack.imgur.com/I2jnN.jpg,
I should only see 2 outputs with the same "STATUS" i.e.(COMPOSING) or (APPR) since they have different "GLDEBITACCT", other than that, there should be no more duplicates.
This is the picture of my Code and Result Set
i think you should be using inner join because if left join where being used all data from table A will repeatedly shows as the table B has its foreign key, or might sometimes you are lacking of WHERE clauses it depends on your query, it will be more helpful to others if you can paste the whole query and their structures with expected results.
https://www.codeproject.com/kb/database/visual_sql_joins.aspx
Good idea will be to place a simple DISTINCT clause in the query
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM

Issue with IfNull using a join | Big Query

I need some help. I've got three tables that I need information from. The most important parameter is the DealID from my table Flostream.orders. If this is null, I want it replaced with the Mobileheads.survey.sales_rule which is the same format.
I've constructed this:
SELECT
filename,
IFNULL(dealID,mobileheads.surveys.sales_rule) AS DealIDcombo,
COUNT(*) AS Total,
SUM(integer(weight)) AS TotalWeight,
SUM(Productweight)/1000 AS SumWeight,
Currency,
Deliverybasecost,
ROUND(SUM(Deliverybasecost),2) AS TotalDelCost,
Productsku,
Productname,
Dealstartdate
FROM
[flostream.orders]
LEFT OUTER JOIN flostream.briisk
ON dealID = Uniquereference
LEFT OUTER JOIN mobileheads.surveys
ON mobileheads.surveys.order_number = ExternalReference
GROUP BY
filename,
DealIDCombo,
currency,
Deliverybasecost,
Productname,
Productsku,
dealstartdate
ORDER BY
filename,
Total desc;
My issue is with this:
LEFT outer JOIN flostream.briisk ON dealID = Uniquereference
Ideally I would like it to be:
LEFT outer JOIN flostream.briisk ON dealIDCombo = Uniquereference
but unfortunately that doesn't work.
Any ideas on how to tackle this?
This is because the join can't access fields that are computed after the join.
See how Ifnull uses the joined table. You need to nest these tables.
First the join with mobileheads.surveys and then the next join.
SELECT * FROM(
SELECT
filename,
IFNULL(dealID,mobileheads.surveys.sales_rule) AS DealIDcombo,
COUNT(*) AS Total,
SUM(integer(weight)) AS TotalWeight,
SUM(Productweight)/1000 AS SumWeight,
Currency,
Deliverybasecost,
ROUND(SUM(Deliverybasecost),2) AS TotalDelCost,
Productsku,
Productname,
Dealstartdate
FROM
[flostream.orders]
LEFT OUTER JOIN mobileheads.surveys
ON mobileheads.surveys.order_number = ExternalReference) as first
LEFT OUTER JOIN flostream.briisk
ON first.dealIDCombo = Uniquereference
GROUP BY
filename,
DealIDCombo,
currency,
Deliverybasecost,
Productname,
Productsku,
dealstartdate
ORDER BY
filename,
Total desc;
Excuse the mess, i don't know where these field belong to. Hopefully this helps. Ask me if you need more explanation!

How do i apply inner join or left join to my query?

This is my query :
select p.cod_produs, p.title, c.category as cname, p.description, p.short_desc, p.img1, p.price, p.qty, p.isActive, p.disponibilitate
from tblproducts p
inner join tblcategory c on p.cat_id = c.id
I have two tables : tblcategory & tblproducts. In my tblcategory i have an attribute cat_id. There are possibilities when my product does not have any categories assigned, in that case the cat_id's value is 0. My query returns all of the products which have categories. So , I want to create a query which returns me all of the products with categories and without(return 0 ). How can i do that ? thx
Inner Join : The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables. i.e. Only if both tables have the matching column, it is added to the result.
Left Join : The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match. I think this is what you need (from what I interpreted from your question).
Read more on joins here