Issue with IfNull using a join | Big Query - sql

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!

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
...

rewriting LEFT OUTER JOIN as a subquery

Is it possible to rewrite the following left join:
SELECT wanted.id AS wanted_id, offers.id AS offer_id, departure_city,
departure_country, destination_city, destination_country, departure_date_min, departure_date_max, MIN(price) as price
FROM wanted LEFT JOIN (SELECT id, wanted_id, SUM(price) AS price FROM `offers` GROUP BY offer_date) AS offers ON wanted.id = offers.wanted_id
WHERE wanted.id IN (SELECT wanted_id FROM wanted_by_user WHERE user_ID=%d) group by offer_id
as a subquery?
I have tried it like this, and technically, it works fine, BUT: it only works like an inner join would (meaning not showing rows if there's no match in the table 'offers'):
FROM wanted, (SELECT id, SUM(price) AS price FROM `offers` GROUP BY offer_date) AS offers "
."WHERE wanted.id IN(SELECT wanted_id FROM wanted_by_user WHERE user_ID=%s)

sql left outer join with a constraining column

Here is the SQL, 'aal_county_zip' has entry for 2 zipcodes whereas 'us_zip' has 15 zipcodes. The requirement is to get 15 rows with only 2 rows having data from 'aal_county_zip'. It works like a normal join. How can I make change the SQL/Table structure to make this work. I also want to add the condition that is commented below.
SELECT DISTINCT a.zcta5ce10 AS zipcode,
c.report_year,
c.aal
FROM aal_county_zip c
RIGHT OUTER JOIN us_zip a
ON ( c.zip = a.zcta5ce10 )
WHERE Lower(c.name) = Lower('alachua')
--and c.report_year=2009
ORDER BY c.report_year DESC
The WHERE Lower(c.name) = Lower('alachua') in your query turns the outer join into an inner join, since it prevents c.name from being NULL.
Consider using a left join instead, as they're often more natural to write. And in any event, apply that condition to the join clause rather than to the where clause, so as to avoid turning it into an inner join.
Borrowing and amending #dasblinkenlight's query:
SELECT DISTINCT
a.zcta5ce10 AS zipcode
, c.report_year
, c.aal
FROM us_zip a
LEFT OUTER JOIN aal_county_zip c
ON c.zip = a.zcta5ce10
AND c.report_year=2009
AND LOWER(c.name) = LOWER('alachua')
ORDER BY c.report_year DESC
That should fix your "only two rows returned" problem. That said, the query is likely missing some additional criteria (and ordering criteria) on us_zip.
SELECT DISTINCT a.zcta5ce10 AS zipcode,
c.report_year,
c.aal
FROM aal_county_zip c
RIGHT OUTER JOIN us_zip a
ON ( c.zip = a.zcta5ce10 )
WHERE Lower(c.name) = Lower('alachua')
AND COALESCE(c.report_year, 2009)=2009
ORDER BY c.report_year DESC
or
SELECT DISTINCT a.zcta5ce10 AS zipcode,
c.report_year,
c.aal
FROM aal_county_zip c
RIGHT OUTER JOIN us_zip a
ON ( c.zip = a.zcta5ce10 AND c.report_year=2009)
WHERE Lower(c.name) = Lower('alachua')
ORDER BY c.report_year DESC
You're doing a RIGHT OUTER JOIN, so your first table, aal_county_zip, may contain nulls. So either you account for those nulls by using COALESCE, or you make it part of the join condition.

SQL query all products including those without descriptions

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".

i want to modify this SQL statement to return only distinct rows of a column

select
picks.`fbid`,
picks.`time`,
categories.`name` as cname,
options.`name` as oname,
users.`name`
from
picks
left join categories
on (categories.`id` = picks.`cid`)
left join options
on (options.`id` = picks.oid)
left join users
on (users.fbid = picks.`fbid`)
order by
time desc
that query returns a result that like:
my question is.... I would like to modify the query to select only DISTINCT fbid's. (perhaps the first row only sorted by time)
can someone help with this?
select
p2.fbid,
p2.time,
c.`name` as cname,
o.`name` as oname,
u.`name`
from
( select p1.fbid,
min( p1.time ) FirstTimePerID
from picks p1
group by p1.fbid ) as FirstPerID
JOIN Picks p2
on FirstPerID.fbid = p2.fbid
AND FirstPerID.FirstTimePerID = p2.time
LEFT JOIN Categories c
on p2.cid = c.id
LEFT JOIN Options o
on p2.oid = o.id
LEFT JOIN Users u
on p2.fbid = u.fbid
order by
time desc
I don't know why you originally had LEFT JOINs, as it appears that all picks must be associated with a valid category, option and user... I would then remove the left, and change them to INNER joins instead.
The first inner query grabs for each fbid, the FIRST entry time which will result in a single entity for the FBID. From that, it re-joins to the picks table for the same ID and timeslot... then continues for the rest of the category, options, users join criteria of that single entry.
2 options, you could write a group by clause.
Or you could write a nested query joined back to itself to get pertinent info.
Nested aliased table:
SELECT
n.fBids
FROM
MyTable t
INNER JOIN
(SELECT DISTINCT fBids
FROM MyTable) n
ON n.ID = t.ID
Or group by option
SELECT fBId from MyTable
GROUP BY fBID
select picks.`fbid`, picks.`time`, categories.`name` as cname,
options.`name` as oname, users.`name` from picks left join categories
on (categories.`id` = picks.`cid`) left join options on (options.`id` = picks.oid)
left join users on (users.fbid = picks.`fbid`)
order by time desc GROUP BY picks.`fbid`
select
picks.fbid,
MIN(picks.time) as first_time,
MAX(picks.time) as last_time
from
picks
group by
picks.fbid
order by
MIN(picks.time) desc
However, if you want only distinct fbid's you cannot display cname and other columns at the same time.