• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 104
  • Last Modified:

Help with Search Query in SQL Server 2008R2 Version

I have a database table that holds records. The table column are company id, department id, section id ,document title and another table that hold the access roles to these records based employee assigned roles.

For eg. DOCUMENT_MANAGEMENT TABLE>>>

ID, COMPANY_ID,DEPARTMENT_ID,SECTION_ID,DOCUMENT_TITLE,DOCUMENT_DESCRIPTION
1,1,1,1,'Procedure for HR Hiring','some data'
2,1,1,2,'Resumes Submissions','some data'
3,1,2,1,'Study of Engineering Designs','some data'
4,1,2,2,'Engineering Architecture','some data'

etc.

The table rows need to be displayed based on a specific role.

And hence the query results needs to be tied to Employee Roles.

EMPLOYEE_ROLE_TABLE>>
ID,EMPLOYEE_ID,DEPARTMENT_ID,SECTION_ID
1,1,1,1
2,1,2,-1
3,2,1,-1
4,2,2,-1

If you noticed the above table, the employee with EMPLOYEE_ID (1), should have access to the Department ID (1 & 2) but the Section ID is limited to section 1 for department 1 and all section ID (denoted by -1 value) for department 2.

In short, the Employee 1 can access all the Sections of department 2 but is restricted to only section 1 of department 1.

Likewise, the Employee 2 can access all the Sections of department 1 & 2.

My idea was to restrict the access by the following query and ensuring that the user role table is linked to the primary query.

SELECT DOCUMENT_TITLE FROM DOCUMENT_MANAGEMENT
WHERE
(DEPARTMENT_ID=-1  OR -1=-1 ) AND
(SECTION_ID=-1   OR -1=-1   ) AND
(UPPER(DOCUMENT_TITLE ) Like '%' +@SEARCH_STR + '%' OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' ) AND
(DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM EMPLOYEE_ROLE_TABLE WHERE USER_ID=1) OR DEPARTMENT_ID=-1) AND
          (SECTION_ID IN (SELECT DISTINCT SUB_DEPARTMENT_ID FROM EMPLOYEE_ROLE_TABLE WHERE USER_ID=1) OR SECTION_ID=-1)  

The issue I am having is for The record #2, is not returned by this query for employee id 1 who has the permission to access all the sections. As the value of in the employee role table is -1 (which indicates that whatever value is stored in the section_Id column of table DOCUMENT_MANAGEMENT should be visible to this employee.

What is wrong with the query I had written.
0
Member_2_7967119
Asked:
Member_2_7967119
  • 11
  • 9
  • 6
2 Solutions
 
Member_2_7967119Author Commented:
The query should read as

SELECT DOCUMENT_TITLE FROM DOCUMENT_MANAGEMENT
WHERE
(DEPARTMENT_ID=-1  OR -1=-1 ) AND
(SECTION_ID=-1   OR -1=-1   ) AND
(UPPER(DOCUMENT_TITLE ) Like '%' +@SEARCH_STR + '%' OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' ) AND
(DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM EMPLOYEE_ROLE_TABLE WHERE USER_ID=1) OR DEPARTMENT_ID=-1) AND
          (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM EMPLOYEE_ROLE_TABLE WHERE USER_ID=1) OR SECTION_ID=-1)
0
 
Pawan KumarDatabase ExpertCommented:
Hi,

could you provide some data and the expected output.

Regards,
Pawan
0
 
Member_2_7967119Author Commented:
No, If I provide as stated OR DEPARTMENT_ID=-1
Won't it retrieve all records that match DEPARTMENT_ID=-1 and not use the Employee Role Critiria?
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
Pawan KumarDatabase ExpertCommented:
So if DEPARTMENT_ID=-1, then you dont want Employee Role Criteria filter. ?
0
 
Pawan KumarDatabase ExpertCommented:
i think issue is some where else. select all the columns and then campare the row data with the query.
0
 
Member_2_7967119Author Commented:
Pawan,

Attached the tables scripts and also the stored procedure. I also provided the insert scripts.

If the search string passed to the stored procedure is 'Engineering' then the record 2, 4 should show up in the resultant output.

I also provide the execution parameters I use.
SCRIPTS_DMS.txt
0
 
Pawan KumarDatabase ExpertCommented:
Hi,

Comment this line - --AND (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR SECTION_ID = -1 )

Run this code and see the output

--


DECLARE @SEARCH_STR AS VARCHAR(1000) = 'Engineering'
SELECT DEPARTMENT_ID,SECTION_ID,DOCUMENT_TITLE  FROm DOCUMENT_MANAGEMENT
WHERE 1=1
AND (UPPER(DOCUMENT_TITLE ) Like '%' +@SEARCH_STR + '%' OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' )
AND (DEPARTMENT_ID=-1  OR -1=-1 )
AND (SECTION_ID=-1   OR -1=-1   ) 
AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1)
--AND (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR SECTION_ID = -1 )

--

Open in new window


O/p
------------

Run the below code now --

DEPARTMENT_ID      SECTION_ID      DOCUMENT_TITLE
2                               2      Engineering Resumes Submissions
1                             10      Study of Engineering Designs
2                             1      Engineering Architecture

--
--

SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1

--

Open in new window


O/p
-----------------------
-1
1

Now from the second query we are getting values Section_id = 1 or -1 and from the first query we are getting 2,10 and 1. So when we match we get only 1 record with Section_id = 1 as this is only value that matches using the in clause, also note that we dont have any value with SECTION_ID = -1.

The record ID = 2 is not coming because it has Section_ID = 2.

Hope it helps.!!
0
 
PortletPaulfreelancerCommented:
-1 indicates no section restriction

Do you also have -1 entries for department?
0
 
Member_2_7967119Author Commented:
Pawan,

I do understand that if i remove the query that refer to the section_id it will work. My intend is that if section_id does not match, then it should negate this line of statement and fetch all the sections under department id.

In short, if the value of employee_role_id is -1 then all the section should be visible to the employee but if the employee_role_id is any other value, only those section should be visible to the employee.

Sorry if I haven't stated that before.

If Department_id is =-1 it would mean that the employee is a super user and has access to all the content.
0
 
PortletPaulfreelancerCommented:
I added a super user (assume both department_id and section_id = -1)

Using a condtional inner join - I believe - will meet you requirement. Please check the results seen below (each is for a different user)
select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id and e.department_id > 0 and e.section_id > 0)
or (d.department_id = e.department_id and e.department_id > 0 and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 0


| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  0 |           0 |            -1 |         -1 |
|  2 |          1 |             1 |          2 |          Resumes Submissions |            some data |  0 |           0 |            -1 |         -1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  0 |           0 |            -1 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  0 |           0 |            -1 |         -1 |


select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id and e.department_id > 0 and e.section_id > 0)
or (d.department_id = e.department_id and e.department_id > 0 and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 1



| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  1 |           1 |             1 |          1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  2 |           1 |             2 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  2 |           1 |             2 |         -1 |


select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id and e.department_id > 0 and e.section_id > 0)
or (d.department_id = e.department_id and e.department_id > 0 and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 2


| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  3 |           2 |             1 |         -1 |
|  2 |          1 |             1 |          2 |          Resumes Submissions |            some data |  3 |           2 |             1 |         -1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  4 |           2 |             2 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  4 |           2 |             2 |         -1 |

Open in new window

see: http://sqlfiddle.com/#!6/92875/5
    CREATE TABLE DOCUMENT_MANAGEMENT
        ([ID] int, [COMPANY_ID] int, [DEPARTMENT_ID] int, [SECTION_ID] int, [DOCUMENT_TITLE] varchar(32), [DOCUMENT_DESCRIPTION] varchar(13))
    ;
        
    INSERT INTO DOCUMENT_MANAGEMENT
        ([ID], [COMPANY_ID], [DEPARTMENT_ID], [SECTION_ID], [DOCUMENT_TITLE], [DOCUMENT_DESCRIPTION])
    VALUES
        (1, 1, 1, 1, 'Procedure for HR Hiring', 'some data'),
        (2, 1, 1, 2, 'Resumes Submissions', 'some data'),
        (3, 1, 2, 1, 'Study of Engineering Designs', 'some data'),
        (4, 1, 2, 2, 'Engineering Architecture', 'some data')
    ;
    
    CREATE TABLE EMPLOYEE_ROLE
        ([ID] int, [EMPLOYEE_ID] int, [DEPARTMENT_ID] int, [SECTION_ID] int)
    ;
        
    INSERT INTO EMPLOYEE_ROLE
        ([ID], [EMPLOYEE_ID], [DEPARTMENT_ID], [SECTION_ID])
    VALUES
        (0, 0, -1, -1),
        (1, 1, 1, 1),
        (2, 1, 2, -1),
        (3, 2, 1, -1),
        (4, 2, 2, -1)
    ;

Open in new window

0
 
PortletPaulfreelancerCommented:
sorry there were some redundancies in the above joins, the following version is simpler

By the way I assume you will just add any wanted text search on top of this security logic.
select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id)
or (d.department_id = e.department_id and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 0
;

select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id)
or (d.department_id = e.department_id and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 1
;

select
*
from document_management d
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id)
or (d.department_id = e.department_id and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
where e.employee_id = 2
;

Open in new window

Results:
| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  0 |           0 |            -1 |         -1 |
|  2 |          1 |             1 |          2 |          Resumes Submissions |            some data |  0 |           0 |            -1 |         -1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  0 |           0 |            -1 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  0 |           0 |            -1 |         -1 |
        

| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  1 |           1 |             1 |          1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  2 |           1 |             2 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  2 |           1 |             2 |         -1 |
        

| ID | COMPANY_ID | DEPARTMENT_ID | SECTION_ID |               DOCUMENT_TITLE | DOCUMENT_DESCRIPTION | ID | EMPLOYEE_ID | DEPARTMENT_ID | SECTION_ID |
|----|------------|---------------|------------|------------------------------|----------------------|----|-------------|---------------|------------|
|  1 |          1 |             1 |          1 |      Procedure for HR Hiring |            some data |  3 |           2 |             1 |         -1 |
|  2 |          1 |             1 |          2 |          Resumes Submissions |            some data |  3 |           2 |             1 |         -1 |
|  3 |          1 |             2 |          1 | Study of Engineering Designs |            some data |  4 |           2 |             2 |         -1 |
|  4 |          1 |             2 |          2 |     Engineering Architecture |            some data |  4 |           2 |             2 |         -1 |

Open in new window

0
 
PortletPaulfreelancerCommented:
>>"The record #2, is not returned by this query for employee id 1 who has the permission to access all the sections. "

    INSERT INTO EMPLOYEE_ROLE
        ([ID], [EMPLOYEE_ID], [DEPARTMENT_ID], [SECTION_ID])
    VALUES
   
        (1, 1, 1, 1),
        (2, 1, 2, -1),

I understand that data to indicate the following:
user 1 has access to section 1 or department 1
user 1 has access to all of department 2

The record #2 is in department 1 section 2, and therefore user 1 should be denied that document
... I think
0
 
Pawan KumarDatabase ExpertCommented:
Try this Updated..

@deptId

DECLARE @deptId AS INT = 1
DECLARE @SEARCH_STR AS VARCHAR(1000) = 'Engineering'

SELECT DEPARTMENT_ID,SECTION_ID,DOCUMENT_TITLE  FROm DOCUMENT_MANAGEMENT
WHERE 1=1
AND (UPPER(DOCUMENT_TITLE ) Like '%' +@SEARCH_STR + '%' OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' )
AND (DEPARTMENT_ID=-1  OR -1=-1 )
AND (SECTION_ID=-1   OR -1=-1   ) 
AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1)
AND (DEPARTMENT_ID=@deptId OR (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR SECTION_ID = -1 ))

--

Open in new window


Hope it helps  !!
0
 
Member_2_7967119Author Commented:
Are the following statement  on line 7 & 8, relevant?

AND (DEPARTMENT_ID=-1  OR -1=-1 )
AND (SECTION_ID=-1   OR -1=-1   )

Also the following statement,
AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1)

If the DEPARMENT_ID=-1 is true, then the check of employee role would be bypassed.

Please illustrate. Thank you.
0
 
Pawan KumarDatabase ExpertCommented:
It worked ?

No first 2 statement we can remove...

Below looks ok to me.

AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1)
0
 
PortletPaulfreelancerCommented:
Why are you not considering the use of a conditional join? It is efficient and helps srperate the security logic from the filtering logic?
select
*
from document_management d
  /* security logic */
inner join employee_role e 
on (d.department_id = e.department_id and d.section_id = e.section_id)
or (d.department_id = e.department_id and e.section_id = -1)
or (e.department_id = -1 and e.section_id = -1)
  /* filtering logic */
WHERE e.employee_id = 2
AND ( UPPER(DOCUMENT_TITLE ) Like '%' +@SEARCH_STR + '%' 
    OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' 
         )  

Open in new window


Also. I asked why  record 2 should be allowed to user 1.  Could you answer please.
0
 
Pawan KumarDatabase ExpertCommented:
Hi Member_2_7967119,
A feedback will be appreciated.

Regards,
Pawan
0
 
Member_2_7967119Author Commented:
Still working on the same. I will update in a day.
0
 
Member_2_7967119Author Commented:
Responding to  "I asked why  record 2 should be allowed to user 1."

As per the data In the EMPLOYEE_ROLE_REGISTRATION the user 1 should have access to department id 1 and section id 1 and also should have access to all sections under department 2. (as -1 in the section id represents that the user has full access).

To illustrate it further, if I add additional column sub_section_id and if it has a value of  -1, and section id has a value greater than 0, then the user 1 can access content of department id, section id 1 and all sub section ids.

Actually I am having 5 levels in my representation.
0
 
Member_2_7967119Author Commented:
Pawan,

The following statement won't work as if the department_id passed is -1, then the query would return all records where department_id=-1 and would not use the reference to the Employee ROle Registration table? Am i missing something here ?


AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1)
0
 
Pawan KumarDatabase ExpertCommented:
What you want as output in above case?
0
 
Member_2_7967119Author Commented:
Pavan,

You were right,  with the query

AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR DEPARTMENT_ID=-1) , I was using the following

AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR @DEPARTMENT_ID=-1), which was returning records if I do not pass any department and overriding the check on the security table.


Now I added a 2 more columns to the table, namely LEVEL1_ID and LEVEL2_ID.

and changed the scripts to the following. Please advise if I am doing it rightly.

DECLARE @deptId AS INT = 11
DECLARE @sectId AS INT = 1052
DECLARE @level1Id AS INT = 1051
DECLARE @level2Id AS INT = 4118
DECLARE @USER_ID AS INT=1
DECLARE @SEARCH_STR AS VARCHAR(1000) = ''

SELECT DOCUMENT_NAME,DEPARTMENT_ID,SECTION_ID,LEVEL1_ID,LEVEL2_ID  FROM DOCUMENT_MANAGEMENT
WHERE  1=1
AND (UPPER(DOCUMENT_NAME ) Like '%' +@SEARCH_STR + '%' OR UPPER(DOCUMENT_DESCRIPTION ) Like '%' +@SEARCH_STR + '%' )
AND (DEPARTMENT_ID IN (SELECT DISTINCT DEPARTMENT_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=@USER_ID) OR DEPARTMENT_ID=-1)
AND (DEPARTMENT_ID=@deptId OR (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR SECTION_ID = -1 ))
AND (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=@USER_ID) OR SECTION_ID=-1)
AND (DEPARTMENT_ID=@deptId OR SECTION_ID=@sectId  OR (SECTION_ID IN (SELECT DISTINCT SECTION_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR SECTION_ID = -1 ))
AND (LEVEL1_ID IN (SELECT DISTINCT LEVEL1_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=@USER_ID) OR LEVEL1_ID=-1)
AND (DEPARTMENT_ID=@deptId OR SECTION_ID=@sectId OR LEVEL1_ID=@level1Id  OR (LEVEL1_ID IN (SELECT DISTINCT LEVEL1_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR LEVEL1_ID = -1 ))
AND (LEVEL2_ID IN (SELECT DISTINCT LEVEL2_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=@USER_ID) OR LEVEL2_ID=-1)
AND (DEPARTMENT_ID=@deptId OR SECTION_ID=@sectId OR LEVEL1_ID=@level1Id OR LEVEL2_ID=@level2Id OR (LEVEL2_ID IN (SELECT DISTINCT LEVEL2_ID FROM [EMPLOYEE_ROLE_REGISTRATION] WHERE USER_ID=1) OR LEVEL2_ID = -1 ))


I have posted the output based on each input parameter being passed with the expected results. There is some issue with the script.

I am attaching the table scripts , table data and the output expected based on each paramater change.


Thank you in advance for all the help.
ee.txt
eeeOutput.txt
0
 
Member_2_7967119Author Commented:
Should I ask this as a separate question?
0
 
Pawan KumarDatabase ExpertCommented:
Yes please, Thank you !, .. :)
0
 
PortletPaulfreelancerCommented:
WHY ask a new question? I would not recommend it in this case. THIS question isn't resolved.
0
 
Member_2_7967119Author Commented:
Thank you for your help .
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

  • 11
  • 9
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now