First of all, execuse the longer question, but I will try to put it as simply as possible...
I'm trying to write a kind of a reporting query, but I'm having a problem getting the desired results. The problem:
Employee table
Id | Name
---------------
1 | John Smith
2 | Alan Jones
3 | James Jones
Task table
Id | Title | StartDate | EmployeeId | Estimate (integer - ticks)
----------------------------------------------------------------------------
1 | task1 | 21.08.2011 | 1 | 90000000000
2 | task2 | 21.08.2011 | 1 | 150000000
3 | task3 | 22.08.2011 | 2 | 1230000000
Question:
How to get the estimate summary per day, grouped, but to include all the employees?
Like this:
Date | EmployeeId | EmployeeName | SummaryEstimate
-------------------------------------------------------------
19.08.2011 | 1 | John Smith | NULL
19.08.2011 | 2 | Alan Jones | NULL
19.08.2011 | 3 | James Jones | NULL
20.08.2011 | 1 | John Smith | NULL
20.08.2011 | 2 | Alan Jones | NULL
20.08.2011 | 3 | James Jones | NULL
21.08.2011 | 1 | John Smith | 90150000000
21.08.2011 | 2 | Alan Jones | NULL
21.08.2011 | 3 | James Jones | NULL
22.08.2011 | 1 | John Smith | NULL
22.08.2011 | 2 | Alan Jones | 1230000000
22.08.2011 | 3 | James Jones | NULL
What I currently do is I have a "dates" table with 30years of days. I left join and group by that table to get other dates included too. Well, here is the query:
SELECT dates.value, employee.Id, employee.Name, sum(task.Estimate)
FROM TableOfDates as dates
left join Tasks as task on (dates.value = convert(varchar(10), task.StartTime, 101))
left join Employees as employee on (employee.Id = task.EmployeeId)
WHERE dates.value >= '2011-08-19' and dates.value < '2011-08-22'
GROUP BY dates.value, employee.Id, employee.Name
ORDER BY dates.value, employee.Id
The convert call is to get the date part of the DateTime column.
The result that I get is:
Date | EmployeeId | EmployeeName | SummaryEstimate
-------------------------------------------------------------
19.08.2011 | NULL | NULL | NULL
20.08.2011 | NULL | NULL | NULL
21.08.2011 | 1 | John Smith | 90150000000
22.08.2011 | 2 | Alan Jones | 1230000000
I am there half of the way, I get dates that are not in the two base joined tables (Employees and Tasks) but I cannot also have all the employees included as in the table shown before this one.
I've tried cross-joining, then subqueries, but little luck there. Any help would be very much appreciated ! Thank you for having the time to go through all of this, I hope I was clear enough...
SELECT DE.DateValue, DE.EmployeeId, DE.EmployeeName, sum(task.Estimate)
FROM
( SELECT
D.value AS DateValue
, E.Id AS EmployeeId
, E.Name AS EmployeeName
FROM
TableOfDates D
CROSS JOIN Employees E ) DE
left join Tasks as task on DE.DateValue = convert(varchar(10), task.StartTime, 101)
AND DE.EmployeeId = task.EmployeeId
WHERE DE.DateValue >= '2011-08-19' and DE.DateValue < '2011-08-22'
GROUP BY DE.DateValue, DE.EmployeeId, DE.EmployeeName
ORDER BY DE.DateValue, DE.EmployeeId
Note that this solution offers the possibility to drop the day-table as you may use a dynamic recursive CTE instead.
The other CTE:s (Employees and Tasks) can be substituted with the real tables.
DECLARE #startDate DATETIME = '2011-08-01'
DECLARE #endDate DATETIME = '2011-09-01'
;WITH Employees(Id,Name)
AS
(
SELECT 1, 'John Smith'
UNION ALL
SELECT 2, 'Alan Jones'
UNION ALL
SELECT 3, 'James Jones'
)
,Tasks (Id, Title, StartDate, EmployeeId, Estimate)
AS
(
SELECT 1, 'task1', '2011-08-21', 1, 90000000000
UNION ALL
SELECT 2, 'task2', '2011-08-21', 1, 150000000
UNION ALL
SELECT 3, 'task3', '2011-08-22', 2, 1230000000
)
,TableOfDates(value)
AS
(
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, #startDate), 0)
UNION ALL
SELECT DATEADD(DAY, 1, value)
FROM TableOfDates
WHERE value < #endDate
)
SELECT dates.value
,employee.Id
,employee.Name
,SUM(task.Estimate) AS SummaryEstimate
FROM TableOfDates dates
CROSS JOIN Employees employee
LEFT JOIN Tasks task
ON dates.value = task.StartDate
AND (employee.Id = task.EmployeeId)
WHERE dates.value >= '2011-08-19'
AND dates.value < '2011-08-26'
GROUP BY
dates.value
,employee.Id
,employee.Name
ORDER BY
dates.value
,employee.Id
use this query:
create table #T_dates (id_date int identity(1,1),inp_date datetime)
create table #T_tasks (id_task int identity(1,1),key_date int, key_emp int, est int)
create table #T_emp (id_emp int identity(1,1),name varchar(50))
insert #T_dates (inp_date) values ('08.19.2011')
insert #T_dates (inp_date) values ('08.20.2011')
insert #T_dates (inp_date) values ('08.21.2011')
insert #T_dates (inp_date) values ('08.22.2011')
insert #T_dates (inp_date) values ('08.23.2011')
insert #T_dates (inp_date) values ('08.24.2011')
--select * from #T_dates
insert #T_emp (name) values ('John Smith')
insert #T_emp (name) values ('Alan Jones')
insert #T_emp (name) values ('James Jones')
--select * from #T_emp
insert #T_tasks (key_date,key_emp,est) values (4,1,900000)
insert #T_tasks (key_date,key_emp,est) values (4,1,15000)
insert #T_tasks (key_date,key_emp,est) values (5,2,123000)
--select * from #T_tasks
select inp_date,id_emp,name,EST
from #T_emp
cross join #T_dates
left join
(
select key_date,key_emp,SUM(est) 'EST' from #T_tasks group by key_date,key_emp
) Gr
ON Gr.key_emp = id_emp and Gr.key_date = id_date
where inp_date >= '2011-08-19' and inp_date <= '2011-08-22'
order by inp_date,id_emp
Related
I have a table that contains a list of names. However, some rows contain the name and the alias separated by , f/k/a—, , f/k/a or , n/k/a . I'm trying to split the names and aliases into separate rows. Can someone please help?
Sample data below:
|---------------------------------------------------------|
| ID | Name |
|---------------------------------------------------------|
| 1 | Evil Empire, f/k/a - Starbucks |
| 2 | Aubrey Drake Graham, n/k/a Drake |
| 3 | Thomas Johnson Bridge, f/k/a Solomans Bridge |
|---------------------------------------------------------|
Desired output below:
|---------------------------------------------------------|
| ID | Name |
|---------------------------------------------------------|
| 1 | Evil Empire |
| 1.1 | Starbucks |
| 2 | Aubrey Drake Graham |
| 2.1 | Drake |
| 3 | Thomas Johnson Bridge |
| 3.1 | Solomans Bridge |
|---------------------------------------------------------|
No need for ordinal splitter. It's a simple unpivot using CROSS APPLY
[EDIT] Changed method of splitting to look for '%/%/%' in cases where there's an alias
select cast(unpvt.id as varchar(9))+iif(unpvt.seq=1, '', '.1') ID,
trim(replace(replace(replace(unpvt.[Name],'f/k/a - ',''),'f/k/a ',''),'n/k/a ','')) [Name]
from (values (1, 'Evil Empire, f/k/a - Starbucks'),
(2, 'Aubrey Drake Graham, n/k/a Drake'),
(3, 'Thomas Johnson Bridge, f/k/a Solomans Bridge'),
(4, 'Thomas, J, Cat, f/k/a Solomans,,,Bridge'),
(5, 'Thomas')) v(id, [Name])
cross apply (values (v.id, substring(v.[Name], 1, patindex('%/%/%', v.Name)-4), 1),
(v.id, substring(v.[Name], patindex('%/%/%', v.Name)-1, len(v.[Name])), 2)) unpvt(id, [Name], seq)
where patindex('%/%/%', v.[Name])>1;
Results
ID Name
1 Evil Empire
1.1 Starbucks
2 Aubrey Drake Graham
2.1 Drake
3 Thomas Johnson Bridge
3.1 Solomans Bridge
4 Thomas, J, Cat
4.1 Solomans,,,Bridge
As you can't use the built in string_split you will need to add a Table Valued Function to do that for you. Using one of these allows you to split your data like this:
Query
declare #t table(ID int,[Name] varchar(100));
insert into #t values
(1,'Evil Empire, f/k/a - Starbucks')
,(2,'Aubrey Drake Graham, n/k/a Drake')
,(3,'Thomas Johnson Bridge, f/k/a Solomans Bridge')
;
select case when s.rn = 1
then t.ID
else t.ID + ((s.rn - 1)/10.)
end as ID
,replace(replace(replace(s.item,' f/k/a - ',''),' f/k/a ',''),' n/k/a ','')
from #t as t
cross apply dbo.fn_StringSplit4k(t.[Name],',',null) as s
order by t.ID
,s.rn;
Output
+----------+-----------------------+
| ID | Name |
+----------+-----------------------+
| 1.000000 | Evil Empire |
| 1.100000 | Starbucks |
| 2.000000 | Aubrey Drake Graham |
| 2.100000 | Drake |
| 3.000000 | Thomas Johnson Bridge |
| 3.100000 | Solomans Bridge |
+----------+-----------------------+
Function
create function [dbo].[fn_StringSplit4k]
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(20) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where case when #delimiter = '' and t < len(#str) then 1 else case when substring(isnull(#str,''),t,1) = #delimiter then 1 else 0 end end = 1)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,case when #delimiter = '' then 1 else isnull(nullif(charindex(#delimiter,isnull(#str,''),s),0)-s,4000) end from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
) a
where rn = #num
or #num is null;
I am creating a code to join two different tables under a certain condition. The tables look like this
(TABLE 2)
date | deal_code | originator | servicer | random |
-----------------------------------------------------
2011 | 001 | commerzbank | SPV1 | 1 |
2012 | 001 | commerzbank | SPV1 | 12 |
2013 | 001 | commerzbank | SPV1 | 7 |
2013 | 005 | unicredit | SPV2 | 7 |
and another table
(TABLE 1)
date | deal_code | amount |
---------------------------
2011 | 001 | 100 |
2012 | 001 | 100 |
2013 | 001 | 100 |
2013 | 005 | 200 |
I would like to have this as the final result
date | deal_code | amount | originator | servicer | random |
--------------------------------------------------------------
2013 | 001 | 100 | commerzbank | SPV1 | 7 |
2013 | 005 | 200 | unicredit | SPV2 | 7 |
I created the following code
select q1.deal_code, q1.date
from table1 q1
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)
that gives me:
(TABLE 3)
date | deal_code | amount |
---------------------------
2013 | 001 | 100 |
2013 | 005 | 200 |
That is the latest observation for table 1, now I would like to have the originator and servicer information given the deal_code and date. Any suggestion? I hope to have been clear enough. Thanks.
This should do what you are looking for. Please be careful when naming columns. Date is a reserved word and is too ambiguous to be a good name for a column.
declare #Something table
(
SomeDate int
, deal_code char(3)
, originator varchar(20)
, servicer char(4)
, random int
)
insert #Something values
(2011, '001', 'commerzbank', 'SPV1', 1)
, (2012, '001', 'commerzbank', 'SPV1', 12)
, (2013, '001', 'commerzbank', 'SPV1', 7)
, (2013, '005', 'unicredit ', 'SPV2', 7)
declare #SomethingElse table
(
SomeDate int
, deal_code char(3)
, amount int
)
insert #SomethingElse values
(2011, '001', '100')
, (2012, '001', '100')
, (2013, '001', '100')
, (2013, '005', '200')
select x.SomeDate
, x.deal_code
, x.originator
, x.servicer
, x.random
, x.amount
from
(
select s.SomeDate
, s.deal_code
, s.originator
, s.servicer
, s.random
, se.amount
, RowNum = ROW_NUMBER()over(partition by s.deal_code order by s.SomeDate desc)
from #Something s
join #SomethingElse se on se.SomeDate = s.SomeDate and se.deal_code = s.deal_code
) x
where x.RowNum = 1
Looks like this would work:
DECLARE #MaxYear INT;
SELECT #MaxYear = MAX(date)
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.deal_code = t2.deal_code;
SELECT t1.date,
t1.deal_code,
t1.amount,
t2.originator,
t2.servicer,
t2.random
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.date = #MaxYear
AND t1.deal_code = t2.deal_code;
I agree with Sean Lange about the date column name. His method gets around the dependency on the correlated sub-query, but at the heart of things, you really just need to add an INNER JOIN to your existing query in order to get the amount column into your result set.
select
q2.date,
q2.deal_code,
q1.amount,
q2.originator,
q2.servicer,
q2.random
from
table1 q1
join
table2 q2
on q1.date = q2.date
and q1.deal_code = q2.deal_code
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)
Please check http://sqlfiddle.com/#!18/1acd1/2
I'm passing UserId to get User's countryid so I can fetch plans according to user's country.
I have plans specific to countries but I don't have plans for all countries so when I pass userid where I don't have any plan for that user/country at the moment no rows returns but in that case I need to show default plans (plans with country id = '1') to that user.
Please see fiddle, In case of userid='4' no rows returns so in that case I need to show default plans to that user. all default plans have countryid=1
I hope you understand my question :) thanks
Here is my take on it.
I took a different approach and started from the tblProfile because your input for this query is actually a UserId so I found it more logical to follow this process:
For the UserId, fetch PlanId of his CountryId
If User's CountryId does not exist in lstCountry or has no PlanId replace User's CountryId by the default CountryId=1 (as mentioned in the comments below)
Finally, fetch Plan details and Currency for the final CountryId
Tables definitions
CREATE TABLE lstCountry(
CountryID int,
CountryTitle varchar(100),
CurrencyCode varchar(3))
INSERT INTO lstCountry (CountryId, CountryTitle, CurrencyCode) VALUES
(1, 'USA', 'USD'),
(2, 'GB', 'GBP'),
(3, 'France', 'EUR')
CREATE TABLE tblProfile(
UserId int,
CountryId int)
INSERT INTO tblProfile (UserId, CountryId) VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4)
CREATE TABLE tblPlan (
PlanId int,
PlanTitle nvarchar(50),
PlanPrice money,
CountryId int)
INSERT INTO tblPlan (PlanId, PlanTitle, PlanPrice, CountryId) VALUES
(1, 'PlanDefault', 10, 1),
(2, 'Plan2', 20, 2),
(3, 'Plan3', 30, 3),
(4, 'PlanDefault', 40, 1),
(5, 'Plan5', 50, 2)
Query
select distinct f.PlanId,
f.PlanTitle,
f.PlanPrice,
e.CurrencyCode
from (
select case when c.PlanId is null then '1' else a.CountryId end as CountryId
from tblProfile a
left join lstCountry b on a.CountryId=b.CountryId
left join tblPlan c on b.CountryId=c.CountryId
where UserId=4
) d
inner join lstCountry e on d.CountryId=e.CountryId
inner join tblPlan f on d.CountryId=f.CountryId
Result for UserId=1:
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-------------|-----------|--------------|
| 1 | PlanDefault | 10 | USD |
| 4 | PlanDefault | 40 | USD |
Result for UserId=2
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-----------|-----------|--------------|
| 2 | Plan2 | 20 | GBP |
| 5 | Plan5 | 50 | GBP |
Result for UserId=3
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-----------|-----------|--------------|
| 3 | Plan3 | 30 | EUR |
Result for UserId=4
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-------------|-----------|--------------|
| 1 | PlanDefault | 10 | USD |
| 4 | PlanDefault | 40 | USD |
Here is the SQL Fiddle
http://sqlfiddle.com/#!18/7068c/8/0
I think a LEFT JOIN and second parse of the table might achieve this. If not, then please look at #JuanCarlosOropeza comments and update your post with Sample and expected data.
SELECT p.PlanId,
p.PlanTitle,
p.PlanPrice,
ISNULL(c.CurrencyCode, d.CurrencyCode) AS CurrencyCode
FROM tblPlan p
LEFT JOIN lstCountry c ON c.CountryId = p.CountryId
LEFT JOIN lstCountry d ON c.CountryId IS NULL
AND d.CountryId = 226
WHERE P.CountryId = (SELECT sq.CountryId
FROM tblProfile sq --Not sure if this requires a Default Country
WHERE UserId = #UserId)
AND p.IsVisible = '1';
I have the following query, where the intention is to show each record with the time until the next record
Data:
gid time name
1010883478 29/03/2016 0:00:02 John
1010883527 29/03/2016 0:00:04 John
1010883578 29/03/2016 0:00:06 John
SQL:
SELECT A.[gid]
,A.[time]
,A.[name]
,(B.[time] - A.[time]) as timeTilNext
FROM [location] A CROSS JOIN [location] B
WHERE B.[gid] IN (
SELECT MIN(C.[gid])
FROM [location] C
WHERE C.[gid] > A.[gid] AND C.[name] = A.[name] )
ORDER BY A.[gid]
Current Output:
gid time name timeTilNext
1010883478 2016-03-29 00:00:02.000 John 1900-01-01 00:00:02.000
1010883527 2016-03-29 00:00:04.000 John 1900-01-01 00:00:02.000
Expected Output:
gid time name timeTilNext
1010883478 2016-03-29 00:00:02.000 John 1900-01-01 00:00:02.000
1010883527 2016-03-29 00:00:04.000 John 1900-01-01 00:00:02.000
1010883578 2016-03-29 00:00:06.000 John -1 (or whatever)
However, it does not show a record for the highest [gid] for a given [name] (only the second highest).
I'm hoping for the highest [gid] to show -1 for timeTilNext, to indicate that there is no more events.
Any ideas about how to modify my query?
In SQL Server 2012 you can use LEAD window function to get the value of the "next" row.
DECLARE #location TABLE ([gid] int, [time] datetime, [name] varchar(50));
INSERT INTO #location ([gid], [time], [name]) VALUES
(1010883478, '2016-03-29 00:00:02', 'John'),
(1010883527, '2016-03-29 00:00:04', 'John'),
(1010883578, '2016-03-29 00:00:06', 'John');
SELECT
A.[gid]
,A.[time]
,A.[name]
,LEAD(A.[time]) OVER(PARTITION BY A.[name] ORDER BY A.[gid]) AS NextTime
,ISNULL(DATEDIFF(second, A.[time],
LEAD(A.[time]) OVER(PARTITION BY A.[name] ORDER BY A.[gid])), -1) AS SecondsTillNext
FROM #location A
ORDER BY A.[gid];
Result
+------------+-------------------------+------+-------------------------+-----------------+
| gid | time | name | NextTime | SecondsTillNext |
+------------+-------------------------+------+-------------------------+-----------------+
| 1010883478 | 2016-03-29 00:00:02.000 | John | 2016-03-29 00:00:04.000 | 2 |
| 1010883527 | 2016-03-29 00:00:04.000 | John | 2016-03-29 00:00:06.000 | 2 |
| 1010883578 | 2016-03-29 00:00:06.000 | John | NULL | -1 |
+------------+-------------------------+------+-------------------------+-----------------+
If the "next" row is not available, LEAD would return NULL. You can use ISNULL() to replace it with some non-null value if you want.
select
*,-1 as 'time until next ' from location t1
where time=(select max(time) from location t2 where t1.name=t2.name) b
SELECT A.gid,A.name,A.time,
(
(SELECT MIN(B.time) FROM [location] B WHERE B.time>A.time AND B.name=A.name)
-
A.time
) as timeTilNext
FROM [location] A
I need to write a statement joining two tables based on dates.
Table 1 contains time recording entries.
+----+-----------+--------+---------------+
| ID | Date | UserID | DESC |
+----+-----------+--------+---------------+
| 1 | 1.10.2010 | 5 | did some work |
| 2 | 1.10.2011 | 5 | did more work |
| 3 | 1.10.2012 | 4 | me too |
| 4 | 1.11.2012 | 4 | me too |
+----+-----------+--------+---------------+
Table 2 contains the position of each user in the company. The ValidFrom date is the date at which the user has been or will be promoted.
+----+-----------+--------+------------+
| ID | ValidFrom | UserID | Pos |
+----+-----------+--------+------------+
| 1 | 1.10.2009 | 5 | PM |
| 2 | 1.5.2010 | 5 | Senior PM |
| 3 | 1.10.2010 | 4 | Consultant |
+----+-----------+--------+------------+
I need a query which outputs table one with one added column which is the position of the user at the time the entry has been made. (the Date column)
All date fileds are of type date.
I hope someone can help. I tried a lot but don't get it working.
Try this using a subselect in the where clause:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE TimeRecord
(
ID INT,
[Date] Date,
UserID INT,
Description VARCHAR(50)
)
INSERT INTO TimeRecord
VALUES (1,'2010-01-10',5,'did some work'),
(2, '2011-01-10',5,'did more work'),
(3, '2012-01-10', 4, 'me too'),
(4, '2012-11-01',4,'me too')
CREATE TABLE UserPosition
(
ID Int,
ValidFrom Date,
UserId INT,
Pos VARCHAR(50)
)
INSERT INTO UserPosition
VALUES (1, '2009-01-10', 5, 'PM'),
(2, '2010-05-01', 5, 'Senior PM'),
(3, '2010-01-10', 4, 'Consultant ')
Query 1:
SELECT TR.ID,
TR.[Date],
TR.UserId,
TR.Description,
UP.Pos
FROM TimeRecord TR
INNER JOIN UserPosition UP
ON UP.UserId = TR.UserId
WHERE UP.ValidFrom = (SELECT MAX(ValidFrom)
FROM UserPosition UP2
WHERE UP2.UserId = UP.UserID AND
UP2.ValidFrom <= TR.[Date])
Results:
| ID | Date | UserId | Description | Pos |
|----|------------|--------|---------------|-------------|
| 1 | 2010-01-10 | 5 | did some work | PM |
| 2 | 2011-01-10 | 5 | did more work | Senior PM |
| 3 | 2012-01-10 | 4 | me too | Consultant |
| 4 | 2012-11-01 | 4 | me too | Consultant |
You can do it using OUTER APPLY:
SELECT ID, [Date], UserID, [DESC], x.Pos
FROM table1 AS t1
OUTER APPLY (
SELECT TOP 1 Pos
FROM table2 AS t2
WHERE t2.UserID = t1.UserID AND t2.ValidFrom <= t1.[Date]
ORDER BY t2.ValidFrom DESC) AS x(Pos)
For every row of table1 OUTER APPLY operation fetches all table2 rows of the same user that have a ValidFrom date that is older or the same as [Date]. These rows are sorted in descending order and the most recent of these is finally returned.
Note: If no match is found by the OUTER APPLY sub-query then a NULL value is returned, meaning that no valid position exists in table2 for the corresponding record in table1.
Demo here
This works by using a rank function and subquery. I tested it with some sample data.
select sub.ID,sub.Date,sub.UserID,sub.Description,sub.Position
from(
select rank() over(partition by t1.userID order by t2.validfrom desc)
as 'rank', t1.ID as'ID',t1.Date as'Date',t1.UserID as'UserID',t1.Descr
as'Description',t2.pos as'Position', t2.validfrom as 'validfrom'
from temployee t1 inner join jobs t2 on -- replace join tables with your own table names
t1.UserID=t2.UserID
) as sub
where rank=1
This query would work
select t1.*,t2.pos from Table1 t1 left outer join Table2 t2 on
t1.Date=t2.Date and t1.UserID=t2.UserID