Resource Assignments in Microsoft Project
Now that we are masters of the data, let’s see who needs to do what on the project. We’ll start by replicating Microsoft Project’s view:
![]()
We’ll need tables MSP_PROJECTS, MSP_RESOURCES, MSP_TASKS and MSP_ASSIGNMENTS to start:
- Add before mentioned tables to the query.
- MS Query will automatically interconnect (join) tables. And it will overdo it. Remove the extra connections by double clicking each of the redundant joins then clicking Remove in the Join dialog box. You only need to keep
MSP_ASSIGNMENT.PROJ_ID = MSP_PROJECTS.PROJ_ID,MSP_ASSIGNMENT.RES_UID = MSP_RESOURCES.RES_UIDandMSP_ASSIGNMENTS.TASK_UID = MSP_TASKS.TASK_UID. - Add
MSP_PROJECTS.PROJ_NAMEto criteria list and type the name of your project into its value. - Add
MSP_RESOURCES.RES_UID>0andMSP_TASKS_TASK_UID>0to criteria list. - Add fields
MSP_RESOURCES.RES_NAME,MSP_TASKS.TASK_NAMEandMSP_ASSIGNMENTS.ASSN_WORKto the query output. - Add sorting keys (Records then Sort)
MSP_RESOURCES.RES_NAMEandMSP_TASKS.TASK_NAME.
For the final result we’re still short of Resource Group data and this is where things get interesting, since you won’t find a corresponding field in MSP_RESOURCES table. This is because Microsoft Project keeps some of its fields in custom fields tables (MSP_*_FIELDS). In our case, Resource group is stored in MSP_TEXT_FIELDS as it is a text field.
Custom fields from MSP_*_FIELDS are related to corresponding records through *_REF_UID.
In order to get Resource Group out on the plain, we need to:
- Add
MSP_TEXT_FIELDSto the query. - Remove all joins connected to
MSP_TEXT_FIELDS. - Join
MSP_RESOURCES.RES_UIDtoMSP_TEXT_FIELDS.TEXT_REF_UIDby dragging the first field and dropping it to the second. - Add
MSP_TEXT_FIELDS.TEXT_VALUEto query output and drag it the first position on the left and rename it toResource Group.
We’re almost there. Resources can have many custom values and in such situations, we would get all values in a display. So we want to limit the output to Resource Group only. Table MSP_CONVERSIONS holds the key to the answer:
- Add
MSP_CONVERSIONSto the query. - Connect
MSP_CONVERSIONS.CONV_VALUEtoMSP_TEXT_FIELDS.TEXT_FIELD_ID. - Add criteria
MSP_CONVERSIONS.CONV_STRING='Resource Group'.
![]()
This is how final SQL statement looks like:
SELECT
TF.TEXT_VALUE AS 'Resource Group',
R.RES_NAME AS 'Resource Name',
T.TASK_NAME AS 'Task Name',
A.ASSN_WORK/P.PROJ_OPT_MINUTES_PER_DAY/1000 AS 'Work'
FROM
MSP_ASSIGNMENTS A,
MSP_CONVERSIONS C,
MSP_PROJECTS P,
MSP_RESOURCES R,
MSP_TASKS T,
MSP_TEXT_FIELDS TF
WHERE
A.PROJ_ID = P.PROJ_ID AND
A.RES_UID = R.RES_UID AND
T.TASK_UID = A.TASK_UID AND
R.RES_UID = TF.TEXT_REF_UID AND
C.CONV_VALUE = TF.TEXT_FIELD_ID AND
P.PROJ_NAME='SOFTDEV' AND
A.RES_UID>0 AND
A.TASK_UID>0 AND
C.CONV_STRING='Resource Group'
ORDER BY
TF.TEXT_VALUE,
R.RES_NAME,
T.TASK_NAME
Now you can create a pivot table out of the data:
- Add Resource Group, Resource Name and Task Name to Row Area.
- Add Work to Data Area.
And here’s how the result should look like:
![]()
There’s still a minor difference to the view in the Microsoft Project. If you look at the first picture at the top, you’ll see that it displays Unassigned tasks as well not grouped resources. Without going into details, SQL statement grows into this monstrosity:
SELECT
'No Group' ,
MSP_RESOURCES.RES_NAME ,
MSP_TASKS.TASK_NAME ,
MSP_ASSIGNMENTS.ASSN_WORK/MSP_PROJECTS.PROJ_OPT_MINUTES_PER_DAY/1000
FROM
MSP_ASSIGNMENTS MSP_ASSIGNMENTS,
MSP_CONVERSIONS MSP_CONVERSIONS,
MSP_PROJECTS MSP_PROJECTS,
MSP_RESOURCES MSP_RESOURCES,
MSP_TASKS MSP_TASKS,
MSP_TEXT_FIELDS MSP_TEXT_FIELDS
WHERE
MSP_ASSIGNMENTS.PROJ_ID = MSP_PROJECTS.PROJ_ID AND
MSP_ASSIGNMENTS.RES_UID = MSP_RESOURCES.RES_UID AND
MSP_TASKS.TASK_UID = MSP_ASSIGNMENTS.TASK_UID AND
MSP_RESOURCES.RES_UID = MSP_TEXT_FIELDS.TEXT_REF_UID AND
MSP_CONVERSIONS.CONV_VALUE = MSP_TEXT_FIELDS.TEXT_FIELD_ID AND
MSP_PROJECTS.PROJ_NAME='SOFTDEV' AND
MSP_ASSIGNMENTS.RES_UID>0 AND
MSP_ASSIGNMENTS.TASK_UID>0 AND
MSP_CONVERSIONS.CONV_STRING='Resource Group' AND
MSP_TEXT_FIELDS.TEXT_VALUE Is Null
UNION(
SELECT
MSP_TEXT_FIELDS.TEXT_VALUE ,
MSP_RESOURCES.RES_NAME ,
MSP_TASKS.TASK_NAME ,
MSP_ASSIGNMENTS.ASSN_WORK/MSP_PROJECTS.PROJ_OPT_MINUTES_PER_DAY/1000
FROM
MSP_ASSIGNMENTS MSP_ASSIGNMENTS,
MSP_CONVERSIONS MSP_CONVERSIONS,
MSP_PROJECTS MSP_PROJECTS,
MSP_RESOURCES MSP_RESOURCES,
MSP_TASKS MSP_TASKS,
MSP_TEXT_FIELDS MSP_TEXT_FIELDS
WHERE
MSP_ASSIGNMENTS.PROJ_ID = MSP_PROJECTS.PROJ_ID AND
MSP_ASSIGNMENTS.RES_UID = MSP_RESOURCES.RES_UID AND
MSP_TASKS.TASK_UID = MSP_ASSIGNMENTS.TASK_UID AND
MSP_RESOURCES.RES_UID = MSP_TEXT_FIELDS.TEXT_REF_UID AND
MSP_CONVERSIONS.CONV_VALUE = MSP_TEXT_FIELDS.TEXT_FIELD_ID AND
MSP_PROJECTS.PROJ_NAME='SOFTDEV' AND
MSP_ASSIGNMENTS.RES_UID>0 AND
MSP_ASSIGNMENTS.TASK_UID>0 AND
MSP_CONVERSIONS.CONV_STRING='Resource Group' AND
MSP_TEXT_FIELDS.TEXT_VALUE Is Not Null
)
UNION(
SELECT
'No Group' ,
MSP_RESOURCES.RES_NAME ,
MSP_TASKS.TASK_NAME ,
MSP_ASSIGNMENTS.ASSN_WORK/MSP_PROJECTS.PROJ_OPT_MINUTES_PER_DAY/1000
FROM
MSP_PROJECTS MSP_PROJECTS,
MSP_TASKS MSP_TASKS,
MSP_ASSIGNMENTS MSP_ASSIGNMENTS,
MSP_RESOURCES MSP_RESOURCES
WHERE
MSP_PROJECTS.PROJ_NAME = 'SOFTDEV' AND
MSP_PROJECTS.PROJ_ID = MSP_ASSIGNMENTS.PROJ_ID AND
MSP_TASKS.TASK_UID = MSP_ASSIGNMENTS.TASK_UID AND
MSP_RESOURCES.RES_UID = MSP_ASSIGNMENTS.RES_UID AND
MSP_RESOURCES.RES_UID >0 AND
MSP_RESOURCES.RES_UID NOT IN (
SELECT
MSP_TEXT_FIELDS.TEXT_REF_UID
FROM
MSP_CONVERSIONS MSP_CONVERSIONS,
MSP_PROJECTS MSP_PROJECTS,
MSP_TEXT_FIELDS MSP_TEXT_FIELDS
WHERE
MSP_TEXT_FIELDS.TEXT_FIELD_ID = MSP_CONVERSIONS.CONV_VALUE AND
MSP_PROJECTS.PROJ_ID = MSP_TEXT_FIELDS.PROJ_ID AND
MSP_PROJECTS.PROJ_NAME = 'SOFTDEV' AND
MSP_CONVERSIONS.CONV_STRING='Resource Group')
)
UNION(
SELECT
'No Group' ,
'Unassigned' ,
MSP_TASKS.TASK_NAME ,
MSP_ASSIGNMENTS.ASSN_WORK/MSP_PROJECTS.PROJ_OPT_MINUTES_PER_DAY/1000
FROM
MSP_ASSIGNMENTS MSP_ASSIGNMENTS,
MSP_PROJECTS MSP_PROJECTS,
MSP_TASKS MSP_TASKS
WHERE
MSP_TASKS.TASK_UID = MSP_ASSIGNMENTS.TASK_UID AND
MSP_PROJECTS.PROJ_ID = MSP_ASSIGNMENTS.PROJ_ID AND
MSP_PROJECTS.PROJ_NAME='SOFTDEV' AND
MSP_ASSIGNMENTS.RES_UID<0
)
ORDER BY 1, 2, 3
Query can’t handle column names if UNION statement is used in SQL SELECT, so you’ll have to rename columns once you’re back in Microsoft Excel.
August 12th, 2008 at 7:59 am
[...] know you didn’t bother with monster SQL SELECT and instead waited for me to come up with something chewable. And here it is. We reached the limits [...]