Select the first row of a LEFT JOIN - sql

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

Related

Can we select first row of data from column in sql?

I have a table with multiple data for same ID. I want to get the first row data for the ID.
I have added the below SQL that I have tried.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
LEFT OUTER JOIN "client_details" ON ("client"."id" = "client_details"."client_id")
Since I have multiple address for the same ID, can we get only the first id?
Currently the output I get is 2 rows with different addresses.
You can add to your SQL LIMIT 1 and in case you want to be sure the order you can also add to your SQL ORDER BY...
You can use distinct on:
select distinct on (c.id) c.id, c.company_name, cd.address
from Client c left join
client_details cd
on c.id = cd.client_id
order by c.id, ?;
The ? is for the column that specifies the ordering (the definition of "first"). I am guessing that cd.id is what you want.
Note that this query removes the double quotes and introduces table aliases. This is easier on both the eyes (to read) and the fingers (to type).
use row_number()
select * from
(
SELECT
"client"."id",
"client"."company_name",
"client_details"."address",row_number() over(partition by "client"."id" order by "client_details"."address") as rn
from Client
LEFT OUTER JOIN "client_details" ON "client"."id" = "client_details"."client_id"
)A where rn=1
If there is a field you can order the results by you could use a lateral join e.g.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
left join lateral (
select *
from client_details cd
where cd.client_id = client.id
order by [some_ordering_field]
limit 1
) "client_details" on true

How to handle the both group by particular column name and order by desc particular column name

Actually i want the data order by column name desc but its also use group by another column name ...
example:
SELECT * ,schemes.depart_id,schemes.scheme_id as s_id FROM `message_details`
left join schemes on schemes.scheme_id=message_details.scheme_id
left join department on department.id=schemes.depart_id
WHERE message_details.reciver_id=13 or message_details.sender_id=13
GROUP by message_details.scheme_id
HAVING order by message_details.msg_id desc
but result its not correct first row the not highest msg_id
SELECT * ,schemes.depart_id,schemes.scheme_id as s_id
FROM `message_details`
left join schemes on schemes.scheme_id=message_details.scheme_id
left join department on department.id=schemes.depart_id
WHERE message_details.reciver_id=13 or message_details.sender_id=13
GROUP by message_details.scheme_id
HAVING order by message_details.msg_id desc
i want the group by scheme_id with highest msg_id show in result
You seem to want filtering not aggregation.
Without sample data and desired results, it is hard to tell exactly what logic you want to implement. I suspect it is something along these lines:
select . . . -- list the columns you want here
from message_details md left join
schemes s
on s.scheme_id = md.scheme_id left join
department d
on d.id = s.depart_id
where 13 in (md.receiver_id, md.sender_id) and
md.scheme_id = (select max(md2.scheme_id)
from message_details md2
where 13 in (md2.receiver_id, md2.sender_id)
);

SQL tables joined with max

I'm working with two tables. I have a full list of groups in table A, and a list of each group member that has been reviewed in table B. So table B is a log of all review records for those members for each group.
select a.Group_Name, Max(b.Request_Review_Date)
From GroupTable a
Left Outer Join GroupReviews b ON a.Group_Name = b.Group_Name
Group By a.Group_Name
What I am trying to return is the full list of groups from table A, and find the latest review date from table B for each of those groups.
I have researched and tried all or most of the inner & outer joins, apply methods....but its just not giving me the results. Can anyone point me in the right direction? Or am I having to bring back two result sets and compare in my ASP code-behind?
Try a CTE then join back to it
WITH Recent AS
(
select group_name, max(Request_Review_Date) AS 'MaxReviewDate'
from GroupReviews
group by group_name
)
select a.group_name, MaxReviewDate
from GroupTable a left join Recent
on group_name = a.group_name
if you need the value for max for all the a.group name rows the ypu should join the subquery for max date
select a.Group_Name, t.max_date
left join (
select b.Group_Name, Max(b.Request_Review_Date) max_date
from GroupReviews b
Group By b.Group_Name
) t on t.Group_Name = a.Group_Name

How to list unused items from database

MDW_CUSTOMER_ACCOUNTS has the following fields: ACCOUNT_ID, MEAL_ID.
MDW_MEALS_MENU has the following fields: MEAL_ID, MEAL_NAME.
I am trying to generate a report on the number of times a particular meal has been subscribed to by a customer using the query,
SELECT count(a.account_id), b.meal_id, b.meal_name
FROM mdw_meals_menu b LEFT JOIN mdw_customer_accounts a
on b.meal_id=a.meal_id
WHERE
a.start_date BETWEEN to_date('01-APR-2013','DD-MON-YYYY')
AND to_date('30-JUN-2013','DD-MON-YYYY')
GROUP BY b.meal_id, b.meal_name
ORDER BY count(a.account_id) desc, b.meal_id;
This only lists the MEAL_IDs that has been subscribed to at least once. But it is not displaying the Ids that have not been subscribed to.
How do I get these MEAL_IDs to print with the count being 0?
i have modified the code, but still i get the same result.
Your where clause is effectively turning your outer join back into an inner join - conditions on an outer-joined table should generally be in the join clause, like so:
SELECT count(a.account_id), b.meal_id, b.meal_name
FROM mdw_meals_menu b
LEFT JOIN mdw_customer_accounts a
on b.meal_id=a.meal_id and
a.start_date BETWEEN to_date('01-APR-2013','DD-MON-YYYY')
AND to_date('30-JUN-2013','DD-MON-YYYY')
GROUP BY b.meal_id, b.meal_name
ORDER BY count(a.account_id) desc, b.meal_id;
You should use a left outer join .

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.