SQL Subquery to get first record - sql

I need to execute a query something like below.
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
(SELECT * FROM (
SELECT ROUND(LAST_ORDER_AMOUNT,5) FROM ORDERS WHERE ID=A.id AND REQUEST_LEVEL='N' ORDER BY O_DATE DESC)
WHERE ROWNUM =1) AS AMOUNT
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
But this gives me A.ID is invalid error in oracle. I need to get the first record from inner query as it will return multiple records.
Can someone please let me know how can i bind these tables to achieve my goal.
Thank you in advance.

You can rewrite subquery using WITH clause, not exactly sure on syntax but should be something like following.
WITH AmountQuery
AS (
SELECT ID
,ROUND(LAST_ORDER_AMOUNT, 5) AS AmountValue
,ROW_NUMBER() OVER ( ORDER BY O_DATE DESC ) AS RN
FROM ORDERS
WHERE REQUEST_LEVEL = 'N'
)
SELECT TO_CHAR(ROWNUM)
,A.Name
,B.Order
,C.AmountValue
FROM Table1 A
LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN AmountQuery C
ON a.ID = c.ID
AND c.RN = 1
here is SQLFiddle to show how it works.
http://sqlfiddle.com/#!4/696b6/36

Probably, LIMIT will do the job for you selecting just one record from the subquery (It worked for me in MySQL. I do not have Oracle, but I think it may be similar). Try something like this:
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
COALESCE( C.AMOUNT ) as AMOUNT,
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN ( SELECT ROUND(LAST_ORDER_AMOUNT,5) AS AMOUNT FROM ORDERS WHERE REQUEST_LEVEL='N' ORDER BY O_DATE DESC ) C ON C.ID = A.id
group by A.id;

Related

I need the most recent value in a GROUP BY - CASE expression

I'm trying to make a CASE expression in T-SQL that's in a GROUP BY clause, that's basically asking if there's a a.id THEN provide b.name associated with it (where a.id = b.id) .
What I have so far is: (Updated Query)
SELECT b.name, ...
MAX(CASE
WHEN a.id IS NOT NULL
THEN b.name END)
FROM ...
LEFT JOIN table_b AS b
ON b.id = a.id
GROUP BY ...
Because it's T-SQL, the CASE has to be in a aggregate function or GROUP BY clause, which is why I included the MAX. However, without the GROUP BY clause, there would be 4 values. I need the most recent value as defined by a.datetime. How can I put that condition in the CASE statement?
Your last edit changes quite a lot. You should use ROW_NUMBER for this:
WITH CTE AS
(
SELECT b.name, ...
RN = ROW_NUMBER() OVER( PARTITION BY a.id
ORDER BY b.DateColumn DESC)
FROM ...
LEFT JOIN table_b AS b
ON b.id = a.id
)
SELECT *,
CASE
WHEN a.id IS NOT NULL
THEN b.name
END
FROM CTE
WHERE RN = 1;
If I understood you correctly(From now on try including the full query, table data\structure, input and desired output) , you can join them from the beggining and then just pick MAX() :
SELECT ....
MAX(b.name)
FROM YourTable a
LEFT JOIN YourTable b
ON(a.id = b.id)
...
GROUP BY ....
If there's a match, the name will be fetched. Otherwise - it will be NULL .

Written a subquery that can return more than one field without using the Exists

The query below is supposed to pull records for fields with the max date.
I am getting an error
You have written a subquery that can return more than one field without using EXISTS reserved word in the Main query's FROM clause. Revise the SELECT statement of the subquery to request only one column.
Code:
SELECT *
FROM TableName
WHERE (((([Project_Name], [Date])) IN (SELECT Project_Name, MAX(Date)
FROM TableName
GROUP BY Project)));
Your probably thinking of a nested subquery used as a table, like the below:
select a.*, b.1, b.2
from FirstTable A
join (Select Id, firstcolumn as 1, secondcolumn as 2
from SecondTable) B on b.ID = a.ID
Works pretty much like a regular join except you are using a subquery. Hope that helps,
SELECT A.*
FROM TableName A
INNER JOIN (select Project_Name, max(Date) MaxDate
from TableName
group by Project) B
ON A.[Project_Name] = B.[Project_Name]
AND A.[Date] = B.MaxDate
A version using EXISTS() looks like this:
SELECT *
FROM TableName AS A
WHERE EXISTS(
SELECT * FROM (
SELECT B.Project_Name, MAX( B.Date ) AS MaxDate
FROM TableName AS B
GROUP BY B.Project_Name ) AS C
WHERE C.Project_Name = A.Project_Name AND C.MaxDate = A.Date
);
Although I have the feeling this will have poorer performance than a JOIN because the GROUP BY statement might have to be executed for each record and each call to the EXISTS() function...

Exposing more fields on group by sql

I know, in a Group By you can't Select a field that is not in an aggregate function or the GROUP BY clause.
However, There must be a workaround using joins or something else.
I have TWO tables BMP_VISITS_SITES and BMP_VISITS_COMMENTS which are connected by StationID in a one-to-many relationship. One Site can have many comments.
I'm trying to write a query that returns all Sites and the latest (only 1) comment. I have a "working" query but it only returns two columns which are in either an aggregate function or group by.
Here is my "working" query:
select a.StationID,
MAX(b.[dateobserved]) as LastDateObserved,
a.Status
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID;
But how can I access all the columns in both tables?
I've tried inner joins with 1/2 success. When I join my BMP_VISITS_SITES to the above query I get all the fields of the table (t1). Great, but as soon as I try joining on BMP_VISITS_COMMENTS (t3) I get more results than I should.
select t1.*, t2.*
--,t3.*
from BMP_VISITS_SITES t1
inner join (
select a.StationID, MAX(b.[dateobserved]) as LastDateObserved from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID
) t2 on t2.StationID = t1.StationID
--inner join sde.BMP_VISITS_COMMENTS t3 on t3.StationID = t2.StationID;
SELECT a.*, b.* FROM
BMP_VISITS_SITES a
OUTER APPLY
(
SELECT TOP 1 *
FROM BMP_VISITS_COMMENTS b
WHERE b.StationID = a.StationID
ORDER BY LastDateObserved DESC
) b
You can use apply to get the last comment record and return all fields from both sides of the query.
Use row_number()
select *
from
(
select a.StationID,
a.Status,
b.*,
row_number() over (partition by a.stationid, a.status order by b.[dateobserved] desc) as rn
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
) v
where rn = 1

query optimization with condition

I want to optimze the follwing query, to not use subquery to get max value:
select c.ida2a2 from table1 m, table2 c
where c.ida3a5 = m.ida2a2
and (c.createstampa2 < (select max(cc.createstampa2)
from table2 cc where cc.ida3a5 = c.ida3a5));
Any idea? Please let me know if you want to get more info.
This may be a more efficient way to write the query:
select c.ida2a2
from table1 m join
(select c.*, MAX(createstampa2) over (partition by ida3a5) as maxcs
from table2 c
) c
on c.ida3a5 = m.ida2a2
where c.createstampa2 < maxcs
I'm pretty sure Oracle optimizes this correctly (filtering the rows before the join). If you wanted to be clearer:
select c.ida2a2
from table1 m join
(select c.*
from (select c.*, MAX(createstampa2) over (partition by ida3a5) as maxcs
from table2 c
) c
where c.createstamp2 < c.maxcs
) c
on c.ida3a5 = m.ida2a2

SQL: Turn a subquery into a join: How to refer to outside table in nested join where clause?

I am trying to change my sub-query in to a join where it selects only one record in the sub-query. It seems to run the sub-query for each found record, taking over a minute to execute:
select afield1, afield2, (
select top 1 b.field1
from anothertable as b
where b.aForeignKey = a.id
order by field1
) as bfield1
from sometable as a
If I try to only select related records, it doesn't know how to bind a.id in the nested select.
select afield1, afield2, bfield1
from sometable a left join (
select top 1 id, bfield, aForeignKey
from anothertable
where anothertable.aForeignKey = a.id
order by bfield) b on
b.aForeignKey = a.id
-- Results in the multi-part identifier "a.id" could not be bound
If I hard code values in the nested where clause, the select duration drops from 60 seconds to under five. Anyone have any suggestions on how to join the two tables while not processing every record in the inner table?
EDIT:
I ended up adding
left outer join (
select *, row_number() over (partition by / order by) as rank) b on
b.aforeignkey = a.id and b.rank = 1
went from ~50 seconds to 8 for 22M rows.
Try this:
WITH qry AS
(
SELECT afield1,
afield2,
b.field1 AS bfield1,
ROW_NUMBER() OVER(PARTITION BY a.id ORDER BY field1) rn
FROM sometable a LEFT JOIN anothertable b
ON b.aForeignKey = a.id
)
SELECT *
FROM qry
WHERE rn = 1
Try this
select afield1,
afield2,
bfield1
from sometable a
left join
(select top 1 id, bfield, aForeignKey from anothertable where aForeignKey in(a.id) order by bfield) b on b.aForeignKey = a.id