SQL joining tables (a,b) - but only rows from a - sql

I want to join two tables (sales_flat_order & sales_flat_order_address). I want to use the entity_id from one table to match with the parent_id in the other table. Now there is one row for every entity_id but there are two rows for every parent_id. But i just want to have one row after the join.
I am using this Statement but it still returns two rows for every entity_id after the join.
select o.customer_email, a.prefix
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
Does anybody have an idea?

select o.customer_email,
a.prefix
from sales_flat_order o
left JOIN (Select distinct a.prefix, a.parentid
from sales_flat_order_address) a
on o.entity_id = a.parent_id

Just add a distinct?
select DISTINCT o.customer_email, a.prefix
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
Obviously this will only remove multiple rows where o.customer_email, a.prefix are in fact the same, otherwise you would need to find some way to decide which record you actually wanted... eg:
select o.customer_email, MIN(a.prefix)
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
group by o.customer_email

SELECT o.customer_email, oa.prefix
FROM sales_flat_order o
LEFT JOIN
sales_flat_order_address oa
ON oa.entity_id =
(
SELECT entity_id
FROM sales_flat_order_address oai
WHERE oai.parent_id = o.entity_id
ORDER BY
FIND_IN_SET(address_type, 'billing,shipping')
LIMIT 1
)
This way you will be getting the prefix from billing address (if it's set), falling back to shipping address if the billing address is not set.

Related

Select the first row of a LEFT JOIN

I'm trying to do a left join. But I only want the first row of the joined table.
When I do :
SELECT DISTINCT
c.reference
FROM contracts as c
output : 7400 rows
But when I try to do the left join I have a lot of duplicates.
I already tried to only get the first row but it does not work. Here is my code :
SELECT DISTINCT
c.reference,
contract_premiums.start_date
FROM contracts as c
LEFT OUTER JOIN contract_premiums ON contract_premiums.contract_id=(
SELECT contract_id FROM contract_premiums
WHERE contract_premiums.contract_id = c.id
ORDER BY contract_premiums.created_at ASC
LIMIT 1)
output : 11500 rows
Note the database in Postgresql and I'm using this request in klipfolio.
If you just want the latest start_date per reference, you can use aggregation:
select c.reference, max(cp.start_date) max_start_date
from contracts c
left join contracts_premiums cp on cp.contract_id = c.id
group by c.reference
This guarantees that you will only get one row per reference.
If you want more columns from contracts_premiums, or if you want to sort by a column other than start_date (possibly, you want created_at instead), then another option is distinct on:
select distinct on (c.reference) c.reference, cp.start_date, cp.created_at
from contracts c
left join contracts_premiums cp on cp.contract_id = c.cid
order by c.reference, cp.created_at desc

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

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.

First() INNER JOIN

SELECT tblT.Tick, First(tblB.BDF) AS FirstOfBDF
FROM tblT INNER JOIN tblB ON tblT.Tick = tblB.Tick
GROUP BY tblT.Tick;
My Access table (tblB) is sorted by Tick and then by BDF. How come it does not retrieve the correct data?
There is no guarantee that table is sorted unless you explicitly mention Order by.
select top 1 tblT.Tick, First(tblB.BDF) AS FirstOfBDF
from FROM tblT INNER JOIN tblB ON tblT.Tick = tblB.Tick
GROUP BY tblT.Tick
Order by tblt.Tick ;

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.