Display Value from Sub Query in SELECT - sql

I need to display the value MIN(pa.DataAdjudicacao) in the SELECT.
And how can I replace this Subquery for a Join?
How can I do it?
As far as I have searched I haven't found any way to do it.
Please help.
SELECT p.id, p.referencia
FROM PCTSproposta p
WHERE p.ID in (SELECT pa.Proposta
FROM PropostaAdjudicada pa
WHERE pa.Proposta = p.ID
GROUP BY pa.Proposta
HAVING MIN(pa.DataAdjudicacao) >= '2020-10-01' And MIN(pa.DataAdjudicacao) <= '2020-10-31')

Your query is overcomplicated.
You use subquery to filter the data by IN predicate, but select the same ID that you've passed into filter of subquery. So your IN looks like EXISTS.
But if you need that min, you should use JOIN:
SELECT p.id,
p.referencia,
pa2.DataAdjudicacao
FROM PCTSproposta p
JOIN (
SELECT pa.Proposta,
MIN(pa.DataAdjudicacao) as DataAdjudicacao
FROM PropostaAdjudicada pa
GROUP BY pa.Proposta
HAVING MIN(pa.DataAdjudicacao) BETWEEN date '2020-10-01'AND date '2020-10-31'
) pa2
on pa2.Proposta = p.ID
Depending on roles of the Id column (if it is primary key of PCTSproposta), data integrity and cardinality the subquery can be merged into main query:
SELECT p.id,
p.referencia,
MIN(pa.DataAdjudicacao) as DataAdjudicacao
FROM PCTSproposta p
JOIN PropostaAdjudicada pa
on pa2.Proposta = p.ID
GROUP BY p.id,
p.referencia
HAVING MIN(pa.DataAdjudicacao) BETWEEN date '2020-10-01'AND date '2020-10-31'

A lateral join comes to mind:
SELECT p.id, p.referencia, pa.*
FROM PCTSproposta p
CROSS APPLY (
SELECT MIN(pa.DataAdjudicacao) minDataAdjudicacao
FROM PropostaAdjudicada pa
WHERE pa.Proposta = p.ID
) pa
WHERE pa.minDataAdjudicacao >= '20201001' AND pa.minDataAdjudicacao < '20201101'

Related

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

Select where MAX(DATE) between X and X

I'm trying to select only patients, who had their LAST appointment in the year 2015. The ideal thing would look like this:
Select person.name, person.surname
inner join patient on patient.pat_id=person.per_id
inner join appointment on appointment.pat_id=patient.pat_id
where MAX(appointment.date) between '31.12.2014'and '01.01.2016'
But of MAX isn't allowed in WHERE clause. What's the best workaround?
You are close:
Select p.name, p.surname
from person p inner join
patient pa
on pa.pat_id = p.per_id join
appointment a
on a.pat_id = pa.pat_id
group by p.per_id, p.name, p.surname
having max(a.date) >= date '2015-01-01' and
max(a.date) < date '2016-01-01';
Notes:
You need group by.
Note that I'm including per_id in the group by, because different people can have the same name.
Date constants can be introduced with the date keyword.
Do not use between with dates; this is particularly true in Oracle where a date column can have a time component.

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
;

SQL query how do i group by name in a table

SELECT R.CALL,
P.NAME,
R.PID,
TIMESTAMPDIFF( 4, CHAR(TIMESTAMP( M.ENDTIME ) - TIMESTAMP( M.START ))) AS MINUTES
FROM ROSTER R, PLAYER_ON P, MATCH M
WHERE R.CALL = P.CALL
AND R.PID = P.PID
AND M.START = R.START
ORDER BY R.CALL ASC, P.NAME ASC
Above is my query, I successfully get the desired columns from the database.
However I still have one step to go.
The sample output for the query is like:
Output
My question is: how do I sum up the total minutes group by names, for example, Edward Ellis will appear in the output only once with total minutes 573+4933.
Here is my attempt:
SELECT CALL,
NAME,
PID,
SUM(MINUTES)
FROM (
SELECT R.CALL,
P.NAME,
R.PID,
TIMESTAMPDIFF( 4, CHAR(TIMESTAMP( M.ENDTIME ) - TIMESTAMP( M.START ))) AS MINUTES
FROM ROSTER R, PLAYER_ON P, MATCH M
WHERE R.CALL = P.CALL
AND R.PID = P.PID
AND M.START = R.START )
GROUP BY NAME
But there is an error says:
SQL0119N An expression starting with "PID" specified in a SELECT clause,
HAVING clause, or ORDER BY clause is not specified in the GROUP BY clause or
it is in a SELECT clause, HAVING clause, or ORDER BY clause with a column
function and no GROUP BY clause is specified. SQLSTATE=42803
Can someone help with that?
You should learn to use proper join syntax (although that has nothing to do with your error). Simple rule: Never use commas in the from clause. Always use explicit join syntax.
Your problem is due to extraneous columns in the select that are not in the group by. In addition, you don't need the subquery:
SELECT P.NAME, SUM(TIMESTAMPDIFF(4,CHAR(TIMESTAMP(M.ENDTIME)-TIMESTAMP(M.START)))) AS MINUTES
FROM ROSTER R JOIN
PLAYER_ON P
ON R.CALL = P.CALL AND R.PID = P.PID JOIN
MATCH M
ON M.START = R.START
GROUP BY NAME
You have some errors in your query. First, try to use JOIN instead of FROM and WHERE to connect tables. Second, you need to use Having clause to sum up the total minutes.
SELECT r.CALL, p.NAME, r.PID, SUM (TIMESTAMPDIFF(4,CHAR(TIMESTAMP(m.ENDTIME)- TIMESTAMP(m.START)))) AS MINUTES
FROM ROSTER r INNER JOIN PLAYER_ON p ON r.call = p.call AND r.pid=p.pid INNER JOIN INNER JOIN MATCH m ON m.START = r.START
GROUP BY p.NAME
HAVING TIMESTAMPDIFF(4,CHAR(TIMESTAMP(m.ENDTIME)- TIMESTAMP(m.START)))
ORDER BY r.CALL ASC, p.NAME ASC;

Compare subselect value with value in master select

In MS Access, I have a query where I want to use a column in the outer query as a condition in the inner query:
SELECT P.FirstName, P.LastName, Count(A.attendance_date) AS CountOfattendance_date,
First(A.attendance_date) AS FirstOfattendance_date,
(SELECT COUNT (*)
FROM(SELECT DISTINCT attendance_date
FROM tblEventAttendance AS B
WHERE B.event_id=8
AND B.attendance_date >= FirstOfattendance_date)
) AS total
FROM tblPeople AS P INNER JOIN tblEventAttendance AS A ON P.ID = A.people_id
WHERE A.event_id=8
GROUP BY P.FirstName, P.LastName
;
The key point is FirstOfattendance_date - I want the comparison deep in the subselect to use the value in each iteration of the master select. Obviously this doesn't work, it asks me for the value of FirstOfattendance_date when I try to run it.
I'd like to do this without resorting to VB code... any ideas?
How about:
SELECT
p.FirstName,
p.LastName,
Count(a.attendance_date) AS CountOfattendance_date,
First(a.attendance_date) AS FirstOfattendance_date,
c.total
FROM (
tblPeople AS p
INNER JOIN tblEventAttendance AS a ON
a.people_id = p.ID)
INNER JOIN (SELECT people_id, Count (attendance_date) As total
FROM (
SELECT DISTINCT people_id,attendance_date
FROM tblEventAttendance)
Group By people_id) AS c ON
p.ID = c.people_id
GROUP BY
p.ID, c.total;
Can you change
B.attendance_date >= FirstOfattendance_date
to
B.attendance_date >= First(A.attendance_date)