SQL Join Statement Issue - tsql

I'm tring to grab all fields from the latest Cash record, and then all fields from the related TransactionInfo record. I can't quite get this to work yet:
select t.*, top 1 c.* from Cash c
inner join TransactionInfo t
on c.TransactionID = t.id
order by c.createdOn desc

select top 1 *
from Cash c
inner join TransactionInfo t on c.TransactionID = t.id
order by createdOn desc

What's that top 1 doing there? If you only want one row then the TOP(1) must come first:
SELECT TOP(1) t.*, c.*
FROM Cash c
INNER JOIN TransactionInfo t
ON c.TransactionID = t.id
ORDER BY c.createdOn DESC

select t.,c.
from (Select top 1 * from Cash order by createdOn desc
) c
inner join TransactionInfo t
on c.TransactionID = t.id
order by createdOn desc
DOn;t use select * especially with a join, it wastes server resources.

SELECT c.*, t.* FROM cash c, transactioninfo t
WHERE c.infoid = t.id AND c.createdOn = (SELECT max(createdOn) FROM cash WHERE infoId = t.id) ORDER BY transactiontabledate desc
You need to find the record with the latest date from the cash table for each transactionId and use that also to filter it out in your query.

Related

Correlated subquery in order by clause in DB2

I had a query similar to the following and was wondering that DB2 complained about the correlation use in the ORDER BY clause. It errored with something like
[42703][-206] "A.ID" is not valid in the context where it is used..
SQLCODE=-206, SQLSTATE=42703
I was able to rewrite the query to avoid the correlation usage but I couldn't find a reference in the documenation about this. Is this a bug or am I just not able to find details on the expected behavior?
SELECT a.id
FROM A a
ORDER BY (
SELECT COUNT(*)
FROM B b
WHERE b.id = a.id
)
You can't use correlated query in order by clause. However there is many ways to get same result, for example
select count(*) as count_num ,a.ID
from
a join b on a.ID=b.ID
GROUP BY a.ID
order by 1 DESC
solution 1:
SELECT a.id, (select count(*) from B where B.id=a.id) nbOFB
FROM A
order by 2
solution 2:
select * from (
SELECT a.id, (select count(*) from B where B.id=a.id) nbOFB
FROM A
) tmp
order by nbOFB
Solution 3:
SELECT a.id, c.nb
FROM A
inner join lateral
(
select count(*) nb from B where B.id=a.id
) c on 1=1
order by c.nb
Solution 4 :
SELECT a.id, ifnull(c.nb, 0) nb
FROM A
left outer join
(
select b.id, count(*) nb from B group by b.id
) c on a.id=c.id
order by ifnull(c.nb, 0)
Solution 5:
with c as (
select b.id, count(*) nb from B group by b.id
)
SELECT a.id, ifnull(c.nb, 0) nb
FROM A left outer join c on a.id=c.id
order by ifnull(c.nb, 0)

Avoiding Order By in T-SQL

Below sample query is a part of my main query. I found SORT operator in below query is consuming 30% of the cost.
To avoid SORT, there is need of creation of Indexes. Is there any other way to optimize this code.
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA
WHERE ID = r.ID
AND Status = 3
AND TableA_ID >ISNULL((
SELECT TOP 1 TableA_ID
FROM TableA
WHERE ID = r.ID
AND Status <> 3
ORDER BY T_Date DESC
), 0)
ORDER BY T_Date ASC
Looks like you can use not exists rather than the sorts. I think you'll probably get a better performance boost by use a CTE or derived table instead of the a scalar subquery.
select *
from r ... left outer join
(
select ID, min(t_date) as min_date from TableA t1
where status = 3 and not exists (
select 1 from TableA t2
where t2.ID = t1.ID
and t2.status <> 3 and t2.t_date > t1.t_date
)
group by ID
) as md on md.ID = r.ID ...
or
select *
from r ... left outer join
(
select t1.ID, min(t1.t_date) as min_date
from TableA t1 left outer join TableA t2
on t2.ID = t1.ID and t2.status <> 3
where t1.status = 3 and t1.t_date < t2.t_date
group by t1.ID
having count(t2.ID) = 0
) as md on md.ID = r.ID ...
It also appears that you're relying on an identity column but it's not clear what those values mean. I'm basically ignoring it and using the date column instead.
Try this:
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA a1
LEFT JOIN (
SELECT ID, MAX(TableA_ID) AS MaxAID
FROM TableA
WHERE Status <> 3
GROUP BY ID
) a2 ON a2.ID = a1.ID AND a1.TableA_ID > coalesce(a2.MAXAID,0)
WHERE a1.ID = r.ID AND a1.Status = 3
ORDER BY T_Date ASC
The use of TOP 1 in combination with the unexplained r alias concern me. There's almost certainly a MUCH better way to get this data into your results that doesn't involve doing this in a sub query (unless this is for an APPLY operation).

Select the last record for the date and time columns

I need to select the last record in the academic table which has two columns for date and time. When I run the query I get an error. Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
USE PCUnitTest
SELECT C.ACCOUNTNO, C.CONTACT, C.LASTNAME, C.KEY4, A.PEOPLE_ID, A.APP_STATUS, A.APP_DECISION, A.REVISION_DATE, A.REVISION_TIME
FROM ACADEMIC AS A INNER JOIN
GM.dbo.CONTACT1 AS C ON A.PEOPLE_ID = C.KEY4
WHERE A.REVISION_DATE = (SELECT TOP (1) REVISION_DATE, REVISION_TIME, PEOPLE_CODE, PEOPLE_ID, PEOPLE_CODE_ID, ACADEMIC_YEAR, ACADEMIC_TERM, ACADEMIC_SESSION, PROGRAM, DEGREE, CURRICULUM
FROM PCUnitTest.dbo.ACADEMIC
ORDER BY REVISION_DATE DESC, REVISION_TIME DESC)
You can Join the query you are using in the where
USE PowerCampusUnitTest
SELECT C.ACCOUNTNO, C.CONTACT, C.LASTNAME, C.KEY4, A.PEOPLE_ID, A.APP_STATUS, A.APP_DECISION, A.REVISION_DATE, A.REVISION_TIME
FROM ACADEMIC AS A
INNER JOIN GoldMineUnitTest.dbo.CONTACT1 AS C
ON A.PEOPLE_ID = C.KEY4
INNER JOIN (
SELECT TOP 1 A2.REVISION_DATE,A2.REVISION_TIME FROM PowerCampusUnitTest.dbo.ACADEMIC A2
ORDER BY REVISION_DATE DESC, REVISION_TIME DESC
)AS A2
ON A.REVISION_DATE = A2.REVISION_DATE AND A.REVISION_TIME = A2.REVISION_TIME
Use ROW_NUMBER()
USE PCUnitTest
SELECT
R.ACCOUNTNO, R.CONTACT, R.LASTNAME, R.KEY4, R.PEOPLE_ID, R.APP_STATUS, R.APP_DECISION, R.REVISION_DATE, R.REVISION_TIME
FROM
(
SELECT C.ACCOUNTNO, C.CONTACT, C.LASTNAME, C.KEY4, A.PEOPLE_ID, A.APP_STATUS, A.APP_DECISION, A.REVISION_DATE, A.REVISION_TIME
,ROW_NUMBER() OVER (ORDER BY A.REVISION_DATE DESC, A.REVISION_TIME DESC) RN
FROM ACADEMIC AS A INNER JOIN
GMUnitTest.dbo.CONTACT1 AS C ON A.PEOPLE_ID = C.KEY4
) R
WHERE RN=1
If you want to get the latest row for each PEOPLE_ID, then add PARTITION BY
SELECT
R.ACCOUNTNO, R.CONTACT, R.LASTNAME, R.KEY4, R.PEOPLE_ID, R.APP_STATUS, R.APP_DECISION, R.REVISION_DATE, R.REVISION_TIME
FROM
(
SELECT C.ACCOUNTNO, C.CONTACT, C.LASTNAME, C.KEY4, A.PEOPLE_ID, A.APP_STATUS, A.APP_DECISION, A.REVISION_DATE, A.REVISION_TIME
,ROW_NUMBER() OVER (PARTITION BY A.PEOPLE_ID ORDER BY A.REVISION_DATE DESC, A.REVISION_TIME DESC) RN
FROM ACADEMIC AS A INNER JOIN
GMUnitTest.dbo.CONTACT1 AS C ON A.PEOPLE_ID = C.KEY4
) R
WHERE RN=1

T-SQL Subquery on Latest Date with InnerJoin

I want to do a similiar thing like this guy:
T-SQL Subquery Max(Date) and Joins
I have to do this with an n:m relation.
So the layout is:
tbl_Opportunity
tbl_Opportunity_tbl_OpportunityData
tbl_OpportunityData
So as you see there is an intersection table which connects opportunity with opportunitydata.
For every opportunity there are multiple opportunity datas. In my view i only want a list with all opportunites and the data from the latest opportunity datas.
I tried something like this:
SELECT
dbo.tbl_Opportunity.Id, dbo.tbl_Opportunity.Subject,
dbo.tbl_User.UserName AS Responsible, dbo.tbl_Contact.Name AS Customer,
dbo.tbl_Opportunity.CreationDate, dbo.tbl_Opportunity.ActionDate AS [Planned Closure],
dbo.tbl_OpportunityData.Volume,
dbo.tbl_OpportunityData.ChangeDate, dbo.tbl_OpportunityData.Chance
FROM
dbo.tbl_Opportunity
INNER JOIN
dbo.tbl_User ON dbo.tbl_Opportunity.Creator = dbo.tbl_User.Id
INNER JOIN
dbo.tbl_Contact ON dbo.tbl_Opportunity.Customer = dbo.tbl_Contact.Id
INNER JOIN
dbo.tbl_Opprtnty_tbl_OpprtnityData ON dbo.tbl_Opportunity.Id = dbo.tbl_Opprtnty_tbl_OpprtnityData.Id
INNER JOIN
dbo.tbl_OpportunityData ON dbo.tbl_Opprtnty_tbl_OpprtnityData.Id2 = dbo.tbl_OpportunityData.Id
The problem is my view now includes a row for every opportunity data, since I don't know how to filter that I only want the latest data.
Can you help me? is my problem description clear enough?
thank you in advance :-)
best wishes,
laurin
; WITH Base AS (
SELECT dbo.tbl_Opportunity.Id, dbo.tbl_Opportunity.Subject, dbo.tbl_User.UserName AS Responsible, dbo.tbl_Contact.Name AS Customer,
dbo.tbl_Opportunity.CreationDate, dbo.tbl_Opportunity.ActionDate AS [Planned Closure], dbo.tbl_OpportunityData.Volume,
dbo.tbl_OpportunityData.ChangeDate, dbo.tbl_OpportunityData.Chance
FROM dbo.tbl_Opportunity INNER JOIN
dbo.tbl_User ON dbo.tbl_Opportunity.Creator = dbo.tbl_User.Id INNER JOIN
dbo.tbl_Contact ON dbo.tbl_Opportunity.Customer = dbo.tbl_Contact.Id INNER JOIN
dbo.tbl_Opprtnty_tbl_OpprtnityData ON dbo.tbl_Opportunity.Id = dbo.tbl_Opprtnty_tbl_OpprtnityData.Id INNER JOIN
dbo.tbl_OpportunityData ON dbo.tbl_Opprtnty_tbl_OpprtnityData.Id2 = dbo.tbl_OpportunityData.Id
)
, OrderedByDate AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY ChangeDate DESC) RN FROM Base
)
SELECT * FROM OrderedByDate WHERE RN = 1
To make it more readable I'm using CTE (the WITH part). In the end the real "trick" is doing a ROW_NUMBER() partitioning the data by tbl_Opportunity.Id and ordering the partitions by ChangeDate DESC (and I call it RN). Clearly the maximum date in each partition will be RN = 1 and then we filter it by RN.
Without using CTE it will be something like this:
SELECT * FROM (
SELECT dbo.tbl_Opportunity.Id, dbo.tbl_Opportunity.Subject, dbo.tbl_User.UserName AS Responsible, dbo.tbl_Contact.Name AS Customer,
dbo.tbl_Opportunity.CreationDate, dbo.tbl_Opportunity.ActionDate AS [Planned Closure], dbo.tbl_OpportunityData.Volume,
dbo.tbl_OpportunityData.ChangeDate, dbo.tbl_OpportunityData.Chance,
ROW_NUMBER() OVER (PARTITION BY dbo.tbl_Opportunity.Id ORDER BY dbo.tbl_OpportunityData.ChangeDate DESC) RN
FROM dbo.tbl_Opportunity INNER JOIN
dbo.tbl_User ON dbo.tbl_Opportunity.Creator = dbo.tbl_User.Id INNER JOIN
dbo.tbl_Contact ON dbo.tbl_Opportunity.Customer = dbo.tbl_Contact.Id INNER JOIN
dbo.tbl_Opprtnty_tbl_OpprtnityData ON dbo.tbl_Opportunity.Id = dbo.tbl_Opprtnty_tbl_OpprtnityData.Id INNER JOIN
dbo.tbl_OpportunityData ON dbo.tbl_Opprtnty_tbl_OpprtnityData.Id2 = dbo.tbl_OpportunityData.Id
) AS Base WHERE RN = 1
The statement can be simplified for one more step further:
SELECT TOP 1 WITH TIES
dbo.tbl_Opportunity.Id, dbo.tbl_Opportunity.Subject, dbo.tbl_User.UserName AS Responsible,
dbo.tbl_Contact.Name AS Customer, dbo.tbl_Opportunity.CreationDate,
dbo.tbl_Opportunity.ActionDate AS [Planned Closure], dbo.tbl_OpportunityData.Volume,
dbo.tbl_OpportunityData.ChangeDate, dbo.tbl_OpportunityData.Chance
FROM
dbo.tbl_Opportunity INNER JOIN
dbo.tbl_User ON dbo.tbl_Opportunity.Creator = dbo.tbl_User.Id INNER JOIN
dbo.tbl_Contact ON dbo.tbl_Opportunity.Customer = dbo.tbl_Contact.Id INNER JOIN
dbo.tbl_Opprtnty_tbl_OpprtnityData ON dbo.tbl_Opportunity.Id = dbo.tbl_Opprtnty_tbl_OpprtnityData.Id INNER JOIN
dbo.tbl_OpportunityData ON dbo.tbl_Opprtnty_tbl_OpprtnityData.Id2 = dbo.tbl_OpportunityData.Id
ORDER BY
ROW_NUMBER() OVER (PARTITION BY dbo.tbl_Opportunity.Id ORDER BY dbo.tbl_OpportunityData.ChangeDate DESC);

MS Access INNER JOIN most recent entry

I'm having some trouble trying to get Microsoft Access 2007 to accept my SQL query but it keeps throwing syntax errors at me that don't help me correct the problem.
I have two tables, let's call them Customers and Orders for ease.
I need some customer details, but also a few details from the most recent order. I currently have a query like this:
SELECT c.ID, c.Name, c.Address, o.ID, o.Date, o.TotalPrice
FROM Customers c
INNER JOIN Orders o
ON c.ID = o.CustomerID
AND o.ID = (SELECT TOP 1 ID FROM Orders WHERE CustomerID = c.ID ORDER BY Date DESC)
To me, it appears valid, but Access keeps throwing 'syntax error's at me and when I hit OK, it selects a piece of the SQL text that doesn't even relate to it.
If I take the extra SELECT clause out it works but is obviously not what I need.
Any ideas?
You cannot use AND in that way in MS Access, change it to WHERE. In addition, you have two reserved words in your column (field) names - Name, Date. These should be enclosed in square brackets when not prefixed by a table name or alias, or better, renamed.
SELECT c.ID, c.Name, c.Address, o.ID, o.Date, o.TotalPrice
FROM Customers c
INNER JOIN Orders o
ON c.ID = o.CustomerID
WHERE o.ID = (
SELECT TOP 1 ID FROM Orders
WHERE CustomerID = c.ID ORDER BY [Date] DESC)
I worked out how to do it in Microsoft Access. You INNER JOIN on a pre-sorted sub-query. That way you don't have to do multiple ON conditions which aren't supported.
SELECT c.ID, c.Name, c.Address, o.OrderNo, o.OrderDate, o.TotalPrice
FROM Customers c
INNER JOIN (SELECT * FROM Orders ORDER BY OrderDate DESC) o
ON c.ID = o.CustomerID
How efficient this is another story, but it works...