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.
| 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 dateJust 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'-FlagEach 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] |
 |
|
|
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. |
 |
|
|
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 |
 |
|
|
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 value123.........| Sales.........| 2007/01/20 | ABCDEF456.........| Sales.........| 2007/02/01 | GHIJKL789.........| Marketing | 2007/02/01 | MNOPQR123.........| Sales.........| 2007/02/01 | STUVWX123.........| Sales.........| 2007/05/17 | ABCDEF789.........| Marketing | 2007/07/19 | GHIJKLSo 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! |
 |
|
|
visakh16
Very Important crosS Applying yaK Herder
52326 Posts |
Posted - 2007-12-17 : 03:42:59
|
| Create Procedure GetCurrentEmployeeRecords@BusinessUnit varchar(50)ASSELECT e.* FROM Employees eINNER JOIN(SELECT EmployeeID,BusinessUnit,MAX(Record Date) AS 'Record Date' FROM Employees WHERE BusinessUnit=@BusinessUnitGROUP BY EmployeeID,BusinessUnit)tON t.EmployeeID=e.EmployeeIDAND t.BusinessUnit=e.BusinessUnitAND t.Record Date =e.Record DateGO |
 |
|
|
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,ValueFROM EmployeesWHERE BusinessUnit=@BusinessUnit)SELECT * from Emp_CTE WHERE RowNo=1GO |
 |
|
|
kutumbarao
Starting Member
13 Posts |
Posted - 2007-12-17 : 03:54:46
|
| Hi Please try with thisCREATE PROCEDURE dbo.USP_EmployeeBusinessDetails( @BusinessUnit VARCHAR(255))ASBEGIN 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 = @BusinessUnitEND |
 |
|
|
kutumbarao
Starting Member
13 Posts |
Posted - 2007-12-17 : 04:27:20
|
| Hi Please try with thisCREATE PROCEDURE dbo.USP_EmployeeBusinessDetails( @BusinessUnit VARCHAR(255))ASBEGIN 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 = @BusinessUnitEND |
 |
|
|
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! |
 |
|
|
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! |
 |
|
|
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 |
 |
|
|
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 sysobjectswhere type = 'U' and name not like 'dt%' and name not like '%' + @ArchiveSuffix and name not like '%staging%'order by nameselect '*/'*/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 nameopen TableListfetch next from TableList into @TableNamewhile @@fetch_status = 0begin 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 @TableNameendclose tablelistdeallocate tableliste4 d5 xd5 Nf6 |
 |
|
|
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!! |
 |
|
|
Heinz23
Yak Posting Veteran
84 Posts |
Posted - 2008-01-04 : 10:45:40
|
quote: Originally posted by blindmanCreate 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 |
 |
|
|
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 |
 |
|
|
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 ONset QUOTED_IDENTIFIER ONGOALTER trigger [dbo].[tr_ASBIData] on [dbo].[ASBIData] for insert, update, deleteasdeclare @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) |
 |
|
|
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 value123.........| Sales.........| 2007/01/20 | ABCDEF456.........| Sales.........| 2007/02/01 | GHIJKL789.........| Marketing.. | 2007/02/01 | MNOPQR123.........| Sales.........| 2007/02/01 | STUVWX123.........| Sales.........| 2007/05/17 | ABCDEF789.........| Marketing.. | 2007/07/19 | GHIJKLNow 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 value123.........| Sales.........| 2007/05/17 | ABCDEF456.........| 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 value123.........| Marketing.. | 2007/08/20 | ABCDEFNow 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 = NULLASBEGIN -- 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.UserIDEND If you need further information don't hesitate to ask. Many thanks in advance!! |
 |
|
|
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? |
 |
|
|
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 = NULLASBEGIN -- 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.UserIDEND |
 |
|
|
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!! |
 |
|
|
Next Page
|
|
|
|
|