I have 2 tables:
Deparment table (save data of Deparment):
AccDocSales table (save data of Order made by which Department):
Now, I want to select these Departments, which is not showing in AccDocSales table per MONTH (This mean which Department does't have DeptCode in AccDocSales table per MONTH).
Ex (for this case):
I used this query:
SELECT distinct MONTH(DocDate) as THANG, B20Dept.Code, B20Dept.Name
FROM B20Dept, B30AccDocSales S1
WHERE YEAR(S1.DocDate) = 2014 AND B20Dept.Code NOT IN
(
SELECT S2.DeptCode
FROM B30AccDocSales S2, B20Dept
WHERE YEAR(S2.DocDate) = 2014 AND S2.DeptCode = B20Dept.Code
AND MONTH(S1.DocDate) = MONTH(S2.DocDate)
)
ORDER BY MONTH(DocDate)
It working but my teacher said "NOT IN" in this query is NOT acceptable. He asked me find another way to do this without "IN", "NOT IN".
PS: I find one more problem with this query. That is which month have all Department in "DeptCode" and which month have no row, they all do not show up any result when run that query.
Please help.
ALTERNATIVES OF NOT IN-
Example Code –
IF OBJECT_ID('Tempdb..#SampleTable1') IS NOT NULL
DROP TABLE #SampleTable1;
IF OBJECT_ID('Tempdb..#SampleTable2') IS NOT NULL
DROP TABLE #SampleTable2;
SET NOCOUNT ON;
CREATE TABLE #SampleTable1
( NUM INT NOT NULL );
GO
INSERT INTO #SampleTable1 (NUM)
VALUES (1),(2),(3),(4),(5) ;
GO
CREATE TABLE #SampleTable2
( NUM INT NOT NULL );
GO
INSERT INTO #SampleTable2 (NUM)
VALUES (4),(5),(6),(7),(8) ;
GO
SELECT NUM AS [#SampleTable1] FROM #SampleTable1;
GO
SELECT NUM AS [#SampleTable2] FROM #SampleTable2;
GO
1. NOT EXISTS
--METHOD 1 (Using NOT EXISTS)
SELECT S1.NUM AS [NOT EXISTS]
FROM #SampleTable1 S1
WHERE NOT EXISTS (SELECT NUM FROM #SampleTable2 S2 WHERE S2.NUM=S1.NUM);
GO
2. EXCEPT
--METHOD 2 (Using EXCEPT)
SELECT NUM AS [EXCEPT]
FROM #SampleTable1
EXCEPT
SELECT NUM FROM #SampleTable2;
GO
3. ANY
--METHOD 3 (Using = ANY)
SELECT S1.NUM AS [= ANY]
FROM #SampleTable1 S1
WHERE NOT (S1.NUM = ANY
(
SELECT S2.NUM
FROM #SampleTable2 S2
) );
4. OUTER APPLY
--METHOD 4 (Using OUTER APPLY and avoiding JOIN CONDITION)
SELECT S1.NUM AS [OUTER APPLY]
FROM #SampleTable1 S1
OUTER APPLY (
SELECT NUM FROM #SampleTable2 S2 WHERE S2.NUM=S1.NUM
) T
WHERE T.NUM IS NULL;
GO
5. LEFT JOIN / IS NULL
--METHOD 5 (Using LEFT JOIN/IS NULL)
SELECT S1.NUM AS [LEFT JOIN]
FROM #SampleTable1 S1
LEFT JOIN #SampleTable2 S2 ON S1.NUM=S2.NUM
WHERE S2.NUM IS NULL;
GO
6. CORRELATED SUBQUERY
--METHOD 6 (Using CORRELATED SUBQUERY)
SELECT NUM AS [CORRELATED SUBQUERY]
FROM #SampleTable1 AS S1
WHERE (SELECT COUNT(*) FROM #SampleTable2 S2
WHERE S2.NUM = S1.NUM) = 0;
GO
7. ALL
--METHOD 7 (Using <> ALL)
SELECT NUM AS [<> ALL]
FROM #SampleTable1
WHERE NUM <>ALL
(
SELECT NUM
FROM #SampleTable2
);
GO
8. CROSS APPLY
--METHOD 8 (CROSS APPLY)
SELECT T1.NUM AS [CROSS APPLY]
FROM #SampleTable1 AS S1
CROSS APPLY
(
SELECT S1.NUM
EXCEPT
SELECT NUM
FROM #SampleTable2
) T1;
GO
9. FULL OUTER JOIN
--METHOD 9 (Using FULL OUTER JOIN/IS NULL)
SELECT S1.NUM AS [FULL OUTER JOIN]
FROM #SampleTable1 S1
FULL OUTER JOIN #SampleTable2 S2 ON S1.NUM=S2.NUM
WHERE S2.NUM IS NULL;
GO
NOT IN
--METHOD 10 (Using NOT IN)
SELECT NUM AS [NOT IN]
FROM #SampleTable1
WHERE NUM NOT IN
(SELECT NUM FROM #SampleTable2);
GO
For Small Number of Data, NOT EXISTS is best choice irrespective of With/Without Index,Null or Non-Null Values. In this case, LEFT JOIN/IS NULL is least efficient while comparing to the performance of other Alternatives because of its behavior to not skipping the
already matched values with the right table and returning all the results and filtering them out at final steps using IS NULL filter.
But For Large set of Data, Sub query method is not recommended.
Try this solution:
select s.*, d.*
from B20Dept d
cross apply (select distinct YEAR(s.DocDate) Y, MONTH(s.DocDate) THANG from B30AccDocSales s) s
left join (
select YEAR(s.DocDate) Y, MONTH(s.DocDate) THANG, s.DeptCode
from B30AccDocSales s
group by YEAR(s.DocDate), MONTH(s.DocDate), s.DeptCode) m on m.Y = s.Y and m.THANG = s.THANG and m.DeptCode = d.Code
where m.DeptCode is null
order by s.Y, s.THANG
EDIT:
In the query below, You can find a solution for the problem, that is in You PS:
declare #Year int = 2014
select s.*, d.*
from B20Dept d
cross apply (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) s (THANG)
left join (
select MONTH(s.DocDate) THANG, s.DeptCode
from B30AccDocSales s
where YEAR(s.DocDate) = #Year
group by MONTH(s.DocDate), s.DeptCode) m on m.THANG = s.THANG and m.DeptCode = d.Code
where m.DeptCode is null
union
select MONTH(s.DocDate) THANG, '', ''
from B30AccDocSales s
where YEAR(s.DocDate) = #Year
group by MONTH(s.DocDate)
having COUNT(distinct s.DeptCode) = (select count(1) from B20Dept)
order by s.THANG
One simple solution, most of the time faster than not in is :
Select * from A where 0 = ( select count(*) from B where A.id = B.id)
Related
I need to create concatenated query will get data with checking data in other tables.
Firstly I declared (and filled it) two tables with list of ID I'll check in other tables. On next step (2) I declared new table and filled it values I get by some checking params. This table contains only one column (ID).
After filling if I execute SELECT DISTINCT query from this table I'll get really unique ID. It's OK.
But on next step (3) I declare more one table and filling it by 3 tables. Of course it contains many duplicates. But I must create this query for checking and concatenating. And after that if I execute select distinct h from #NonUnicalConcat it returns many duplicate IDs.
What I did wrong? Where is an error?
USE CurrentBase;
--STEP 1
DECLARE #TempCSTable TABLE(TempTableIDColumn int);
INSERT INTO #TempCSTable VALUES('3'),('4');
DECLARE #TempCVTable TABLE(TempTableIDColumn int);
INSERT INTO #TempCVTable VALUES('2'),('13');
--STEP 2
DECLARE #TempIdTable TABLE(id int);
INSERT INTO #TempIdTable
SELECT TT1.ID
FROM Table1 AS TT1
LEFT OUTER JOIN Table2 ON Table2.ID = TT1.OptionalColumn
LEFT OUTER JOIN Table3 AS TT2 ON TT2.ID = TT1.OptionalColumn
LEFT OUTER JOIN Table4 AS TT3 ON TT3.ID = TT2.OptionalColumn
WHERE TT1.ValueDate > '2020-06-30'
AND TT1.ValueDate < '2020-08-04'
AND TT1.OptBool = '1'
AND TT1.OptBool2 = '0'
AND EXISTS
(
SELECT Table5.ID
FROM Table5
WHERE Table5.ID = TT1.ID
AND Table5.CV IN
(
SELECT TempTableIDColumn
FROM #TempCVTable
)
AND Table5.OptBool = '1'
)
AND EXISTS
(
SELECT Table6.ID
FROM Table6
WHERE Table6.IID = TT3.ID
AND Table6.CS IN
(
SELECT TempTableIDColumn
FROM #TempCSTable
)
);
SELECT distinct * FROM #TempIdTable;--this code realy select distinct
--STEP 3
DECLARE #NonUnicalConcat TABLE(c int, s int, h int);
INSERT INTO #NonUnicalConcat
SELECT TT1.TempTableIDColumn AS cc,
TT2.TempTableIDColumn AS ss,
TT3.id AS hh
FROM #TempCVTable AS TT1,
#TempCSTable AS TT2,
#TempIdTable AS TT3
WHERE NOT EXISTS
(
SELECT HID
FROM OtherBase.dbo.Table1
WHERE HID = TT3.id
AND CS = TT2.TempTableIDColumn
AND CV = TT1.TempTableIDColumn
);
select distinct h from #NonUnicalConcat;--this code return many duplicates
i will try and be as clear as possible on this one, as i have no idea what to do next and would love a kick in the right direction.
Im trying to compare the values within 2 tables. The tables look like this:
Table1:
Table2
INSERT INTO #table1 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
INSERT INTO #table2 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
I used this code to fill the testtables. Normally this is an automated process, and the tables are filled from an XML string.
Furthermore, the numbers in the element name are considered "groups" meaning T1 P1 and N1 are together.
I would like to compare T1 and P1 etc from Table1 to any combination of T and P from table2
If they match, i would like to overwrite the value of Table 1 N1 with the value of the matched N on table 2. (in the example, table1 N3 would be replaced with table2 N2
Besides that i also want to keep every group in table 1 that is not in table 2
but also add every group that is in table 2 but not in table 1 on one of the blank spots.
Last but not least, if the T value is filled, but P value is empty, it does not have to overwrite/change anything in table1.
The expected result would be this:
Table1:
i made the changes bold.
I dont really have an idea on where to start on this. Ive tried functions as except and intersect, but did not get even close to what i would like to see.
with t1 as (
select * from (values
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
) v([elementName], [elementValue])
),
t2 as (
select * from (values
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
) v([elementName], [elementValue])
),
pivoted_t1 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t1) t1
pivot(min(value) for letter in ([t], [p], [n])) pvt1
),
pivoted_t2 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t2) t2
pivot(min(value) for letter in ([t], [p], [n])) pvt2
),
amended_values as (
select
pvt1.number,
coalesce(pvt2.t, pvt1.t) as t,
coalesce(pvt2.p, pvt1.p) as p,
coalesce(pvt2.n, pvt1.n) as n,
count(case when pvt1.t = '' and pvt1.p = '' then 1 end) over(order by pvt1.number rows between unbounded preceding and current row) as empty_row_number
from
pivoted_t1 pvt1
left join pivoted_t2 pvt2 on pvt1.t = pvt2.t and pvt1.p = pvt2.p and pvt1.t <> '' and pvt1.p <> ''
),
added_new_values as (
select
a.number,
coalesce(n.t, a.t) as t,
coalesce(n.p, a.p) as p,
coalesce(n.n, a.n) as n
from
amended_values a
left join (
select number, t, p, n, row_number() over (order by number) as row_number
from pivoted_t2 t2
where
t2.t <> ''
and t2.p <> ''
and not exists (select * from pivoted_t1 t1 where t1.t = t2.t and t1.p = t2.p)
) n on n.row_number = a.empty_row_number
)
select
concat([elementName], number) as [elementName],
[elementValue]
from
added_new_values
unpivot ([elementValue] for [elementName] in ([t], [p], [n])) upvt
;
I have added table function in the where clause.
select cmp_id, acno_code, sl_type, sl_code, 0 op_Dr, 0 op_cr, 0 tr_Dr, sum(amount) tr_Cr
from vf_finance
where cmp_id =#cmp_id1
and unitcode in (select * from UTILfn_Split( #unit_code,',') )
and stat_code in ('AT','PR' )
--and pc_code in (select * from UTILfn_Split( #sba,',') )
AND DOC_dT >=convert(datetime,#from_date,103) and doc_Dt <= convert(datetime,#to_date,103)
and amount < 0
GROUP BY cmp_id, acno_code, sl_type, sl_code
) as gl
inner join ps_Accmas acc on acc.cmp_id = gl.cmp_id and acc.acno_Code = gl.acno_code
inner join ps_owner o on gl.cmp_id = o.cmp_id
left outer join view_sl_code sl on gl.cmp_id = sl.cmp_id and gl.sl_type = sl.sl_type and gl.sl_Code = sl.sl_Code
inner join ps_slType slt on gl.cmp_id = slt.cmp_id and gl.sl_Type = slt.sl_type
where sl.sl_type in (select * from UTILfn_Split( #sl_type,',') )
and acc.acno_code in(select * from UTILfn_Split( #facno_code,',') )
group by gl.cmp_id, gl.acno_code,gl.sl_code,gl.sl_type,slt.sl_DEsc,acc.acno_DEsc, sl.sl_DEsc, o.owner_name
order by gl.cmp_id, gl.acno_code,gl.sl_code,gl.sl_type
Can anyone please suggest how I can avoid function in where clause?
You may try this. There are some issues in this existing query which I'll point first
First unitcode in (select * from UTILfn_Split( #unit_code,',') here you must use one column name instead of *, although i don't know about your function UTILfn_Split but still mention column name is preferable.
for your query you may use inner join instead of in with function having return type table.
Instead of
sl.sl_type in (select * from UTILfn_Split( #sl_type,',') )
You may try this
yourtble as sl inner join
(select value from UTILfn_Split( #sl_type,',') as t2
on sl.sl_type = t2.[value] ---- here column name with t2 depends on your function,
---what table structure is returning, in your case it is [value]
(Edited from the original).
In plpgsql, (PostgreSQL 9.2), I have a function defined as:
CREATE OR REPLACE FUNCTION test (patient_recid integer, tencounter timestamp without time zone)
RETURNS SETOF view_dx AS
$BODY$
#variable_conflict use_column
DECLARE
r view_dx%rowtype;
BEGIN
FOR r IN
With person AS (
select ....
)
, alldx AS (
select ....
)
............
select ... from first cte
union
select ... from second cte
union
etc., etc.,
LOOP
r.tposted = ( .
With person AS (
... SAME AS ABOVE,
alldx AS (
... SAME AS ABOVE,
)
select max(b.tposted)
from alldx b
where r.cicd9 = b.code and r.cdesc = b.cdesc);
r.treated = (
With person AS (
........SAME AS ABOVE )
, alldx AS (
........SAME AS ABOVE
)
select ...);
r.resolved = (
With person AS (
select p.chart_recid as recid
from patients p
where p.recid = patient_recid
)
...etc, etc,
RETURN NEXT r;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION test(integer, timestamp without time zone)
OWNER TO postgres;
Edit: Essentially, I have multiple cte's defined which work well in the "For r IN" section of code with multiple unions, but when executing the LOOP...END LOOP section, each CTE needs to be redefined with each SELECT statement. Is there a good way to avoid multiple definitions of the same CTE?
Or is there a better (i.e., faster) way of doing this.
All suggestions are most welcome and appreciated.
TIA
[this is not an answer (too little information, too large program), but a hint for rewriting the stacked CTE.]
The members of the union all appear to be based on select b.* from alldx b, all with a few different extra conditions, mostly based on the existance of other tuples within the same CTE. My suggestion is to unify these, replacing them by boolean flags, as in:
WITH person AS (
SELECT p.chart_recid as recid
FROM patients p
WHERE p.recid = patient_recid
)
, alldx AS (
SELECT d.tposted, d.treated, d.resolved, d.recid as dx_recid, d.pmh, d.icd9_recid
, i.code, i.cdesc, i.chronic
FROM dx d
JOIN icd9 i ON d.icd9_recid = i.recid
JOIN person p ON d.chart_recid = p.recid
WHERE d.tposted::date <= tencounter::date
)
SELECT uni.tposted, uni.treated, uni.resolved, uni.dx_recid, uni.pmh, uni.icd9_recid
, uni.code, uni.cdesc, uni.chronic
, (uni.tposted::date = tencounter::date
) AS is_dx_at_encounter -- bitfield
, EXISTS ( -- a record from a more recent date has resolved this problem.
SELECT 1
FROM alldx x
WHERE x.resolved = true
AND uni.code = x.code AND uni.cdesc = x.cdesc AND uni.tposted = x.tposted
AND x.tposted >= uni.tposted
) AS dx_resolved -- bitfield
, EXISTS ( -- a record from a more recent date has resolved this problem.
SELECT 1
FROM alldx x
WHERE x.resolved = false
AND uni.code = x.code AND uni.cdesc = x.cdesc AND uni.tposted = x.tposted
AND x.tposted > uni.tposted
) AS dx_recurred -- bitfield
, EXISTS ( SELECT * from alldx x where x.chronic = true
AND uni.code = x.code AND uni.cdesc = x.cdesc
) AS dx_chronic -- bitfield
-- etcetera
FROM alldx uni
;
The person CTE could probably be incorporated, too.
and maybe you don't even need the final loop
but you'll have to find out which combination(s) of the resulting bitfields will be needed.
the UNION (without ALL) in the original is a terrible beast: it collects all the results from the union parts, but has to remove duplicates. This will probably introduce a sort-step, since CTE-references tend to hide their key fields or implied ordering from the calling query.
As far as I can tell, CTE's defined before the LOOP do not transfer to the LOOP itself. However, a temporary table can be defined in the BEGIN block which is available in the LOOP block. The following solution runs 50 times faster then my original code. Anybody have a better approach?
CREATE OR REPLACE FUNCTION test2 (patient_recid integer, tencounter timestamp without time zone)
RETURNS SETOF view_dx AS
$BODY$
#variable_conflict use_column
DECLARE
r view_dx%rowtype;
BEGIN
-- create table can only be created in the BEGIN block
Create temp table all_dx ON COMMIT DROP AS
With person AS (
select p.chart_recid as recid
from patients p
where p.recid = patient_recid
)
, alldx AS (
select d.tposted, d.treated, d.resolved, d.recid as dx_recid, d.pmh, d.icd9_recid, i.code, i.cdesc, i.chronic
from dx d
join icd9 i on (d.icd9_recid = i.recid)
join person p on (d.chart_recid = p.recid)
where d.tposted::date <= tencounter::date
)
select * from alldx order by tposted desc;
-- will loop through all the records produced by the unions and assign tposted, pmh, chronic, etc...
FOR r IN
With
dx_at_encounter AS ( -- get all diagnosis at time of encounter
select code, cdesc from all_dx a
where a.tposted::date = tencounter::date
)
, dx_resolved AS ( -- get most recent date of every resolved problem.
select b.* from all_dx b
join (
select a.code, a.cdesc , max(tposted) as tposted
from all_dx a
where a.resolved = true
group by code,cdesc) j
on (b.code = j.code and b.cdesc = j.cdesc and b.tposted = j.tposted)
)
, never_resolved AS ( -- get all problems that have never been resolved before time of encounter.
-- "not exists" is applied to each select output row AFTER the output row b.* is formed.
select b.code, b.cdesc from all_dx b
where not exists
(select 1
from dx_resolved d
where b.code = d.code and b.cdesc = d.cdesc)
)
, recurrent AS ( -- get all recurrent problems. (Problems that are now current after being resolved).
select b.code, b.cdesc
from all_dx b
join dx_resolved r on (b.cdesc = r.cdesc and b.tposted::date > r.tposted::date )
where (b.resolved is null or b.resolved = false)
)
, chronic_dx AS (
select b.code, b.cdesc
from all_dx b
where b.chronic = true
)
-- all diagnosis at time of encounter
select a.code,
a.cdesc
from dx_at_encounter a
union
-- all recurrent problems
select
a.code,
a.cdesc
from recurrent a
union
-- all problems that have never been resolved
select
a.code,
a.cdesc
from never_resolved a
union
--all chonic problems
select
a.code,
a.cdesc
from chronic_dx a
-- LOOP goes to END LOOP which returns back to LOOP to process each of the result records from the unions.
LOOP
r.tposted = ( -- get most recent useage of a diagnosis.
select max(b.tposted)
from all_dx b
where r.cicd9 = b.code and r.cdesc = b.cdesc);
r.treated = (
select b.treated from all_dx b
where b.tposted = r.tposted and b.code = r.cicd9 and b.cdesc = r.cdesc);
r.resolved = (
select b.resolved from all_dx b
where b.tposted = r.tposted and b.code = r.cicd9 and b.cdesc = r.cdesc);
r.pmh = (
select distinct true
from all_dx b
where
b.pmh = true and
b.code = r.cicd9 and
b.cdesc = r.cdesc );
r.chronic = (
select distinct true
from all_dx b
where
b.chronic = true and
b.code = r.cicd9 and
b.cdesc = r.cdesc);
RETURN NEXT r; -- return current row of SELECT
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION test2(integer, timestamp without time zone)
OWNER TO postgres;
I have one table vwuser. I want join this table with the table valued function fnuserrank(userID). So I need to cross apply with table valued function:
SELECT *
FROM vwuser AS a
CROSS APPLY fnuserrank(a.userid)
For each userID it generates multiple records. I only want the last record for each empid that does not have a Rank of Term(inated). How can I do this?
Data:
HistoryID empid Rank MonitorDate
1 A1 E1 2012-8-9
2 A1 E2 2012-9-12
3 A1 Term 2012-10-13
4 A2 E3 2011-10-09
5 A2 TERM 2012-11-9
From this 2nd record and 4th record must be selected.
In SQL Server 2005+ you can use this Common Table Expression (CTE) to determine the latest record by MonitorDate that doesn't have a Rank of 'Term':
WITH EmployeeData AS
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY empId, ORDER BY MonitorDate DESC) AS RowNumber
FROM vwuser AS a
CROSS APPLY fnuserrank(a.userid)
WHERE Rank != 'Term'
)
SELECT *
FROM EmployeeData AS ed
WHERE ed.RowNumber = 1;
Note: The statement before this CTE will need to end in a semi-colon. Because of this, I have seen many people write them like ;WITH EmployeeData AS...
You'll have to play with this. Having trouble mocking your schema on sqlfiddle.
Select bar.*
from
(
SELECT *
FROM vwuser AS a
CROSS APPLY fnuserrank(a.userid)
where rank != 'TERM'
) foo
left join
(
SELECT *
FROM vwuser AS b
CROSS APPLY fnuserrank(b.userid)
where rank != 'TERM'
) bar
on foo.empId = bar.empId
and foo.MonitorDate > bar.MonitorDate
where bar.empid is null
I always need to test out left outers on dates being higher. The way it works is you do a left outer. Every row EXCEPT one per user has row(s) with a higher monitor date. That one row is the one you want. I usually use an example from my code, but i'm on the wrong laptop. to get it working you can select foo., bar. and look at the results and spot the row you want and make the condition correct.
You could also do this, which is easier to remember
SELECT *
FROM vwuser AS a
CROSS APPLY fnuserrank(a.userid)
) foo
join
(
select empid, max(monitordate) maxdate
FROM vwuser AS b
CROSS APPLY fnuserrank(b.userid)
where rank != 'TERM'
) bar
on foo.empid = bar.empid
and foo.monitordate = bar.maxdate
I usually prefer to use set based logic over aggregate functions, but whatever works. You can tweak it also by caching the results of your TVF join into a table variable.
EDIT:
http://www.sqlfiddle.com/#!3/613e4/17 - I mocked up your TVF here. Apparently sqlfiddle didn't like "go".
select foo.*, bar.*
from
(
SELECT f.*
FROM vwuser AS a
join fnuserrank f
on a.empid = f.empid
where rank != 'TERM'
) foo
left join
(
SELECT f1.empid [barempid], f1.monitordate [barmonitordate]
FROM vwuser AS b
join fnuserrank f1
on b.empid = f1.empid
where rank != 'TERM'
) bar
on foo.empId = bar.barempid
and foo.MonitorDate > bar.barmonitordate
where bar.barempid is null