"тнιѕ вℓσg ¢συℓ∂ ѕανє уσυя мσηєу ιƒ тιмє = мσηєу" - ∂.мαηנαℓу

Tuesday, 1 October 2013

SQL based Report in CRM 2011 using SSRS - Sample Report Competitor Win / Loss

Please refer the following posts for basics.

SQL based Report in CRM 2011 using SSRS - Sample Report User Summary - Part 1

http://crmdm.blogspot.com/2013/09/sql-based-report-in-crm-2011-using-ssrs.html


SQL based Report in CRM 2011 using SSRS - Sample Report User Summary - Part 2

http://crmdm.blogspot.com/2013/09/sql-based-report-in-crm-2011-using-ssrs_29.html


      This post aims to learn SQL based reporting for CRM 2011 using SSRS. This time I would like to pick up a sample report from CRM called Competitor Win / Loss. If you are an expert in SQL please ignore this post.

Scenario:

The report is based on the simple concept Opportunities which has Competitor / Competitors. As you all know in CRM we might add competitors related to the Opportunity. Also remember that if we loose an opportunity we might select a Competitor by whom we lost the golden opportunity.

For instance a competitor was involved in total 10 Opportunities. 5 Opportunities were won by Business ( which is good for Business.) 3 are Open ( Competitor is still trying on it ) 2 Lost ( Lost for the Business and lost against this competitor)

In CRM we have 3 Competitors listed. We also have 3 Open and 2 Closed Opportunities.  Lets see how these competitors are involved in the 5 sample Opportunities.


This report uses temporary tables to achieve the expected results.


 Lets examine the query in detail as the first part and then the report design. Because query is the important element of this report.


1. The core query used in this report could be found in  the dataset DSCompetitor. And it looks something like


" 

Create table #CompetitorOppIds (
       opportunityid uniqueidentifier,
       competitorid uniqueidentifier,
       name nvarchar(max),
       statecode int,
       lostto int
       primary key clustered
       (     
              [opportunityid],
              [competitorid]
       )     
)

create statistics compoppstat on #CompetitorOppIds(opportunityid, competitorid)

Declare @SQL  nVarchar(max)
Set @SQL =
'insert into #CompetitorOppIds
select oppcomp.opportunityid, oppcomp.competitorid, NULL, o.statecode, 0
from (' +  @CRM_FilteredOpportunity + ') as o
join FilteredOpportunityCompetitors  as oppcomp
on (o.opportunityid = oppcomp.opportunityid)
where o.statecode <> 2

insert into #CompetitorOppIds
select oppc.opportunityid, oppc.competitorid, NULL, o.statecode, 0
from (' +  @CRM_FilteredOpportunity + ') as o
join FilteredOpportunityClose as oppc
on (o.opportunityid = oppc.opportunityid and o.statecode = 2 and oppc.statecode = 1 and oppc.competitorid IS NOT NULL)

Select
filcomp.name as Competitor,
comp.competitorid,
sum(Case when comp.statecode = 0 then 1 else 0 end) as openopp,
sum( Case when comp.statecode = 1 or comp.statecode = 2 then 1 else 0 end) as closeopp,
sum(case when comp.statecode = 1 then 1 else 0 end) as wonopp,
sum(case when comp.statecode = 2 then 1 else 0 end) as lostopp
From #CompetitorOppIds comp
join (' +  @CRM_FilteredCompetitor + ') as filcomp
on (filcomp.competitorid = comp.competitorid)
group by comp.competitorid, filcomp.name '

Exec (@SQL)
drop table #CompetitorOppIds"

How does it look like ? A hard nut? May be but we are gonna crack it.


For now try to understand 3 things and rest is on the way.


I. This query is using 2 parameters



  • @CRM_FilteredCompetitor 
  •  @CRM_FilteredOpportunity
This also means that we need to pass these 2 values to the query if we need to play around this query outside the report ( could be SQL Management Studio)

II. And its using a temporary table called #CompetitorOppIds

III. 3rd point is that it has a @SQL variable which holds the whole query. We might split this, for a better understanding.



2. Lets take this hard nut to SQL Management Studio and crack it. So that we could understand it in a better way.


First step is to declare the 2 parameters as shown below



Declare @CRM_FilteredOpportunity nVarchar(40)
Declare @CRM_FilteredCompetitor nVarchar(40)

SET @CRM_FilteredOpportunity='FilteredOpportunity'
SET @CRM_FilteredCompetitor='FilteredCompetitor'




After this try to execute the query. You might get some error something like this. Please do this step only if you get this error message.



Msg 102, Level 15, State 1, Line 3
Incorrect syntax near ')'.
Msg 102, Level 15, State 1, Line 10
Incorrect syntax near ')'.
Msg 102, Level 15, State 1, Line 22


Incorrect syntax near ')'.

Never mind. In select queries you could see something like 




from (' +  @CRM_FilteredOpportunity + ')

Just remove the simple brackets from 3 places.



3. So now our query looks like

"  
Declare @CRM_FilteredOpportunity nVarchar(40)
Declare @CRM_FilteredCompetitor nVarchar(40)

SET @CRM_FilteredOpportunity='FilteredOpportunity'
SET @CRM_FilteredCompetitor='FilteredCompetitor'


Create table #CompetitorOppIds (
      opportunityid uniqueidentifier,
      competitorid uniqueidentifier,
      name nvarchar(max),
      statecode int,
      lostto int
     primary key clustered
      (    
            [opportunityid],
            [competitorid]
      )    
)

create statistics compoppstat on #CompetitorOppIds(opportunityid, competitorid)

Declare @SQL  nVarchar(max)
Set @SQL =
'insert into #CompetitorOppIds
select oppcomp.opportunityid, oppcomp.competitorid, NULL, o.statecode, 0
from ' +  @CRM_FilteredOpportunity + ' as o
join FilteredOpportunityCompetitors  as oppcomp
on (o.opportunityid = oppcomp.opportunityid)
where o.statecode <> 2

insert into #CompetitorOppIds
select oppc.opportunityid, oppc.competitorid, NULL, o.statecode, 0
from ' +  @CRM_FilteredOpportunity + ' as o
join FilteredOpportunityClose as oppc
on (o.opportunityid = oppc.opportunityid and o.statecode = 2 and oppc.statecode = 1 and oppc.competitorid IS NOT NULL)

Select
filcomp.name as Competitor,
comp.competitorid,
sum(Case when comp.statecode = 0 then 1 else 0 end) as openopp,
sum( Case when comp.statecode = 1 or comp.statecode = 2 then 1 else 0 end) as closeopp,
sum(case when comp.statecode = 1 then 1 else 0 end) as wonopp,
sum(case when comp.statecode = 2 then 1 else 0 end) as lostopp
From #CompetitorOppIds comp
join ' +  @CRM_FilteredCompetitor + ' as filcomp
on (filcomp.competitorid = comp.competitorid)
group by comp.competitorid, filcomp.name '

Exec (@SQL)
drop table #CompetitorOppIds"


4. As the next step we are gonna crack the @SQL. We also replaced the parameters with values. Please note we are doing all these to understand the query in detail.

Now the query looks like

Create table #CompetitorOppIds (
      opportunityid uniqueidentifier,
      competitorid uniqueidentifier,
      name nvarchar(max),
      statecode int,
      lostto int
     primary key clustered
      (    
            [opportunityid],
            [competitorid]
      )    
)

create statistics compoppstat on #CompetitorOppIds(opportunityid, competitorid)

insert into #CompetitorOppIds
select oppcomp.opportunityid, oppcomp.competitorid, NULL, o.statecode, 0
from   FilteredOpportunity    as o
join FilteredOpportunityCompetitors  as oppcomp
on (o.opportunityid = oppcomp.opportunityid)
where o.statecode <> 2

insert into #CompetitorOppIds
select oppc.opportunityid, oppc.competitorid, NULL, o.statecode, 0
from  FilteredOpportunity  as o
join FilteredOpportunityClose as oppc
on (o.opportunityid = oppc.opportunityid and o.statecode = 2 and oppc.statecode = 1 and oppc.competitorid IS NOT NULL)

Select
filcomp.name as Competitor,
comp.competitorid,
sum(Case when comp.statecode = 0 then 1 else 0 end) as openopp,
sum( Case when comp.statecode = 1 or comp.statecode = 2 then 1 else 0 end) as closeopp,
sum(case when comp.statecode = 1 then 1 else 0 end) as wonopp,
sum(case when comp.statecode = 2 then 1 else 0 end) as lostopp
From #CompetitorOppIds comp
join FilteredCompetitor  as filcomp
on (filcomp.competitorid = comp.competitorid)
group by comp.competitorid, filcomp.name

drop table #CompetitorOppIds"

5. Lets dig more


Also please note the following info about the joining tables 


Ref: MSDN


  • FilteredOpportunity--Potential revenue-generating event or sale to an account, that needs to be tracked through a sales process to completion.
  • FilteredCompetitor--Tracks information about a business competing for the sale represented by a lead or opportunity.
  • FilteredOpportunityClose--Activity that is created automatically when an opportunity is closed, containing information such as the description of the closing and actual revenue.
  • FilteredOpportunityCompetitors-- Association between opportunities and competitors.


Opportunity entity:

statecode
State
0
Open

State
1
Won

State
2
Lost

Joins ref( http://www.w3schools.com/sql/sql_join.asp)


Clustered and Nonclustered index --ref (http://technet.microsoft.com/en-us/library/ms190457.aspx)


CREATE STATISTICS --ref( http://msdn.microsoft.com/en-us/library/ms188038.aspx)


Query part by part.


Query Part 1:


INNER JOIN between FilteredOpportunity and FitereredOpportunityCompetitors to find out which Opportunities are in either OPEN state or WON state. We have 4.



insert into #CompetitorOppIds
select oppcomp.opportunityid, oppcomp.competitorid, NULL, o.statecode, 0
from   FilteredOpportunity    as o
join FilteredOpportunityCompetitors  as oppcomp
on (o.opportunityid = oppcomp.opportunityid)


where o.statecode <> 2




The info we need are opportunityid, competitorid, name, statecode and lostto


From this Join query we get the Opportunities those are won or open which has competitor.


---------------------------------------------------------------------------------------------------------------------------

Nota Bene:
Its good to understand the difference between SELECT INTO and INSERT INTO SELECT statements. 
The SELECT INTO statement selects data from one table and inserts it into a new table. (Ref:http://www.w3schools.com/sql/sql_select_into.asp)

For instance, if table is not created before



The other one is


The INSERT INTO SELECT statement selects data from one table and inserts it into an existing table. Any existing rows in the target table are unaffected. ( Ref: http://www.w3schools.com/sql/sql_insert_into_select.asp )


For instance, we have already created a temporary table and we would like to insert a few more rows. OR appending some rows to an existing temporary table

---------------------------------------------------------------------------------------------------------------------------

Query Part 2:




insert into #CompetitorOppIds
select oppc.opportunityid, oppc.competitorid, NULL, o.statecode, 0
from  FilteredOpportunity  as o
join FilteredOpportunityClose as oppc
on (o.opportunityid = oppc.opportunityid and o.statecode = 2 and oppc.statecode = 1 and oppc.competitorid IS NOT NULL)



This means Opportunity is lost and Opportunity close activity is completed and it has a competitor.

For Opportunity,


statecode
State
0
Open

State
1
Won

State
2
Lost

For Opportunity Close Entity,


statecode
State
0
Open
State
1
Completed
State
2
Canceled







These rows ( row ) appended to the existing temporary table using the INSERT INTO SELECT statement.



We would like to have the competitor name and further details. So


Query part 3: Temporary table INNER JOIN FilteredCompetitor



Select
filcomp.name as Competitor,
comp.competitorid,
sum(Case when comp.statecode = 0 then 1 else 0 end) as openopp,
sum( Case when comp.statecode = 1 or comp.statecode = 2 then 1 else 0 end) as closeopp,
sum(case when comp.statecode = 1 then 1 else 0 end) as wonopp,
sum(case when comp.statecode = 2 then 1 else 0 end) as lostopp
From #CompetitorOppIds comp
join FilteredCompetitor  as filcomp
on (filcomp.competitorid = comp.competitorid)


group by comp.competitorid, filcomp.name

This is a clever query. So we got Competitor name ,id, next is aggregations

SUM ( Open Opp)
SUM ( Closed Opp)
SUM( Won Opp)
SUM (Lost Opp)


We lost one opportunity because of the 3rd Competitor. And we won one Opportunity against the same competitor

Lets test it once more. Lets close one opportunity as lost and see the result again.



We lost one more opportunity because of the second Competitor. There are no more open opportunities against this Competitor. Whereas first and third competitors are still having open opportunities against them. Fingers crossed for Business and Competitors !

6. Here is the test query with some more comments. Please note this is just one approach to learn sql reporting with CRM 2011. If you have a better approach please feel to try that. Our target is same, which is nothing but learning the sql based reports for CRM 2011.

"--- This is our temporary table. Please note that its prefixed with ‘#’ character
Create table #CompetitorOppIds (
      opportunityid uniqueidentifier,
      competitorid uniqueidentifier,
      name nvarchar(max),
      statecode int,
      lostto int
     primary key clustered
      (    
            [opportunityid],
            [competitorid]
      )    
)
--- Please note the fields defined for the temporary table
---Indexing helps SQL server to retrieve the rows quickly

--This is to improve query performance
create statistics compoppstat on #CompetitorOppIds(opportunityid, competitorid)

insert into #CompetitorOppIds
select oppcomp.opportunityid, oppcomp.competitorid, NULL, o.statecode, 0
from   FilteredOpportunity    as o
join FilteredOpportunityCompetitors  as oppcomp
on (o.opportunityid = oppcomp.opportunityid)
where o.statecode <> 2

--Lets see whats inside the temp table now
SELECT * FROM #CompetitorOppIds

insert into #CompetitorOppIds
select oppc.opportunityid, oppc.competitorid, NULL, o.statecode, 0
from  FilteredOpportunity  as o
join FilteredOpportunityClose as oppc
on (o.opportunityid = oppc.opportunityid and o.statecode = 2 and oppc.statecode = 1 and oppc.competitorid IS NOT NULL)

--Lets see whats inside the temp table now
SELECT * FROM #CompetitorOppIds

Select
filcomp.name as Competitor,
comp.competitorid,
sum(Case when comp.statecode = 0 then 1 else 0 end) as openopp,
sum( Case when comp.statecode = 1 or comp.statecode = 2 then 1 else 0 end) as closeopp,
sum(case when comp.statecode = 1 then 1 else 0 end) as wonopp,
sum(case when comp.statecode = 2 then 1 else 0 end) as lostopp
From #CompetitorOppIds comp
join FilteredCompetitor  as filcomp
on (filcomp.competitorid = comp.competitorid)
group by comp.competitorid, filcomp.name

--Deletion of temporary table
drop table #CompetitorOppIds"


7. Competitor Win / Loss report is using this query. The report design is not complex for this report. Its based on matrix control. For more details about matrix control please refer this post.

http://crmdm.blogspot.com/2013/09/custom-fetchxml-based-grid-report-with.html




8. Here is the preview of the report.  Please note that the main part of this report is the SQL query. 






Sunday, 29 September 2013

SQL based Report in CRM 2011 using SSRS - Sample Report User Summary - Part 2


Please refer the following link for the first part.

SQL based Report in CRM 2011 using SSRS - Sample Repoort User Summary - Part 1


http://crmdm.blogspot.com/2013/09/sql-based-report-in-crm-2011-using-ssrs.html

In the first part we examined the User Summary Report provided in CRM 2011. The next part is to modify the report based on a scenario.

Scenario:

We would like to see the User summary report with user's details and corresponding activities owned by the user. Activities should be categorized based on the activity type. A count of activities should be displayed against each user.

So we have FilteredSystemuser -- Filtered view which holds user's profile

FilteredActivityPointer -- Filtered view which holds a user's activity or task details.

Relation:

SystemUser  to ActivityPointer ( One to Many )

So lets see how could we do this.

From the downloaded report we have the following query. And we need to alter this query based on our scenario.


Declare @SQL nVarchar(4000)

SET @SQL = 'SELECT role.name, 
    cast(systemuser.systemuserid as nvarchar(50)) as systemuserid, 
    fullname, title, internalemailaddress, address1_telephone1, systemuser.businessunitidname, systemuser.businessunitid

FROM FilteredSystemUser AS systemuser
    LEFT JOIN FilteredSystemUserRoles AS userroles on systemuser.systemuserid = userroles.systemuserid
    LEFT JOIN FilteredRole AS role on role.roleid = userroles.roleid
  where  domainname is not null and domainname <> '''' and accessmode <> 3
ORDER BY systemuser.businessunitidname, fullname '


EXEC(@SQL)

In this scenario we do not need any role information. So we could remove that part and add the query bits to include activity details.

Our New Query would be

SELECT COUNT(Activity.activityid) AS ActivityCOUNT,Activity.activitytypecodename AS ACTIVITYTYPE, 
cast(systemuser.systemuserid as nvarchar(50)) as systemuserid, 
    fullname, title, internalemailaddress, address1_telephone1, systemuser.businessunitidname, 
    systemuser.businessunitid

FROM FilteredSystemUser AS systemuser
    LEFT JOIN FilteredActivityPointer AS Activity on systemuser.systemuserid = Activity.ownerid 
where  domainname is not null and domainname <> '''' and accessmode <> 3
GROUP BY Activity.activitytypecodename,systemuser.systemuserid,fullname, 
title, internalemailaddress, address1_telephone1, systemuser.businessunitidname, 
systemuser.businessunitid

ORDER BY systemuser.businessunitidname, fullname 


"The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by one or more columns" ( Ref: http://www.w3schools.com/sql/sql_groupby.asp )

In our case we need to have a count of activities based on the activitytypecodename ( For instance, E-mail, Task, Fax etc). We are looking for activities owned by each user in our CRM.
The reason for LEFT JOIN -- Some users may not have any activities, but still we need to list the user profile.


1. We need to apply this query to our core data set of User summary report, DSSystemusers as shown below.


2. The next part is to alter the report design. We need to remove the existing column grouping ( based on roles ) .And  need to bring the column grouping based on activity type




Say 'Okay' and press 'Okay' button

3.  In the Data area of the matrix, we need to display the activity count. So Choose the ActivityCount as shown below.



4. Now lets add the new Column group. Right click on the Data section and Add Column group as shown below.



5.  Choose the Activity Type as show below. We don't prefer a group header in this case as it could display dynamic values.



6. Lets preview the report. We could see some symbols. Symbol were used in the downloaded report. So we need to change it.




7.  Choose the Text box properties as shown below.




8. We could see something like this.


We need to change to some fonts, say Arial




We need to do the same change for Data section as well.

9. Now let's preview the report.






10. To make it more readable we could make the Activity type font to align vertical. Its a property called Writing mode


11.  Lets preview the report.  




12. The last recommended change is if there are no activities, lets display the Activity type as ' No activities'. We could easily identify the users who doesn't have any activities. This change could be done in Activity Type Text box with an expression.


Expression used:


=IIf(Fields!ACTIVITYTYPE.Value ="","No Activities",Fields!ACTIVITYTYPE.Value)


13. And our final Doughnut is here. 





















SQL based Report in CRM 2011 using SSRS - Sample Report User Summary - Part 1

This post is regarding SQL based report in CRM using SSRS.

It is worth to refer the following if you need the basics.


Custom Fetchxml based Report in CRM 2011 using SSRS

http://crmdm.blogspot.com/2013/09/custom-fetchxml-based-report-in-crm.html

Custom Reports in CRM 2011 using SSRS.
http://crmdm.blogspot.com/2013/09/custom-reports-in-crm-2011-using-ssrs.html


It would be good if we follow the principle "First things first". Lets try to understand the concept of SQL report from CRM itself. Dynamics CRM provided us some sample reports to learn. Lets start the base right from there.

Scenario: User Summary Report in CRM.

Also we could find : How to open a CRM record from the SSRS report ?, How could we pass parameter to a SSRS report?

We are going to download the provided User summary Report in CRM and trying to understand how its done and analyse the key things done in this report. We could use these tips when we develop our own custom reports. 

1. In CRM Main Application left navigation, choose Workplace -> Reports-->Select User Report and click on the Edit button found on the ribbon.

2. Actions --> Download Report as shown below.



3. Lets Create a new project called 'SQLBasedReports'

For basics please refer the following link:
http://crmdm.blogspot.com/2013/09/custom-fetchxml-based-report-in-crm.html

4. We could add the downloaded User Summary report as shown below.



5. And then open the report by double clicking on it.  The report design is shown below.




5. The first change we need to do is the Connection to CRM ( Datasource ). Lets change the name to 'MyCRM' and provide our connections.  

data source=localhost; // This represents the SQL Server name

initial catalog=Adventure_Works_Cycle_MSCRM // This is the CRM DB

So we need to change it accordingly.



6. Please note that there are 3 different data sets in this report. 

UserInfo-- This data set is used to retrieve the current user full name

DSSystemUsers -- Core part of the report. It retrieves the user details

DSNumandCurrency -- To retrieve the formats in CRM.

We are going to examine each of these.




7. UserInfo -- This query brings the fullname of the user by passing the GUID of the user as shown below.

select fullname 
from FilteredSystemUser 

where systemuserid = dbo.fn_FindUserGuid()




Query executed from SQL Server Management Studio




8. Now we are going to examine our core query.

DSSystemUsers



In SQL Server Management Studio



We could see a parameter here @CRM_FilteredSystemUser . Lets find out how this value is supplied.

In the Data set properties, go to Parameter section. There is a parameter defined for the query






There is no change required. We are trying to understand how the expression was defined.

The value will be - FilteredSystemUser -- FilteredView which stores user details.


Lets give a try with the query with the real value.

Test query looks like 


Declare @SQL nVarchar(4000)

SET @SQL = 'SELECT role.name, 
    cast(systemuser.systemuserid as nvarchar(50)) as systemuserid, 
    fullname, title, internalemailaddress, address1_telephone1, systemuser.businessunitidname, systemuser.businessunitid

FROM FilteredSystemUser AS systemuser
    LEFT JOIN FilteredSystemUserRoles AS userroles on systemuser.systemuserid = userroles.systemuserid
    LEFT JOIN FilteredRole AS role on role.roleid = userroles.roleid
where  domainname is not null and domainname <> '''' and accessmode <> 3
ORDER BY systemuser.businessunitidname, fullname '


EXEC(@SQL)

Please note:

"The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match." ( Ref: http://www.w3schools.com/sql/sql_join_left.asp )

In our scenario, user may not have a role. This possibility is well managed in the query.

9. The last data set is DSNumandCurrency -- To retrieve the formats in CRM.

Query used --select * from dbo.fn_GetFormatStrings()

10. Now lets analyse the report design.

There are 4 Row groups 

  • full name 
  • title
  • email
  • phone
And one Column group based on
  • name ( Here it is role name. For instance, System Administrator)






11. When a user clicks on the full name, the report opens the corresponding CRM user record.

In other words,

How to open a CRM record from the SSRS report ?

Open the fullname text box properties as shown below.



Text box properties --> Action and click on the expression as shown below.




We could see the following expression

=IIF(IsNothing(Parameters!CRM_URL.Value), System.DBNull.Value, Parameters!CRM_URL.Value & "?OTC=8&ID={"& Fields!systemuserid.Value.ToString() &"}")

This means that we are passing the GUID of the System user. Also there is a CRM_URL value which holds the CRM URL value. OTC- Object Type Code
Accounts -1
Contact -2
Systemuser - 8  etc.


So please keep in mind that in another scenario we need to modify this expression accordingly.


12.  Here is a sample run of the user summary report.



13. Its possible to change the colours of the report as per our preference.

For instance,





Saturday, 28 September 2013

Custom Reports in CRM 2011 using SSRS- Tips

As you all know there are mainly two types of Custom reports could be developed by using SSRS ( SQL Server Reporting Services ) 

  •  SQL- based ( Microsoft MSDN says "For security reasons, you cannot deploy custom SQL-based reports to Microsoft Dynamics CRM Online") 
  •  Fetch - based. ( Works in both On-Premise and Online versions)

Limitations of Fetch- based Reports:

The limitations of fetch-based reports are well explained in the following MSDN Blog post.

Reference:
"
  1. Fetch does not support RIGHT OUTER JOIN and FULL OUTER JOIN
  2. Fetch does not support EXISTS/IN condition with sub-query/expression
  3. An amount of 5000 returned records maximum
  4. No “UNION” selects
  5. You cannot specify group by / sum queries – You can only select the records in detail and then perform the aggregation in your report. 
  6. Number of entity join (link) limitations
  7. FetchXML reports cannot use non-CRM online data sources
  8. Learning curve – for report writers that are not familiar with FetchXML the syntax is quite different from SQL." (Ref:http://blogs.msdn.com/b/crminthefield/archive/2012/11/27/custom-reporting-in-microsoft-dynamics-crm-fetch-vs-filtered-views.aspx)
Now if you start developing SQL- based report, please never forget the following tips.

"Filtered views exist for all Microsoft Dynamics CRM entities, including custom entities. Your custom SQL-based reports cannot read data directly from the Microsoft Dynamics CRM database tables. Instead, you must use the filtered views to retrieve data for your custom SQL-based reports."(Ref:http://msdn.microsoft.com/en-us/library/gg328467.aspx)

"SQL-based reports in Microsoft Dynamics CRM use the filtered views provided for each entity to retrieve data for the reports. Filtered views are fully compliant with the Microsoft Dynamics CRM security model. When you run a report that obtains data from filtered views, the Microsoft Dynamics CRM security role determines what data you can view in the report. Data in filtered views is restricted at these levels: the organization, the business unit, the owner, and at the field level."(Ref: http://msdn.microsoft.com/en-us/library/gg328467.aspx)

In simple words, when we use Filtered views for SQL- based reporting, CRM implements the same security model into our report, which is really cool.