Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 Find latest records

Author  Topic 

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2007-12-16 : 02:50:40
Hi all,

I have a question regarding SQL Server Performance and would be grateful for a tip. Let's say I have a DB with 50.000 records. These records belong to 1.000 different datasets, so there is 1 actual and 49 historical data records. For example a company with 1000 employees has a database where each year a new record is created for each employee so after 50 years they have 50.000 records (50 years x 1000 employees). 1 record is actual, and 49 are historical. What is the best way to store this? Main target is performance for the enduser, so when an employee clicks "See all my records" it should be fast. But on the other hand the application mainly works only with the current year. Additionally it should also be fast for the boss of business unit who wants to see the latest records of his e.g. 100 employees. I have some ideas and would like to get your opinion:

1. Retrieve by latest date
Just store the records. To get the current year just select the record with the latest year. Disadvantage might be with larger databases: If the company switches to store the requests per month, each user would have 600 records (12 months x 50 years). Each time the latest record should be retrieved, 600 recards have to be compared regarding the latest date (or sorted by date descending using Top1, but this might be a problem for the boss then? Or could this be combined for a group if the boss wants to see all the latest records of his employees?).

2. Add a 'IsCurrent'-Flag
Each time a new record is stored it should be compared to the latest record. If it is newer, the 'IsCurrent'-Flag should be removed and then checked on the new record. This should be fast processed (because on saving a new record it only needs to be checked against the currently 'IsCurrent'-flagged record), and for retrieving the current record no further comparison is necessary. But how could I do this? I need to update the "AddRecord"-SP with this comparison, but I don't know how to do this.

Currently number 2 is my favorite, I just don't know how to do it ;-) What is your opinion about it, and could you include an example?

Thanks

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2007-12-16 : 03:49:25
Option 2 is not efficient. Option 1 is preferable. If you have index on the date column there are various way to retrieve the record fast. You can use max() on the date or row_number() over (order by date desc)


KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2007-12-16 : 07:40:20
May be go for first option. Additionally partition the table based on date and add an index on latest partition (having recent records)for faster access.
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2007-12-16 : 09:46:26
there is no need to partition a table with only 50k rows.


elsasoft.org
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2007-12-17 : 03:15:37
Hi all,

thanks a lot for your comments. Unfortunately I'm a bit lost now as I'm not an SQL expert. Partition the table? Max on the date?

Here is the example in more details, maybe someone could help me with this?

Company with 1000 employees. Each month there might be a new record for an employee, but it's not necessary. Some employees have 12 records in a year, and some only 1 or 2 or 5 ....

Employee ID | Business Unit | Record Date | Some value
123.........| Sales.........| 2007/01/20 | ABCDEF
456.........| Sales.........| 2007/02/01 | GHIJKL
789.........| Marketing | 2007/02/01 | MNOPQR
123.........| Sales.........| 2007/02/01 | STUVWX
123.........| Sales.........| 2007/05/17 | ABCDEF
789.........| Marketing | 2007/07/19 | GHIJKL

So now I need to do this:
Retrieve all latest records from a given Business Unit. So for Business Unit Sales I want to get back 2 records, from 123 on 2007/05/17 and from 456 on 2007/02/01. But I'm not sure how to do this . I need to have a stored procedure like 'GetCurrentEmployeeRecords' where I give the Business Unit as an input-parameter. I guess for you guys this is easy so I'm very grateful for any help. Many thanks!
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2007-12-17 : 03:42:59
Create Procedure GetCurrentEmployeeRecords
@BusinessUnit varchar(50)
AS
SELECT e.*
FROM Employees e
INNER JOIN
(
SELECT EmployeeID,BusinessUnit,MAX(Record Date) AS 'Record Date' FROM Employees
WHERE BusinessUnit=@BusinessUnit
GROUP BY EmployeeID,BusinessUnit)t
ON t.EmployeeID=e.EmployeeID
AND t.BusinessUnit=e.BusinessUnit
AND t.Record Date =e.Record Date


GO
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2007-12-17 : 03:49:53
Another method:-

Create Procedure GetCurrentEmployeeRecords
@BusinessUnit varchar(50)
AS
;
With Emp_CTE (RowNo,EmpID,BusUnit,Date,Value) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID Order by Record Date DESC) AS 'RowNo',
EmployeeID,
BusinessUnit,
Record Date,
Value
FROM Employees
WHERE BusinessUnit=@BusinessUnit
)

SELECT * from Emp_CTE WHERE RowNo=1

GO
Go to Top of Page

kutumbarao
Starting Member

13 Posts

Posted - 2007-12-17 : 03:54:46
Hi Please try with this

CREATE PROCEDURE dbo.USP_EmployeeBusinessDetails
(
@BusinessUnit VARCHAR(255)
)
AS
BEGIN
SELECT * FROM
EmployeeBusinessUintDetails EBU
INNER JOIN (
SELECT EmployeeID, MAX(RecordDate) RecordDate
FROM EmployeeBusinessUintDetails
WHERE BusinessUnit = @BusinessUnit
GROUP BY EmployeeBusinessUintDetails
) X
ON X.EmployeeID = EBU.EmployeeID AND X.RecordDate = EBU.RecordDate
WHERE BusinessUnit = @BusinessUnit
END
Go to Top of Page

kutumbarao
Starting Member

13 Posts

Posted - 2007-12-17 : 04:27:20
Hi Please try with this

CREATE PROCEDURE dbo.USP_EmployeeBusinessDetails
(
@BusinessUnit VARCHAR(255)
)
AS
BEGIN
SELECT * FROM
EmployeeBusinessUintDetails EBU
INNER JOIN (
SELECT EmployeeID, MAX(RecordDate) RecordDate
FROM EmployeeBusinessUintDetails
WHERE BusinessUnit = @BusinessUnit
GROUP BY EmployeeBusinessUintDetails
) X
ON X.EmployeeID = EBU.EmployeeID AND X.RecordDate = EBU.RecordDate
WHERE BusinessUnit = @BusinessUnit
END
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2007-12-19 : 05:19:07
Dear Visakh16 and kutumbarao,

thanks for your help. I'll try it the next days!
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-01-04 : 02:47:27
Hi all,

I've used the first posting from visakh16 and this works fine . I even managed to add another INNER JOIN so now I've connected 3 tables (backslapping ).

But as usual, requirements are changing.... Now I have an additionall table with around 2-3 million records per year. The requirement here is the same as before: Find the latest records for a given Business Unit. So technically I would be able to do it also with your suggestions which are already working. But I guess this is a number of rows where I should think about partitioning as supposed by you, visakh16, right? I would appreciate any feedback. Many thanks!
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-01-04 : 09:09:09
3m rows is still not that many. It doesn't automatically call for partitioning.


elsasoft.org
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2008-01-04 : 10:39:11
I don't store archive records in the same table as current data.

Create your primary table and then run the script below, which will generate a script for creating an archive table and a trigger on your main table to process it.

AVOID using bit flags to designate "current" or "deleted" records. These are known as "soft deletes", and will really suck down your database performance.

set nocount on
--Auto archive script
--blindman, 3/2/2006
--blindman, 11/7/2007: Enhanced to handle inserts/updates/deletes

--creates a script to create archive tables and triggers for saving deleted records.

declare @TableNameMask varchar(50)
declare @ArchiveSuffix varchar(50)
declare @DeletedColumnName varchar(50)
set @TableNameMask = 'TableNameMask'
set @ArchiveSuffix = '_arc'
set @DeletedColumnName = 'DeletedFlag' --Will indicate whether archive record was the result of a delete operation.

declare @TableName varchar(500)
declare @FieldDefs varchar(max)
declare @FieldList varchar(max)
declare @DeletedList varchar(max)
declare @SQLStringTABLECREATE varchar(max)
declare @SQLStringTRIGGERHEADER varchar(max)
declare @SQLStringTRIGGERINSERT varchar(max)
declare @SQLStringTRIGGERDELETE varchar(max)
declare @KeyList varchar(max)
declare @FirstKey varchar(100)


/*
select '/*'
select name as 'The following tables will be scripted:'
from sysobjects
where type = 'U'
and name not like 'dt%'
and name not like '%' + @ArchiveSuffix
and name not like '%staging%'
order by name
select '*/'
*/

declare TableList cursor for
select name
from sysobjects
where type = 'U'
and name like @TableNameMask
and name not like 'dt%'
and name not like '%' + @ArchiveSuffix
and name not like '%staging%'
and name not in ('sysdiagrams')
order by name

open TableList
fetch next from TableList into @TableName

while @@fetch_status = 0
begin
set @FieldDefs = null
set @FieldList = null
set @DeletedList = null
set @SQLStringTABLECREATE = null
set @SQLStringTRIGGERHEADER = null
set @SQLStringTRIGGERINSERT = null
set @SQLStringTRIGGERDELETE = null
set @KeyList = null
set @FirstKey = null

select @FieldDefs = isnull(@FieldDefs + ',' +char(10) + ' ', '')
+ syscolumns.name
+ ' ' + systypes.name
+ isnull(' (' + case systypes.name
when 'timestamp' then Null
when 'smalldatetime' then Null
when 'datetime' then Null
when 'int' then Null
when 'bigint' then Null
when 'smallint' then Null
when 'tinyint' then Null
when 'real' then Null
when 'bit' then Null
when 'money' then Null
when 'uniqueidentifier' then Null
when 'xml' then Null
when 'decimal' then cast(syscolumns.prec as varchar(10)) + ', ' + cast(syscolumns.scale as varchar(10))
when 'numeric' then cast(syscolumns.prec as varchar(10)) + ', ' + cast(syscolumns.scale as varchar(10))
else case when syscolumns.length = -1 then 'max' else cast(syscolumns.length as varchar(10)) end
end + ')', '')
+ ' ' + case syscolumns.isnullable when 0 then 'NOT NULL' else 'NULL' end
from syscolumns
inner join sysobjects on syscolumns.id = sysobjects.id
inner join systypes on syscolumns.xusertype = systypes.xusertype
where sysobjects.name = @TableName
and systypes.name not in ('text', 'ntext', 'image') --These cannot be referenced in the deleted table
and syscolumns.name not in ('Modified', 'Modifier')
order by syscolumns.colid

select @FieldList = isnull(@FieldList + ',' +char(10) + ' ', '')
+ syscolumns.name
from syscolumns
inner join sysobjects on syscolumns.id = sysobjects.id
inner join systypes on syscolumns.xusertype = systypes.xusertype
where sysobjects.name = @TableName
and systypes.name not in ('text', 'ntext', 'image') --These cannot be referenced in the deleted table
and syscolumns.name not in ('Modified', 'Modifier')
order by syscolumns.colid

select @DeletedList = isnull(@DeletedList + ',' +char(10) + ' ', '')
+ 'deleted.' + syscolumns.name
from syscolumns
inner join sysobjects on syscolumns.id = sysobjects.id
inner join systypes on syscolumns.xusertype = systypes.xusertype
where sysobjects.name = @TableName
and systypes.name not in ('text', 'ntext', 'image') --These cannot be referenced in the deleted table
and syscolumns.name not in ('Modified', 'Modifier')
order by syscolumns.colid

select @KeyList = isnull(@KeyList + char(10) + ' and ', '') + 'deleted.' + columns.name + ' = inserted.' + columns.name
from sys.objects objects
inner join sys.indexes indexes on objects.object_id = indexes.object_id
inner join sys.index_columns index_columns
on indexes.object_id = index_columns.object_id
and indexes.index_id = index_columns.index_id
inner join sys.columns columns
on index_columns.object_id = columns.object_id
and index_columns.column_id = columns.column_id
where is_primary_key = 1
and objects.name = @TableName

set @FirstKey =
(select top 1 columns.name
from sys.objects objects
inner join sys.indexes indexes on objects.object_id = indexes.object_id
inner join sys.index_columns index_columns
on indexes.object_id = index_columns.object_id
and indexes.index_id = index_columns.index_id
inner join sys.columns columns
on index_columns.object_id = columns.object_id
and index_columns.column_id = columns.column_id
where is_primary_key = 1
and objects.name = @TableName
order by columns.column_id)

set @SQLStringTABLECREATE = '--<<[' + @TableName + ']>>--' + char(10)
+ 'Create table [' + @TableName + @ArchiveSuffix + ']' + char(10)
+ ' (' + @FieldDefs + ',' + char(10)
+ ' ' + @DeletedColumnName + ' bit NOT NULL,' + char(10)
+ ' Modified datetime NULL,' + char(10)
+ ' Modifier varchar (50) NULL)' + char(10)
+ 'GO' + char(10)
+ char(10)
+ 'ALTER TABLE [' + @TableName + @ArchiveSuffix + '] ADD ' + char(10)
+ ' CONSTRAINT [DF_' + @TableName + @ArchiveSuffix + '_Deleted] DEFAULT (0) FOR [' + @DeletedColumnName + '],' + char(10)
+ ' CONSTRAINT [DF_' + @TableName + @ArchiveSuffix + '_Modified] DEFAULT (getdate()) FOR [Modified],' + char(10)
+ ' CONSTRAINT [DF_' + @TableName + @ArchiveSuffix + '_Modifier] DEFAULT (suser_sname()) FOR [Modifier]' + char(10)
+ 'GO' + char(10)

set @SQLStringTRIGGERHEADER ='CREATE TRIGGER [TR_' + @TableName + '_Archiver] ON [' + @TableName + ']' + char(10)
+ 'FOR INSERT, UPDATE, DELETE' + char(10)
+ 'AS' + char(10)
+ 'set nocount on' + char(10)
+ 'if exists (select * from dbo.sysobjects where id = object_id(N''[' + @TableName + @ArchiveSuffix + ']'') and OBJECTPROPERTY(id, N''IsUserTable'') = 1)' + char(10)
+ 'begin'

set @SQLStringTRIGGERINSERT = 'insert into [' + @TableName + @ArchiveSuffix + ']' + char(10)
+ ' (' + @FieldList + ',' + char(10)
+ ' ' + @DeletedColumnName + ')' + char(10)
+ 'select ' + @FieldList + ',' + char(10)
+ ' 0' + char(10)
+ 'from inserted' + char(10)
+ 'where TRIGGER_NESTLEVEL() = 1' + char(10)

set @SQLStringTRIGGERDELETE = 'insert into [' + @TableName + @ArchiveSuffix + ']' + char(10)
+ ' (' + @FieldList + ',' + char(10)
+ ' ' + @DeletedColumnName + ')' + char(10)
+ 'select ' + @DeletedList + ',' + char(10)
+ ' 1' + char(10)
+ 'from deleted' + char(10)
+ 'left outer join inserted on ' + @KeyList + char(10)
+ 'where inserted.' + @FirstKey + ' is null' + char(10)
+ 'end' + char(10)
+ 'GO'

if @FirstKey is not null
begin
print @SQLStringTABLECREATE
print @SQLStringTRIGGERHEADER
print @SQLStringTRIGGERINSERT
print @SQLStringTRIGGERDELETE
end
else select '--NO PRIMARY KEY ON TABLE ' + @TableName + '!'

fetch next from TableList into @TableName
end

close tablelist
deallocate tablelist


e4 d5 xd5 Nf6
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-01-04 : 10:41:53
Wow, 3 Million is not many? I thought about performance problems, so thanks for the info. (OK, I know that you might already have performance problems with 10k of data if you organise it as ugly as possible but it's good to know that it's possible without performance problem.) If I encounter some I'll be right back here at the board as the replies are always fast and helpful. Thanks to all of you!!
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-01-04 : 10:45:40
quote:
Originally posted by blindman
Create your primary table and then run the script below, which will generate a script for creating an archive table and a trigger on your main table to process it.

Wow, Blindman, many thanks. That's definitely too much for me to understand but I'll start it and see what happens. But what do you mean with the trigger? Does it automatically fill the archive table with old data? And if so, how does it determine which row could be archived? OK, I got something to read for the weekend
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2008-01-04 : 14:00:24
quote:
But what do you mean with the trigger? Does it automatically fill the archive table with old data? And if so, how does it determine which row could be archived? OK, I got something to read for the weekend

Yes. That is exactly what it does.

e4 d5 xd5 Nf6
Go to Top of Page

Joe Davies
Starting Member

1 Post

Posted - 2008-05-17 : 18:07:37
Hi Blindman, I read through this thread but I don't understand how to implement your solution. I had a solution from Nigel Rivett (who I believe is a member of this forum) it worked great for an audit trail of table changes but when I introduced a binary image field I then got the following error message "Cannot use text, ntext, or image columns in the 'inserted' and 'deleted' tables." Is there a way of having exception fields to escape from? I'm not fussed about keeping an audit trail of changes to the image column.

This is the code I had from Nigel's blog .....

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO

ALTER trigger [dbo].[tr_ASBIData] on [dbo].[ASBIData] for insert, update, delete
as

declare @bit int ,
@field int ,
@maxfield int ,
@char int ,
@fieldname varchar(128) ,
@TableName varchar(128) ,
@PKCols varchar(1000) ,
@sql varchar(2000),
@UpdateDate varchar(21) ,
@UserName varchar(128) ,
@Type char(1) ,
@PKSelect varchar(1000)

select @TableName = 'ASBIData'

-- date and user
select @UserName = system_user ,
@UpdateDate = convert(varchar(8), getdate(), 112) + ' ' + convert(varchar(12), getdate(), 114)

-- Action
if exists (select * from inserted)
if exists (select * from deleted)
select @Type = 'U'
else
select @Type = 'I'
else
select @Type = 'D'

-- get list of columns
select * into #ins from inserted
select * into #del from deleted

-- Get primary key columns for full outer join
select @PKCols = coalesce(@PKCols + ' and', ' on') + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
where pk.TABLE_NAME = @TableName
and CONSTRAINT_TYPE = 'PRIMARY KEY'
and c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

-- Get primary key select for insert
select @PKSelect = coalesce(@PKSelect+'+','') + '''<' + COLUMN_NAME + '=''+convert(varchar(100),coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))+''>'''
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
where pk.TABLE_NAME = @TableName
and CONSTRAINT_TYPE = 'PRIMARY KEY'
and c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

if @PKCols is null
begin
raiserror('no PK on table %s', 16, -1, @TableName)
return
end

select @field = 0, @maxfield = max(ORDINAL_POSITION) from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName
while @field < @maxfield
begin
select @field = min(ORDINAL_POSITION) from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName and ORDINAL_POSITION > @field
select @bit = (@field - 1 )% 8 + 1
select @bit = power(2,@bit - 1)
select @char = ((@field - 1) / 8) + 1
if substring(COLUMNS_UPDATED(),@char, 1) & @bit > 0 or @Type in ('I','D')
begin
select @fieldname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName and ORDINAL_POSITION = @field
select @sql = 'insert Audit (Type, TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName)'
select @sql = @sql + ' select ''' + @Type + ''''
select @sql = @sql + ',''' + @TableName + ''''
select @sql = @sql + ',' + @PKSelect
select @sql = @sql + ',''' + @fieldname + ''''
select @sql = @sql + ',convert(varchar(1000),d.' + @fieldname + ')'
select @sql = @sql + ',convert(varchar(1000),i.' + @fieldname + ')'
select @sql = @sql + ',''' + @UpdateDate + ''''
select @sql = @sql + ',''' + @UserName + ''''
select @sql = @sql + ' from #ins i full outer join #del d'
select @sql = @sql + @PKCols
select @sql = @sql + ' where i.' + @fieldname + ' <> d.' + @fieldname
select @sql = @sql + ' or (i.' + @fieldname + ' is null and d.' + @fieldname + ' is not null)'
select @sql = @sql + ' or (i.' + @fieldname + ' is not null and d.' + @fieldname + ' is null)'
exec (@sql)
end
end

Thanks a lot for your help!


He is no fool who gives what he cannot keep to gain what he cannot lose. (Jim Elliot)
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-07-01 : 18:35:25
Hi all,

currently the script I use works really fine but now I face a problem: The query returns wrong results when a user moves to another division. Unfortunately I don't know how to fix this so I hope to find some help here.

Here is the question:
I have 2 tables, UserStatics and UserFlexibles.
In UserStatics there is one row per Employee. It contains information which will never change, e.g. Birthday, Date of joining the company, etc.
In UserFlexibles there might be 1-n records. So at least everyone has 1 record but he might also have hundreds of records. Here is an example of UserFlexibles.
Employee ID | Business Unit | Record Date | Some value
123.........| Sales.........| 2007/01/20 | ABCDEF
456.........| Sales.........| 2007/02/01 | GHIJKL
789.........| Marketing.. | 2007/02/01 | MNOPQR
123.........| Sales.........| 2007/02/01 | STUVWX
123.........| Sales.........| 2007/05/17 | ABCDEF
789.........| Marketing.. | 2007/07/19 | GHIJKL

Now I need a script which returns a list of all latest entries for a given Business Unit. So e.g. for Business Unit 'Sales' I want to get back these rows:
Employee ID | Business Unit | Record Date | Some value
123.........| Sales.........| 2007/05/17 | ABCDEF
456.........| Sales.........| 2007/02/01 | GHIJKL
(In the script below there is the additional join with the UserStatics as described above but this might not be important for the problem).

In this case the script works fine. But now I face the following problem: User 123 left 'Sales' and is now member of 'Marketing'. So there is an entry like this (while the others are still in the table):
Employee ID | Business Unit | Record Date | Some value
123.........| Marketing.. | 2007/08/20 | ABCDEF

Now the script should only return the row containing user 456. Unfortunately it still returns both rows, 123 (from 2007/05/17) and 456. (This would also fix the problem when a user leaves the company because then I simply create a new record and set the Business Unit to 'none' and so it will not be returned when I call the SP with Business Unit 'Sales').

This is the script I currently use:


PROCEDURE [dbo].[GetCurrentUsers]
@BusinessUnitID INT,
@LatestUpdate DATETIME = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF @LatestUpdate IS NULL
SET @LatestUpdate = GetDate()

SELECT PS.*, latest.*
FROM UserStatics PS
INNER JOIN
(
SELECT PF.*
FROM UserFlexibles PF
INNER JOIN
(
SELECT UserID,BusinessUnitID,MAX(FetchedDate) AS 'FetchedDate' FROM UserFlexibles
WHERE BusinessUnitID = @BusinessUnitID AND FetchedDate < @LatestUpdate
GROUP BY UserID,BusinessUnitID
)t
ON t.UserID=PF.UserID
AND t.BusinessUnitID=PF.BusinessUnitID
AND t.FetchedDate =PF.FetchedDate
)latest
ON latest.UserID = PS.UserID
END


If you need further information don't hesitate to ask. Many thanks in advance!!
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-07-02 : 16:13:41
Playing around with my little SQL knowledge the problem might be in this select:
SELECT UserID,BusinessUnitID,MAX(FetchedDate) AS 'FetchedDate' FROM UserFlexibles
WHERE BusinessUnitID = @BusinessUnitID AND FetchedDate < @LatestUpdate
GROUP BY UserID,BusinessUnitID
When I remove 'BusinessUnitID' from the Select it works fine: I only get the latest record for each userId. Unfortunately I do not have the BusinessUnitID then I could not just add it to the Select without adding it to the GROUP-Section, otherwise I got:
Column 'UserFlexibles.BusinessUnitID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
But when I add it also to the GROUP-Part it will return multiple rows for each userId if it has different BusinessUnitIDs.... Any ideas?
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2008-07-03 : 02:48:31
How about this?
PROCEDURE [dbo].[GetCurrentUsers]
@BusinessUnitID INT,
@LatestUpdate DATETIME = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF @LatestUpdate IS NULL
SET @LatestUpdate = GetDate()

SELECT PS.*, latest.*
FROM UserStatics PS
INNER JOIN
(
SELECT PF.*
FROM UserFlexibles PF
INNER JOIN
(
SELECT UserID,BusinessUnitID,MAX(FetchedDate) AS 'FetchedDate' FROM UserFlexibles
WHERE BusinessUnitID = @BusinessUnitID AND FetchedDate < @LatestUpdate
GROUP BY UserID,BusinessUnitID
)t
ON t.UserID=PF.UserID
AND t.BusinessUnitID=PF.BusinessUnitID
AND t.FetchedDate =PF.FetchedDate
OUTER APPLY (SELECT BusinessUnitID
FROM UserFlexibles
WHERE UserID=PF.UserID
AND FetchedDate>t.FetchedDate
AND BusinessUnitID<>PF.BusinessUnitID)b
WHERE b.BusinessUnitID IS NULL

)latest
ON latest.UserID = PS.UserID
END
Go to Top of Page

Heinz23
Yak Posting Veteran

84 Posts

Posted - 2008-07-03 : 03:48:24
MAGIC VISAKH16!!!!

EXCELLENT! That's working fine. Wow, I haven't heard of 'Outer apply' before but I'll check Books online to see what it's exactly doing. I'm sure I would have never found this solution on my own, so thanks a lot visakh16!!
Go to Top of Page
    Next Page

- Advertisement -