Top 1 not returning results when joined - sql-server

I have the below SQL query that is attempting to return the Last Transaction date for a specific part. The subquery that I'm left joining runs fine when I run it by itself (with the part specific criteria)
SELECT TOP 1 S1.*
FROM PartTran S1
WHERE S1.TranDate > '10/10/2016' AND S1.TranType <> 'ADJ-CST' AND S1.PartNum = '0000AAAO' ORDER BY S1.TranDate DESC
However when I join this into my main query, its returning null.
SELECT T1.PartNum, T2.TranDate, T2.TranType
FROM dbo.Part T1
LEFT JOIN (SELECT TOP 1 S1.* FROM PartTran S1 WHERE S1.TranDate > '10/10/2016' AND S1.TranType <> 'ADJ-CST' ORDER BY S1.TranDate DESC) T2 ON T1.Company = T2.Company AND T1.PartNum = T2.PartNum
WHERE T1.PartNum = '0000AAAO'
Am I missing something here?

Can you please check this following query-
SELECT
T1.PartNum,
T2.TranDate,
T2.TranType
FROM dbo.Part T1
LEFT JOIN
(
SELECT TOP 1 S1.*
FROM PartTran S1
WHERE S1.TranDate > '10/10/2016'
AND S1.TranType <> 'ADJ-CST'
AND S1.PartNum = '0000AAAO'
-- I think this above filter (AND S1.PartNum = '0000AAAO') is required
-- other wise top 1 can select records belongs to other PartNum and
-- your left join will return NULL logically
ORDER BY S1.TranDate DESC
) T2 ON T1.Company = T2.Company
AND T1.PartNum = T2.PartNum
WHERE T1.PartNum = '0000AAAO';

The reason why your original query doesn't work has to do with order of operations.
The derived table T2 resulted in 1 and only 1 record; not 1 record per PART number. This has to do with the derived table obtaining it's results BEFORE it can be joined to T1. Since the part numbers didn't match unless you got lucky on the part and day and company... you would get no data. A cross/outer apply allows you to get the TOP record per Join Criteria. and thus will return multiple records; 1 for each part and company; instead of just 1.
I think you're after a cross or outer apply and you can avoid the 2nd filter in the derived table (T2) If you want parts w/o any transactions kept then use the outer apply, if you only want those with part transactions use cross apply.
SELECT T1.PartNum, T2.TranDate, T2.TranType
FROM dbo.Part T1
CROSS APPLY (SELECT TOP 1 S1.*
FROM PartTran S1
WHERE S1.TranDate > '10/10/2016'
AND S1.TranType <> 'ADJ-CST'
ORDER BY S1.TranDate DESC) T2
ON T1.Company = T2.Company
AND T1.PartNum = T2.PartNum
WHERE T1.PartNum = '0000AAAO'
Alternatively you could use a row number instead of top and partition by your company and partNum ordering by transdate and only return row number 1st ordering by your transdate descending.
Here's a MSDN Doc link showing how cross/outer apply
works.

Try removing the 'LEFT' in your join because it is allowing you to select rows in the sub query that don't meet the criteria of your WHERE clause. That seemed to fix the issue in my text environment at least.

I would suggest a simpler query:
SELECT TOP 1 p.PartNum, T.TranDate, T.TranType
FROM dbo.Part p JOIN
PartTran pt
ON pt.Company = p.Company AND
pt.PartNum = t.PartNum AND
pt.TranType <> 'ADJ-CST' AND
pt.TranDate > '2016-10-10'
WHERE p.PartNum = '0000AAAO'
ORDER BY pt.TranDate DESC;

Related

Find most recent date for ID with multiple records in MS Access

I have a field, "ID", and it has repeat values. (In the example; A21, B42, and C14). My two other fields in the table are "Date" and "Measurement". I want to create a query that will call the previous date WITH matching ID and display the results of that previous row. My end goal is to have a field in my query that will find the change between the current measurement for the ID and the measurement from the date prior.
I have attached an image of the table I have and what I want the query to display.
Sadly MS Access does not support lag(). This however can be emulated with a self-join and a not exists condition with a correlated subquery:
select
t.id,
tprev.date as previous_date,
tprev.measureement as previous_measurement
from Table1 as t
left join Table1 as tprev
on (tprev.id = t.id)
and (tprev.dat < t.date)
and (not exists (
select 1
from Table1 as t1
where
t1 = t.id
and t1.dat < t.date
and t1.dat > tprev.date
))
This is how to make the described query function:
SELECT t.NUM, t.ID, tprev.Date_ AS previous_date, tprev.Measurement AS previous_measurement
FROM Table1 AS t LEFT JOIN Table1 AS tprev ON (tprev.Date_ < t.Date_) AND (tprev.id = t.id)
WHERE not exists
(select 1
from Table1 AS t1
where
t1.ID = t.ID
and t1.Date_ < t.Date_
and t1.Date_ > tprev.Date_);

Is null in where statement doesnt returns me rows

I am using a complex query. I need to returns me always a row even it doesnt find anything.
SELECT
a.InventoryItemID,
a.Name,
a.RetailPrice,
b.MainGroupItemCode,
b.MainGroupItemID,
c.VatValue,
a.Code,
a.Weight,
b.MainGroupItemName,
a.RetailPrice2,
a.FreePrice,
case when isnull(e.IsActive,0)=1 and isnull(d.price,0)!=0 then d.Price else RetailPrice End as CustomPrice
from InventoryMaster a
join InventoryMainGroupItems b on a.MainGroupItemID=b.MainGroupItemID
join VatCodes c on b.VatCodeID=c.VatCodeID
join InventoryPrices d on d.InventoryItemID=a.InventoryItemID
join InventoryCatalog e on e.CatalogID=d.CatalogID
where a.InventoryItemID=2 and ISNULL(e.catalogID,1)=3
The problem is in last line ISNULL(e.catalogID,1)=3. In my table it doesn't exist CatalogID with number 3.
So it doesnt returns me anything, but there is CatalogID with number 1. I have set that if is null to return me 1, Unfortunately i dont get any row back from my query. How can i fix this ?
My question has been solved i just want to add one more join table with one wheere condition isnide
SELECT *
from
(
SELECT t1.ID,
t1.Name,
COALESCE(t2.price,t1.Price) AS price ,
Row_number() OVER(partition BY t1.ID ORDER BY t1.ID) rn
FROM InventoryMaster t1
LEFT JOIN inventoryprices t2
ON t1.ID=t2.ID
LEFT join InventoryCatalog t3
ON t3.ID=t2.ID and t3.ID=2
where t1.ID=2
) t
WHERE t.rn=1
it returns me always the retailprice from First Table Inventory
Add 3 cols near beginning of the select
d.InventoryItemID,
d.CatalogID,
e.CatalogID
Then remove And ISNULL from the Where, and run to see what you get.
It may be that
join InventoryCatalog e
needs
ON d.CatalogID=ISNULL(e.catalogID,1)

Using sub-queries and filter in WHERE clause while joining tables

SELECT DISTINCT(t1.Ticker),t2.SecurityID,t2.ClosePrice,t2.QuoteDateTime FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2
ON t2.SecurityID =t1.SecurityID
WHERE t2.QuoteDateTime IN (SELECT max(QuoteDateTime) FROM [Hub].[SecurityMaster].[SecurityPrices]) AND t1.SecurityTypeName = 'REIT'
I get an output with no data. The subquery doesn't run along with the other filter in the WHERE clause. I am not sure what I am doing wrong. Can somebody please help!
If you are trying to get the lastest row from SecurityPrices for each Ticker, one option is to use cross apply():
select --distinct /* distinct not needed if `Ticker` is unique on `smd`
smd.Ticker
, sp.SecurityID
, sp.ClosePrice
, sp.QuoteDateTime
from [Hub].[SecurityMaster].[SecurityMasterDetails] as smd
cross apply (
select top 1
i.SecurityID
, i.ClosePrice
, i.QuoteDateTime
from [Hub].[SecurityMaster].[SecurityPrices] i
where i.SecurityID = smd.SecurityID
order by i.QuoteDateTime desc
) as sp
where SecurityTypeName = 'REIT' /* which table does this column belong to? */
I think your query would be
SELECT DISTINCT TOP 1 WITH TIES
t1.Ticker,
t2.SecurityID,
t2.ClosePrice,
t2.QuoteDateTime
FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2 ON t2.SecurityID =t1.SecurityID
WHERE SecurityTypeName = 'REIT'
ORDER BY t2.QuoteDateTime DESC
You aren't getting results because the max(QuoteDateTime) record doesn't have SecurityTypeName = 'REIT'. I think you want the max(QuoteDateTime) for this SecurityTypeName, so this can be done with an INNER JOIN.
SELECT DISTINCT
(t1.Ticker),
t2.SecurityID,
t2.ClosePrice,
t2.QuoteDateTime
FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2
ON t2.SecurityID =t1.SecurityID
INNER JOIN
(SELECT max(QuoteDateTime) DT FROM [Hub].[SecurityMaster].[SecurityPrices]) P on P.DT = t2.QuoteDateTime
WHERE SecurityTypeName = 'REIT'
EDIT
Your data doesn't have what you think it does, I suspect. Here is how you can check...
--Find the SecurityID that matches the max date
SELECT
SecurityID ,
max(QuoteDateTime) DT
FROM [Hub].[SecurityMaster].[SecurityPrices]
GROUP BY SecurityID
--I'm betting this ID isn't in your SecurityMasterDetails where the Type is REIT
SELECT DISTINCT
SecurityID
FROM SecurityMasterDetails
WHERE SecurityTypeName = 'REIT'
Since the SecurityID returned in the first query isn't in the second query result set, you are going to get NULL results.

SQL - Apply only one set of joins at a time

I have three joins in my query. My requirement is to select records if either first two joins get satisfied or the third join gets satisfied
select * from security.requestview r
-- either below two joins satisfy
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
-- or this join
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
But ofcourse it will try to satisfy all the joins together.
Is there any way I can put some condition where it will select record if either first two joins satisfy or the last join alone satisfies?
Update
I tried putting them as where conditions but then the query is running forever
To answer this question:
Is there any way I can put some condition where it will select record
if either first two joins satisfy or the last join alone satisfies?
No, I know of no way to do this in a single query.
One way you can write this so that the third join only gets executed if the first one doesn't return records is in a multi-query series that populates a temp table or table variable:
INSERT INTO #tmp
SELECT (the first join);
IF (SELECT COUNT(*) FROM #tmp) = 0
INSERT INTO #tmp
SELECT (the second join);
SELECT * FROM #tmp;
WITH FirstJoin AS (
select * from security.requestview r
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
), SecondJoin AS (
select * from security.requestview r
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
), BothJoins AS (
SELECT * FROM FirstJoin
UNION ALL
SELECT * FROM SecondJoin
)
SELECT DISTINCT * FROM BothJoins
To get this to work you need to Change the "Select *" inside the CTE's so that every returned column is named. (Since I don't know whats in your tables I couldn't do it).
Please note that FirstJoin and SecondJoin needs to return the same columns.
select *
from table1
left join table2
on table1.id = table2.t1
left join table3
on table1.id = table3.t1
left join table4
on table1.id = table4.t1
where table2.t1 is not null
or table3.t1 is not null
or table4.t1 is not null

Top 1 for each joined record

In this SQL Server query, I want to return at most 1 lostReason for each booking. However, the sub-query seems to be returning the first record from the lostBusiness table for every booking. Let me know if I need to clarify.
SELECT
bookings.bookingNumber, lost.lostReason
FROM
bookings
LEFT OUTER JOIN(SELECT TOP (1)
bookingNumber,
lostReason
FROM
lostBusiness) AS lost ON bookings.bookingNumber = lost.bookingNumber
if you need more than one column
select
bookings.bookingNumber, lost.*
from bookings
outer apply
(
select top 1
lost.bookingNumber,
lost.lostReason,
--other columns
from lostBusiness as lost
where bookings.bookingNumber = lost.bookingNumber
order by -- put you order by here
) as lost
or
;with cte as (
select
*,
row_number() over (partition by bookings.bookingNumber order by /* ??? */) as row_num
from bookings
left outer join lostBusiness as lost on bookings.bookingNumber = lost.bookingNumber
)
select * from cte where row_num = 1
if you need more than one column
select
bookings.bookingNumber, max(lost.lostReason) as lostReason
from bookings
left outer join lostBusiness as lost on bookings.bookingNumber = lost.bookingNumber
group by bookings.bookingNumber
If you just want any lost reason, MAX or MIN would do:
SELECT
Bookings.BookingNumber,
MAX(LostBusiness.LostReason) as SomeLostReason
FROM
Bookings
LEFT JOIN LostBusiness ON bookings.BookingNumber= lostBusiness.BookingNumber
GROUP BY
Bookings.BookingNumber
Your query fails because you are joining to a single record, not to a record for each booking.
Try this
select *,
(
select top 1
lostreason
from lostbusiness
where lostbusiness.bookingnumber = bookings.bookingnumber
-- order by goes here.
)
from bookings
You should understand that data doesn't have any inherent order, so you should define what you mean by the "first" reason, by means of an order by clause in the subquery.

Resources