| Author |
Topic |
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 18:46:36
|
| I have a "Log" table that records when "Employees" are assigned/removed from "Clients", The table has the following structure:Employee_ID (nchar)Client_ID (nchar)AssignedRemoved (bit),LogDate (datetime)The AssignedRemoved field is a bit that specifies if the entry is for a client Assignment (1) or Removal (0). I need to get a resultset that contains all of the Client_IDs for a given User_ID where the Client is still assigned AS OF a certain date.Here is some smaple dataUserA, ClientA, 1, 1/6/07UserB, ClientA, 1, 1/8/07UserC, ClientB, 1, 2/5/07UserA, ClientA, 0, 1/12/07UserA, ClientD, 1, 1/7/07UserC, ClientA, 1, 1/14/07So, for example, if I wanted a list of all clients assigned to UserA as of 1/8/07... the result would be "ClientA, ClientD". If I change the date input to 1/13/07, the result for UserA would be just "ClientD".So, I'm thinking I will need to do some sort of self join here, but I am having a hard time figuring it out. Can anyone help? Thanks in advance. |
|
|
dinakar
Master Smack Fu Yak Hacker
2507 Posts |
Posted - 2007-04-23 : 18:50:37
|
| SELECT <columns>FROM Logwhere employee_id = @userand AssignedRemoved = 1and employee_id <> Client_Idwill give you both ClientA and ClientD. Now I dont understand what is the logic for you to choose ClientD over ClientA when date is 1/13/07.************************Life is short. Enjoy it.************************http://weblogs.sqlteam.com/dinakar/ |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 19:48:43
|
Thanks for the reply, however I'm not sure where the employee_id <> Client_Id fits in, given that they will never be equal. Perhaps I dind't make myself clear. I need to pass an employee (userID) and a date. I am trying to find out which clients are assigned to the given employee as of the given date.quote: Originally posted by dinakar SELECT <columns>FROM Logwhere employee_id = @userand AssignedRemoved = 1and employee_id <> Client_Idwill give you both ClientA and ClientD. Now I dont understand what is the logic for you to choose ClientD over ClientA when date is 1/13/07.************************Life is short. Enjoy it.************************http://weblogs.sqlteam.com/dinakar/
|
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2007-04-23 : 21:18:27
|
[code]select *from Logwhere AssignedRemoved = 1 and Employee_ID = @UserIDand LogDate <= @Date[/code] KH |
 |
|
|
Vinnie881
Master Smack Fu Yak Hacker
1231 Posts |
Posted - 2007-04-23 : 21:23:46
|
| Declare @InputDate datetimeDeclare @EmployeeID varchar(20)Select @InputDate = '04/08/2007', @EmployeeID = 'UserA'SELECT Client_IDFROM Table1 bwhere employee_id = @Employee_IDand AssignedRemoved = 1and b.LogDate = ( select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and a.AssignedRemoved = 1 and ( ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0 ) is null or ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0) < (select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 1 ) ) ) |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 21:27:46
|
Thanks for the reply, khtan. Unfortunately, its not quite so simple which is why I think I need some sort of self-join.With your code, I will get a list of all clients which were assigned to the employee on or before the specified date. However, some of these were subsequently "unassigned". In my example, if I pass " UserA" and the date 1/13/07 to your codd, I will get two records, when I should only get one.quote: Originally posted by khtan
select *from Logwhere AssignedRemoved = 1 and Employee_ID = @UserIDand LogDate <= @Date KH
|
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2007-04-23 : 21:33:08
|
I *think* this will do the trick, simple and quick but kind of unusual:select client_IDfrom Logwhere user_ID = @UserID and logDate <= @EndDategroup by client_IDhaving max(case when AssignedRemoved = 1 then logDate else '1/1/1900' end) > max(case when AssignedRemoved = 0 then logDate else '1/1/1900' end) - Jeffhttp://weblogs.sqlteam.com/JeffS |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2007-04-23 : 21:34:34
|
try thisselect *from Log lwhere AssignedRemoved = 1 and Employee_ID = @UserIDand LogDate <= @Dateand not exists ( select * from Log x where x.Employee_ID = l.Employee_ID and x.Client_ID = l.Client_ID and x.AssignedRemoved = 0 and x.LogDate <= @Date ) KH |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2007-04-23 : 21:40:51
|
| mine might look funny but it will work, and it should be quite efficient (no self join needed).- Jeffhttp://weblogs.sqlteam.com/JeffS |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2007-04-23 : 21:42:42
|
quote: Originally posted by jsmith8858 mine might look funny but it will work, and it should be quite efficient (no self join needed).- Jeffhttp://weblogs.sqlteam.com/JeffS
Yes. Your codes works well. Mine does not cater for situation where the client is assign again to the Employee. KH |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 21:51:29
|
Wow, that's a doozy. I couldn't get it to compile. I get the following error: "An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference."Also, and I am no expert in this, but this would seem to be a really poorly perfomring query, with so many sub-queries. Is there no way to accomplish the same thing with self-joins?quote: Originally posted by Vinnie881 Declare @InputDate datetimeDeclare @EmployeeID varchar(20)Select @InputDate = '04/08/2007', @EmployeeID = 'UserA'SELECT Client_IDFROM Table1 bwhere employee_id = @Employee_IDand AssignedRemoved = 1and b.LogDate = ( select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and a.AssignedRemoved = 1 and ( ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0 ) is null or ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0) < (select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 1 ) ) )
|
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 21:59:28
|
Thanks for your help, jsmith and kthan! JSmith's does seem to be the best as it take into account the situation where a client is reassigned to the same employee at a later time. One question, though. What does the "else '1/1/1900' end" part do. I assume that this references some system min date. Is there a way to accomplish the same thing without referenceing this date in a hard-coded fashion?quote: Originally posted by jsmith8858 I *think* this will do the trick, simple and quick but kind of unusual:select client_IDfrom Logwhere user_ID = @UserID and logDate <= @EndDategroup by client_IDhaving max(case when AssignedRemoved = 1 then logDate else '1/1/1900' end) > max(case when AssignedRemoved = 0 then logDate else '1/1/1900' end) - Jeffhttp://weblogs.sqlteam.com/JeffS
|
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2007-04-23 : 22:07:58
|
| I like the way you are thinking (i.e., removing hard-coded dates), but in this case it is just there to ensure that the logic works and has no real effect other than to avoid dealing with NULLS. As long as you have no dates at or before 1/1/1900 in your data, you will be fine. It's essentially like using a default of 0 when doing some integer assignments or arithmetic.- Jeffhttp://weblogs.sqlteam.com/JeffS |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 22:09:49
|
| If I am assured to never have a null date, is this something I can remove, or is it better to keep just in case? |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2007-04-23 : 22:36:24
|
| No, it's not for null dates, it's for when there is no assigned or removed row in the data for a particular client within the date range.- Jeffhttp://weblogs.sqlteam.com/JeffS |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-23 : 22:44:58
|
| Ahh... I see. Thanks for all your help. |
 |
|
|
Vinnie881
Master Smack Fu Yak Hacker
1231 Posts |
Posted - 2007-04-24 : 00:52:41
|
I had the parenthesise wrong.The reason this code is has so many subqueries is due to you nee to retrieve the last date the client was applied taking into consideration if they were ever unapplied. This query should give you exacltly what you need factoring in all scenerios Like : A client was applied, then later unapplied on a specific date.The other code I have seen does not factor this in.Declare @InputDate datetimeDeclare @EmployeeID varchar(20)Select @InputDate = '04/08/2007', @EmployeeID = 'UserA'SELECT Client_IDFROM Table1 bwhere employee_id = @EmployeeIDand AssignedRemoved = 1and b.LogDate = ( select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and a.AssignedRemoved = 1 and ( ( ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0 ) is null) or ( select max(a.logdate) as logdate from table1 c where c.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 0) < (select max(a.logdate) as logdate from table1 a where a.logDate <= @InputDate and a.Employee_ID = @EmployeeID and a.Client_ID = b.ClientID and AssignedRemoved = 1 ) ) ) ) |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-24 : 01:06:57
|
| Thanks for the reply, vinnie. I see that now. However, I have been playing around with the code provided by jsmith and I think this handles all situations... such as a client being assigned, unassigned and later assigned again. Thanks again for your help, though. |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2007-04-24 : 01:28:32
|
Since this is a SQL Server 2005 forum, try this approach!-- Prepare sample dataDECLARE @Sample TABLE ([User] VARCHAR(10), Client VARCHAR(10), Assigned BIT, dt DATETIME)INSERT @SampleSELECT 'UserA', 'ClientA', 1, '1/6/07' UNION ALLSELECT 'UserB', 'ClientA', 1, '1/8/07' UNION ALLSELECT 'UserC', 'ClientB', 1, '2/5/07' UNION ALLSELECT 'UserA', 'ClientA', 0, '1/12/07' UNION ALLSELECT 'UserA', 'ClientD', 1, '1/7/07' UNION ALLSELECT 'UserC', 'ClientA', 1, '1/14/07'-- Initialize a common table expression;WITH cteLog ([User], Client)AS ( SELECT y.[User], y.Client FROM ( SELECT [User], Client, Assigned, dt, ROW_NUMBER() OVER (PARTITION BY [User], Client ORDER BY dt DESC, Assigned) AS RecID FROM @Sample WHERE dt <= '1/8/07' ) AS y WHERE y.Assigned = 1 AND y.RecID = 1) -- change assigned [asc] to assigned desc if order of assigned is important-- Show the expected outputSELECT DISTINCT c1.[User], STUFF((SELECT DISTINCT TOP 100 PERCENT ', ' + c2.Client FROM cteLog AS c2 WHERE c2.[User] = c1.[User] ORDER BY ', ' + c2.Client FOR XML PATH('')), 1, 2, '') AS ClientsFROM cteLog AS c1ORDER BY c1.[User] This will also handle cases where a user is assigned and removed from a client the very same day.But still the sample data can't distinguish from the case where a user has been assigned to a client, and then on the same day is removed and later same day is assigned again. My code will only handle cases where a user is assigned a client and later same day is removed from client.Peter LarssonHelsingborg, Sweden |
 |
|
|
akashenk
Posting Yak Master
111 Posts |
Posted - 2007-04-24 : 01:42:41
|
| Thanks, Peso. That appears to work as well. I believe you also need to add a "and y.[user]=@userId" line at the end so that the Employee can be passed as an input parameter.What is the advantage of this approach over JSmith's?...select client_IDfrom Logwhere user_ID = @UserID and logDate <= @EndDategroup by client_IDhaving max(case when AssignedRemoved = 1 then logDate else '1/1/1900' end) > max(case when AssignedRemoved = 0 then logDate else '1/1/1900' end) |
 |
|
|
Next Page
|