Assistance with this query

  • I'm a bit new to SQL, and I'm attempting my first real project. Currently, my setup is: SQL Server 2008 R2 with a linked SQL Server 2000.

    A couple points that I'm having issues comprehending:

  • Each record in the "employee" table has it's own ACTIONDATE, which allows more than one record for the same person, so getting one result for one person is appearing difficult. EMPLOYEEIDs are unique to the user, so how would I word the query to grab the most recent ACTIONDATE for each employeeid?
  • Multiple joins are causing my queries to run for over 3 minutes. Each table has 100k records, minimum. How would I optimize a query with multiple joins with big tables? Or is there a better way to grab this data?
  • I'm attempting to make a hierarchy from a SQL Server that houses are workforce software and place it into my SQL Server 2008 R2 to be utilized in another application. I want to grab all active employees (ENDDATE IS NULL) with the most recent ACTIONDATE of each active EMPLOYEEID.

    The query I'm attempting to run is:

    SELECT DISTINCT employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE as employee

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON employee.employeeid = org.employeeid

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON employee.employeeid = job.employeeid

    WHERE employee.ENDDATE IS NULL

  • Try something like this

    SELECT employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    FROM dbo.AUEMPLOYEEJOBTITLE as job

    CROSS APPLY

    (SELECT TOP 1 emp.employeeid, emp.FIRSTNAME, emp.LASTNAME, emp.STARTDATE, emp.EMAILADDRESS, emp.ENDDATE

    FROM dbo.AUEMPLOYEE emp

    INNER JOIN dbo.AUEMPLOYEEJOBTITLE empjob

    ON emp.EMPLOYEEID = empjob.employeeid

    ORDER BY ACTIONDATE) employee

    RIGHT JOIN dbo.AUEMPLOYEEORGANIZATION as org

    ON employee.employeeid = org.employeeid

    WHERE employee.ENDDATE IS NULL

  • Also check that you're using an account that is db_owner on the linked server db. There's a known issue when querying across linked servers and statistics permissions. Post the execution plan if you need it to be faster.

  • Try this query it will work

    SELECT temp.EMPLOYEEID, temp.FIRSTNAME, temp.LASTNAME, temp.STARTDATE, temp.EMAILADDRESS, temp.ORGANIZATIONID, temp.JOBTITLEID

    (

    SELECT row_number() OVER (PARTITION BY employee.EMPLOYEEID ORDER BY ACTIONDATE) as 'RowNumber',

    employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE as employee

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON employee.employeeid = org.employeeid

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON employee.employeeid = job.employeeid

    WHERE employee.ENDDATE IS NULL

    GROUP BY employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID,ACTIONDATE

    ) temp

    WHERE RowNumber<=1

    ORDER BY temp.EMPLOYEEID

    Regards,

    Arjun

  • foxxo (3/25/2013)


    Try something like this

    SELECT employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    FROM dbo.AUEMPLOYEEJOBTITLE as job

    CROSS APPLY

    (SELECT TOP 1 emp.employeeid, emp.FIRSTNAME, emp.LASTNAME, emp.STARTDATE, emp.EMAILADDRESS, emp.ENDDATE

    FROM dbo.AUEMPLOYEE emp

    INNER JOIN dbo.AUEMPLOYEEJOBTITLE empjob

    ON emp.EMPLOYEEID = empjob.employeeid

    ORDER BY ACTIONDATE) employee

    RIGHT JOIN dbo.AUEMPLOYEEORGANIZATION as org

    ON employee.employeeid = org.employeeid

    WHERE employee.ENDDATE IS NULL

    Thank you Foxxo! That actually ran really quick too. I have one more question, if you don't mind. Since there are multiple records with the same employeeid (basically changes made are transactional which causes multiple, I would like to only include the latest actiondate of each employeeid in the table. Is this possible? This is to ensure that I do not have duplicate records for employees, I'm probably going to set this up to run as a task to run nightly to update the hierarchy.

    The permissions are correct for the linked connections. 🙂

    @writearjun53 - I'm getting an incorrect syntax error from that code after the second SELECT

    Msg 156, Level 15, State 1, Line 3

    Incorrect syntax near the keyword 'SELECT'.

    Msg 102, Level 15, State 1, Line 12

    Incorrect syntax near ')'.

    Thank you!

  • please try now

    SELECT temp.EMPLOYEEID, temp.FIRSTNAME, temp.LASTNAME, temp.STARTDATE, temp.EMAILADDRESS, temp.ORGANIZATIONID, temp.JOBTITLEID From

    (

    SELECT row_number() OVER (PARTITION BY employee.EMPLOYEEID ORDER BY ACTIONDATE) as 'RowNumber',

    employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE as employee

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON employee.employeeid = org.employeeid

    RIGHT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON employee.employeeid = job.employeeid

    WHERE employee.ENDDATE IS NULL

    GROUP BY employee.EMPLOYEEID, employee.FIRSTNAME, employee.LASTNAME, employee.STARTDATE, employee.EMAILADDRESS, org.ORGANIZATIONID, job.JOBTITLEID

    ) temp

    WHERE RowNumber<=1

    ORDER BY temp.EMPLOYEEID

  • Hey,

    Thanks for the reply.

    I'm receiving the follow error:

    Msg 8120, Level 16, State 1, Line 3

    Column 'linkedServer.linkedDB.dbo.AUEMPLOYEE.ACTIONDATE' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

    Msg 8120, Level 16, State 1, Line 3

    Column 'linkedServer.linkedDB.dbo.AUEMPLOYEE.ACTIONDATE' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

    I apologize, it doesn't appear I mentioned it, but ACTIONDATE is actually used in all tables by the software as a transaction record to show when the change was made.

  • ;WITH Employees AS (

    SELECT TOP 1

    employeeid,

    FIRSTNAME,

    LASTNAME,

    EMAILADDRESS,

    STARTDATE

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE

    WHERE employee.ENDDATE IS NULL -- still active

    ORDER BY ACTIONDATE DESC -- most recent

    )

    SELECT

    e.*,

    org.ORGANIZATIONID,

    job.JOBTITLEID

    FROM Employees e

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON e.EMPLOYEEID = job.employeeid

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON e.employeeid = org.employeeid

    Check the actual execution plan. SQL Server does a surprisingly good job of figuring out when a query can be run remotely in its entirety but it doesn't always work. When it doesn't, you can often force it using OPENQUERY.

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • ChrisM@Work (3/26/2013)


    ;WITH Employees AS (

    SELECT TOP 1

    employeeid,

    FIRSTNAME,

    LASTNAME,

    EMAILADDRESS,

    STARTDATE

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE

    WHERE employee.ENDDATE IS NULL -- still active

    ORDER BY ACTIONDATE DESC -- most recent

    )

    SELECT

    e.*,

    org.ORGANIZATIONID,

    job.JOBTITLEID

    FROM Employees e

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON e.EMPLOYEEID = job.employeeid

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON e.employeeid = org.employeeid

    Check the actual execution plan. SQL Server does a surprisingly good job of figuring out when a query can be run remotely in its entirety but it doesn't always work. When it doesn't, you can often force it using OPENQUERY.

    Thank you Chris! So close! This is only pulling the records for one employeeid.

  • D'Oh!

    ;WITH Employees AS (

    SELECT rn = ROW_NUMBER() OVER(PARTITION BY employeeid ORDER BY ACTIONDATE DESC),

    employeeid,

    FIRSTNAME,

    LASTNAME,

    EMAILADDRESS,

    STARTDATE

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE

    WHERE employee.ENDDATE IS NULL -- still active

    )

    SELECT

    e.*,

    org.ORGANIZATIONID,

    job.JOBTITLEID

    FROM Employees e

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON e.EMPLOYEEID = job.employeeid

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON e.employeeid = org.employeeid

    WHERE e.rn = 1

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • This is really odd, Chris. When I run that code, it runs quick and easy enough, however for some reason, it is duplicating ACTIONORDERS*

    *NOTE - I changed ACTIONDATE to ACTIONORDER as I had appear to have missed this column as the key for ACTIONDATES, as ACTIONORDERS are unique.

    ;WITH Employees AS (

    SELECT rn = ROW_NUMBER() OVER(PARTITION BY employeeid ORDER BY ACTIONORDER DESC),

    employeeid,

    FIRSTNAME,

    LASTNAME,

    EMAILADDRESS,

    STARTDATE,

    ACTIONORDER

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE as employee

    WHERE employee.ENDDATE IS NULL -- still active

    )

    SELECT

    e.*,

    org.ORGANIZATIONID,

    job.JOBTITLEID

    FROM Employees e

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEJOBTITLE as job

    ON e.EMPLOYEEID = job.employeeid

    LEFT JOIN linkedServer.linkedDB.dbo.AUEMPLOYEEORGANIZATION as org

    ON e.employeeid = org.employeeid

    WHERE e.rn = 1

    Results in:

    rnemployeeidFIRSTNAMELASTNAMEEMAILADDRESSSTARTDATEACTIONORDERORGANIZATIONIDJOBTITLEID

    11TestTester1NULL8/15/2007 5:00270-3002NULL

    11TestTester1NULL8/15/2007 5:00270-3002NULL

    12TestTester2NULL7/11/2006 5:0063307-3002NULL

    12TestTester2NULL7/11/2006 5:0063307-3002NULL

    13TestTester3NULL4/11/2006 5:0086963-3002NULL

    13TestTester3NULL4/11/2006 5:0086963-3002NULL

    14TestTester4NULL12/7/2005 6:0014380-3002NULL

    14TestTester4NULL12/7/2005 6:0014380-3002NULL

    Clearly picture of the results. http://i.imgur.com/z8tXFvJ.png

    And so... Any ideas?

  • Run this, post the first ten or so rows returned:

    SELECT rn = ROW_NUMBER() OVER(PARTITION BY employeeid ORDER BY ACTIONORDER DESC),

    employeeid,

    FIRSTNAME,

    LASTNAME,

    EMAILADDRESS,

    STARTDATE,

    ACTIONORDER

    FROM linkedServer.linkedDB.dbo.AUEMPLOYEE as employee

    WHERE employee.ENDDATE IS NULL -- still active

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • http://i.imgur.com/sctRECw.png

  • tmac25 (3/26/2013)


    This is really odd, Chris. ...

    And so... Any ideas?

    Yes - one or both of the other two tables has multiple rows per employee. Can you check both tables to find out?

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Hello Chris!

    I apologize, I attempted to point this out in the original post, I see where I could have been more clear.

    The software that is being utilized all has ACTIONDATES/ACTIONORDER in all the tables I'm using that signify a change to a user. Essentially, and this is where I knew it was going to get really complicated, which is why I posted, I'm sure there has got to be a easier way to do this, I find the most recent (ACTIONORDER) active (NOT NULL) employeeid from auemployee.

    I would then cross reference that to AUJOB and AUORG to get the most recent change (ACTIONORDER) in both of those tables for that employeeid and join them into a hierarchy table.

    Thank you!

  • Viewing 15 posts - 1 through 15 (of 19 total)

    You must be logged in to reply to this topic. Login to reply