T-SQL get max value - sql

I have the following query
SELECT P.ID, MAX(ENTERDATE) as ENTERDATE, MAX(B.CostID) as CostID
FROM Protocol P JOIN BANK B ON P.ID= B.ID
group by P.ID
I need to find out what the Maximum enterdate was along with the corresponding CostID but I do not want to use MAX(B.CostID) but I am forced
to do so as I have a group by P.ID and all other fields need to have some kind of aggregate. How do I say group by P.ID and show me the max(enterdate) but give me the corresponding CostID for that Enterdate?

SELECT P.ID,
ENTERDATE = MAX(p.ENTERDATE),
CostID = (SELECT CostID FROM Bank WHERE ID = p.ID)
FROM Protocol P
group by P.ID
OR something like
SELECT t.*,
CostID
FROM Bank b
JOIN (
SELECT P.ID,
ENTERDATE = MAX(p.ENTERDATE)
FROM Protocol P
group by P.ID
) t ON t.ID = b.ID

Why not subquery it:
SELECT MaxTable.*, BANK.CostID AS CostID
FROM
(
SELECT P.ID, MAX(ENTERDATE) AS ENTERDATE
FROM Protocol P
GROUP BY P.ID
) AS MaxTable
JOIN BANK
ON MaxTable.ID = BANK.ID

You can try with the analytical function ROW_NUMBER:
SELECT *
FROM
(SELECT p.id, p.enterdate, b.costid,
ROW_NUMBER() OVER(PARTITION BY p.id ORDER BY p.enterdate DESC) AS rownum
FROM Protocol P JOIN BANK B ON P.ID= B.ID)
WHERE rownum = 1

Related

Get max/min value of the column independet from where clause

I am having the following query and running it on postgress
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value
p.creation_date,
cp.id as c_part_id,
fr.distance
count(*) OVER() AS total_item
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only
What is missing here is that I also need to get max(p.value) and min(p.value) not affected by the "where" clause - so calculated from total (all) values.
I am dreaming that I can do it within one query and reduce the number of transactions.
Honestly not sure if it is possible!
What I tried is something like this ->
SELECT
(SELECT COUNT(*) from t_p) as count,
(SELECT json_agg(t.*) FROM (
SELECT * FROM t_p
where ***
) AS t) AS rows
But this one did not look really nice as it require additional JSON manipulation at the backend.
I discovered that I might try to use the "with" statement to create a temporary view so the where condition is only evaluated once, but did not succeed to make it works...
You can add the extra columns as scalar subqueries in the form (select min(value) from t_p). Their values are not related to the main query so they should be totally independent.
Your original query has some minor syntax issues (missing commas). I fixed those and the result is:
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value,
p.creation_date,
cp.p_id as c_part_id,
fr.distance,
count(*) OVER() AS total_item,
(select min(value) from t_p) as min_value,
(select max(value) from t_p) as max_value
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only
See running query (without any data) at DB Fiddle.
You can join to a sub-query that calculates both MIN & MAX.
...
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
cross join (
select
min(value) as min_value
, max(value) as max_value
, avg(value) as avg_value
from t_p
) as v
...
Then use v.min_value and v.max_value in the select.
Doesn't even have to be a LATERAL.
You could get the minimum and maximum "on the side" like this:
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value
p.creation_date,
cp.id as c_part_id,
fr.distance,
count(*) OVER() AS total_item,
p.min_value,
p.max_value
from (SELECT id,
name,
tags,
creator,
value,
creation_date,
min(value) OVER () AS min_value,
max(value) OVER () AS max_value,
FROM t_p) AS p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only;

Is there a way to distinct multiple columns in sql?

Is there a way to distinct multiple columns? When I tried to do it with p.name it says that there is an error that occurred.
SELECT DISTINCT( V.NAME ),
POH.status,
poh.shipdate,
pod.orderqty,
POD.receivedqty,
POD.rejectedqty,
p.NAME
FROM purchasing.vendor v
INNER JOIN purchasing.productvendor pv
ON v.businessentityid = pv.businessentityid
INNER JOIN production.product p
ON pv.productid = P.productid
INNER JOIN purchasing.purchaseorderdetail POD
ON P.productid = POD.productid
INNER JOIN purchasing.purchaseorderheader POH
ON POD.purchaseorderid = POH.purchaseorderid
ORDER BY v.NAME,
p.NAME;
If you want one row per NAME, then you can use ROW_NUMBER():
with q as (
<your query here with columns renamed so there are no duplicates>
)
select q.*
from (select q.*,
row_number() over (partition by v_name order by v_name) as seqnum
from q
) q
where seqnum = 1;
DISTINCT is not a function, it is an operator and its scope is the entire SELECT clause
(The query formatting is just for emphasizing the point)
SELECT DISTINCT
V.NAME,
POH.status,
poh.shipdate,
pod.orderqty,
POD.receivedqty,
POD.rejectedqty,
p.NAME
FROM purchasing.vendor v
...
That answers the error you get, however, I doubt if this will give you the results you are looking for

How to make LEFT JOIN with row having max date?

I have two tables in Oracle DB
Person (
id
)
Bill (
id,
date,
amount,
person_id
)
I need to get person and amount from last bill if exist.
I trying to do it this way
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN Bill b
ON b.person_id = p.id AND b.date = (SELECT MAX(date) FROM Bill WHERE person_id = 1)
WHERE p.id = 1;
But this query works only with INNER JOIN. In case of LEFT JOIN it throws ORA-01799 a column may not be outer-joined to a subquery
How can I get amoun from the last bill using left join?
Please try the below avoiding sub query to be outer joined
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN(select * from Bill where date =
(SELECT MAX(date) FROM Bill b1 WHERE person_id = 1)) b ON b.person_id = p.id
WHERE p.id = 1;
What you are looking for is a way to tell in bills, for each person, what is the latest record, and that one is the one to join with. One way is to use row_number:
select * from person p
left join (select b.*,
row_number() over (partition by person_id order by date desc) as seq_num
from bills b) b
on p.id = b.person_id
and seq_num = 1
You cannot have a subquery inside an ON statement.
Instead you need to convert your LEFT JOIN statement into a whole subquery.
Not tested but this should work.
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN (
SELECT id FROM Bill
WHERE person_id = p.id
AND date = (SELECT date FROM Bill WHERE person_id = 1)) b
WHERE p.id = 1;
I'm not quite sure why you would want to filter for the date though.
Simply filtering for the person_id should do the trick
you should join Person and Bill to the result for max date in bill related to person_id
select Person.id, bill.amount
from Person
left join bill on bill.person_id = person.id
left join (
select person_id, max(date) as max_date
from bill
group by person_id ) t on t.person_id = Person.id and b.date = t.max_date
Hey you can do like this
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN Bill b
ON b.person_id = p.id AND b.date = (SELECT max(date) FROM Bill WHERE person_id = 1)
WHERE p.id = 1
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN Bill b
ON b.person_id = p.id
WHERE (SELECT max(date) FROM bill AS sb WHERE sb.person_id=p.id LIMIT 1)=b.date;
SELECT
p.id,
c.amount
FROM Person p
LEFT JOIN (select b.person_id as personid,b.amount as amount from Bill b where b.date1= (select max(date1) from Bill where person_id=1)) c
ON c.personid = p.id
WHERE p.id = 1;
try this
select * from person p
left join (select MAX(id) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
from bills b) b
on p.id = b.person_id
I use GREATEST() function in join condition:
SELECT
p.id,
b.amount
FROM Person p
LEFT JOIN Bill b
ON b.person_id = p.id
AND b.date = GREATEST(b.date)
WHERE p.id = 1
This allows you to grab the whole row if necessary and grab the top x rows
SELECT p.id
,b.amount
FROM person p
LEFT JOIN
(
SELECT * FROM
(
SELECT date
,ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY date DESC) AS row_num
FROM bill
)
WHERE row_num = 1
) b ON p.id = b.person_id
WHERE p.id = 1
;

Selecting the Id of the item with a MAX value when doing a left join

Each product in the database can have many revisions. Some products might not have any revisions at all.
ProductRevision table has the following fields: Id, Version, SubmitDate
I am trying to figure out how I can select a field called LatestRevisionId based on the MAX SubmitDate and if not revision then the field will be null
SELECT p.Id, p.Name, p.Price
FROM Product p
LEFT OUTER JOIN ProductRevision pr ON p.Id = pr.ProductId
Do I have to do a sub select in my select? I really want to try and use HAVING but can't figure out how to do it with a left join.
I was trying to do the following as a sub select:
(SELECT Id
FROM ProductRevision
WHERE ProductId=p.Id
HAVING SubmitDate=MAX(SubmitDate)
) AS LatestVersionId
Please note that I am using SQL SERVER 2008
Here's one option using row_number:
SELECT *
FROM (
SELECT p.Id, p.Name, p.Price, pr.id as LatestRevisionId,
row_number() over (partition by p.Id order by pr.SubmitDate desc) rn
FROM Product p
LEFT OUTER JOIN ProductRevision pr PN p.Id = pr.ProductId
) t
WHERE rn = 1
This will select a single Product with the latest matching row from the ProductRevision table.
If you just prefer to use max, then you need to join the table back to itself again:
SELECT p.Id, p.Name, p.Price, pr.id as LatestRevisionId
FROM Product p
LEFT OUTER JOIN ProductRevision pr PN p.Id = pr.ProductId
LEFT OUTER JOIN (SELECT ProductId, MAX(SubmitDate) MaxSubmitDate
FROM ProductRevision
GROUP BY ProductId) mpr ON pr.ProductId = mpr.ProductId AND
pr.SubmitDate = mpr.MaxSubmitDate
This could perhaps return duplicates though if multiple revisions share the same date.
if you are using SQL Server 2012 and aboe, below code will give you desired result.
SELECT DISTINCT p.Id, p.Name, p.Price, FIRST_VALUE(pr.ID) OVER (PARTITION BY p.Id ORDER BY pr.SubmitDate DESC) AS LatestVersionId
FROM Product p
LEFT OUTER JOIN ProductRevision pr ON p.Id = pr.ProductId
You can use a LEFT JOIN like this:
SELECT p.Id, p.Name, p.Price, pr.RevisionId as LatestRevisionId
FROM Product p LEFT OUTER JOIN
(SELECT pr.*,
ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY SubmitDate DESC) as seqnum
FROM ProductRevision pr
)
ON p.Id = pr.ProductId AND seqnum = 1;
If you want to aggregation other values, then just do:
SELECT p.Id, p.Name, p.Price,
MAX(CASE WHEN seqnum = 1 THEN pr.RevisionId END) as LatestRevisionId
FROM Product p LEFT OUTER JOIN
(SELECT pr.*,
ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY SubmitDate DESC) as seqnum
FROM ProductRevision pr
)
ON p.Id = pr.ProductId
GROUP BY p.Id, p.Name, p.Price;
This should be the simplest method to accomplish what you want.

Get the biggest price using MAX() function

I am using SQL Oracle, and I want to change my query using MAX function and not ROWNUM.
SELECT *
FROM (SELECT a.name, price.price
FROM price
LEFT JOIN a
ON a.id = price.tk_a
ORDER BY price.price DESC)
WHERE ROWNUM <=1;
Any help or suggestions please?
You can use the MAX aggregate function with KEEP ( DENSE_RANK FIRST ORDER BY ... ) to get the maximum of another column:
SELECT MAX( a.name ) KEEP ( DENSE_RANK FIRST ORDER BY p.price DESC ) AS name,
MAX( p.price ) AS price
FROM a
LEFT OUTER JOIN price p
ON ( a.id = p.tk_a );
Try this:
select a1.name, p1.price
from price p1
JOIN a a1 ON a1.id = p1.tk_a
JOIN (SELECT MAX(p.price)
FROM price p) C on p1.price = c.price
MAX and group by
SELECT a.name, MAX(p.price)
FROM price
LEFT JOIN a ON a.id = p.tk_a
GROUP BY a.name
ORDER BY p.price DESC
If you just want the single MAX price then use this
SELECT MAX(p.price), (SELECT MAX(b.name) FROM a b LEFT JOIN price pp ON b.id = pp.tk_a WHERE a.id = b.id AND p.price = pp.price) AS name
FROM price
LEFT JOIN a ON a.id = p.tk_a
ORDER BY p.price DESC
Use This
SELECT *
FROM (SELECT a.name,MAX( price.price)
FROM price
LEFT JOIN a
ON a.id = price.tk_a
GROUP BY a.name
ORDER BY price.price DESC)