Creating a Stored Procedure with if statement on Inner Join - sql-server

I am trying to create a stored procedure which would execute an INNER JOIN code block based on a parameter value. However, I keep getting "Incorrect syntax near '#reSourceID'."
if (#VendorID = 11)
#reSourceID = 't.reSourceID'
if (#VendorID = 5)
#reSourceID = 't.SourceID'
SELECT t.ID, fsg.SigCap, fsg.VendorId
FROM FormCap fsg
INNER JOIN FlightTrip t
ON fsg.SourceId = #reSourceID
AND fsg.VendorId = #VendorID
INNER JOIN ContractProvider cpu
ON t.Id = cpu.VendorId
WHERE (t.ID = #FinTransID)
AND (cpu.userID = #UserID)
Any ideas what would be causing the error?

Your syntax error is because you haven't really joined on FlightTrip t. You can't do what you are trying to do in compiled SQL. You would have to create a varchar variable full of your statement and then use EXEC SP_EXECUTESQL(#Statement) to run it.
declare #statement nvarchar(4000)
select #statement = '
SELECT t.ID,
fsg.SigCap,
fsg.VendorId
FROM FormCap fsg
INNER JOIN FlightTrip t
ON fsg.SourceId = '+convert(nvarchar(100),#reSourceID)+'
AND fsg.VendorId = '+convert(nvarchar(100),#VendorID)+'
INNER JOIN ContractProvider cpu
ON t.Id = cpu.VendorId
WHERE t.ID = '+convert(nvarchar(100),#FinTransID)+'
AND cpu.userID = '''+convert(nvarchar(100),#UserID)+''''
exec sp_executesql #statement

SELECT t.ID, fsg.SigCap, fsg.VendorId
FROM FormCap fsg
INNER JOIN FlightTrip t
ON fsg.SourceId = CASE #VendorID
WHEN 11 THEN t.reSourceID
WHEN 5 THEN t.SourceID
END
AND fsg.VendorId = #VendorID
INNER JOIN ContractProvider cpu
ON t.Id = cpu.VendorId
WHERE (t.ID = #FinTransID)
AND (cpu.userID = #UserID)

As Bill answered, the optimim solution is probably going to involve dynamic SQL although I'd recommend going a little farther both for performance and security.
If you parameterize your call to sp_executeSQL you'll both avoid issues with escaping quotes and potential SQL injection problems but it will allow SQL to reuse the plan since the query text won't change.
Here's what that would look like:
if (#VendorID = 11)
#reSourceID = 't.reSourceID'
if (#VendorID = 5)
#reSourceID = 't.SourceID'
DECLARE #sql NVARCHAR(MAX)
DECLARE #params NVARCHAR(500)
SET #sql ='SELECT t.ID,
fsg.SigCap,
fsg.VendorId
FROM FormCap fsg
INNER JOIN FlightTrip t
ON fsg.SourceId = '+CONVERT(NVARCHAR(MAX),#reSourceID)+'
AND fsg.VendorId = #VendorID
INNER JOIN ContractProvider cpu
ON t.Id = cpu.VendorId
WHERE t.ID = #FinTransID
AND cpu.userID = #userID'
SET #params = N'#VendorID INT, #FinTransID INT, #userID UNIQUEIDENTIFIER'
EXECUTE sp_executesql #sql, #params, #venderID = #venderID, #finTransID=#finTransID, #userID = #userID
Above is the gist of the solution, I don't fully know your types and inputs. Also, without schema, I can't test my code, but it should get your going.
For more information on sp_executesql, msnd is a great resource.

Related

Stored procedure takes more than 30 seconds to execute

I am optimizing the stored procedure in which I get list of more than 10K user ids in one table variable as a parameter. I am setting flag #ContainsUserIds if there is any data into the table variable. In main query selecting the all the users that are present in table variable or all the users if no data found in table variable (There are more condition in where clause).
The problem is here that the statement in where clause takes more than 30 second which has OR condition. If I removed the first condition to check #ContainsUserIds = 0 then it will execute within a second. Can someone please help me to optimize this query.
This Stored procedure is called from different position so the User ids may not pass.
CREATE PROCEDURE [core].[spGetUsersByFilter]
(
#ClientId nvarchar(38) = NULL,
#UserIds [UserIdList] readonly
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #ContainsUserIds BIT = 0,
SET #ContainsUserIds = CASE
WHEN EXISTS(SELECT TOP 1 1 FROM #UserIds)
THEN 1
ELSE 0
END;
SELECT DISTINCT ES.Id
FROM [db].[Users] E
JOIN [db].[UserDetails] ES ON ES.UserId = E.Id
WHERE E.[Active] = 1
AND (#ContainsUserIds = 0 OR E.UserId IN(SELECT Item FROM #UserIds))
AND ES.ClientId = #ClientId
END
SQL notoriously dislikes OR, the optimizer sometimes works around it but in most cases it's a good idea to convert things to a UNION [ALL] syntax. If there's just 1 OR that's often easy to do, if there are multiple things explode fast.
Anyway, you could thus convert your stored procedure to:
CREATE PROCEDURE [core].[spGetUsersByFilter]
(
#ClientId nvarchar(38) = NULL,
#UserIds [UserIdList] readonly
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #ContainsUserIds BIT = 0,
SET #ContainsUserIds = CASE
WHEN EXISTS(SELECT * FROM #UserIds)
THEN 1
ELSE 0
END;
SELECT DISTINCT ES.Id
FROM [db].[Users] E
JOIN [db].[UserDetails] ES ON ES.UserId = E.Id
WHERE E.[Active] = 1
AND (#ContainsUserIds = 0)
AND ES.ClientId = #ClientId
UNION ALL
SELECT DISTINCT ES.Id
FROM [db].[Users] E
JOIN [db].[UserDetails] ES ON ES.UserId = E.Id
WHERE E.[Active] = 1
AND (#ContainsUserIds = 1)
AND E.UserId IN (SELECT Item FROM #UserIds))
AND ES.ClientId = #ClientId
END
That said, you might just as well split things up here and do 2 separate SELECTs. Also, if the table-variable contains more than a couple of records you may prefer to use a temp-table as the latter allows for indexing and most importantly has statistics handling which WILL result in a better execution plan. I also prefer JOIN over IN (), just make sure there are no doubled values when JOINing.
CREATE PROCEDURE [core].[spGetUsersByFilter]
(
#ClientId nvarchar(38) = NULL,
#UserIds [UserIdList] readonly
)
AS
BEGIN
SET NOCOUNT ON
SELECT DISTINCT Item
INTO #UserIds
FROM #UserIds
IF ##ROWCOUNT = 0
BEGIN
SELECT DISTINCT ES.Id
FROM [db].[Users] E
JOIN [db].[UserDetails] ES ON ES.UserId = E.Id
WHERE E.[Active] = 1
AND ES.ClientId = #ClientId;
END
ELSE
BEGIN
CREATE UNIQUE INDEX uq0_UserIds ON #UserIds ( Item ) WITH (FILLFACTOR = 100);
SELECT DISTINCT ES.Id
FROM [db].[Users] E
JOIN [db].[UserDetails] ES ON ES.UserId = E.Id
JOIN #UserIds T ON T.Item = E.UserId
WHERE E.[Active] = 1
AND ES.ClientId = #ClientId
END
END
PS: all above written in notepad and untested, some assembly may be required =)

Increase Open Query performance

I have a small problem with this query. It returns me around 13k rows and takes around 12seconds or more.
SELECT
hla.Id,
hc.Nome AS Colaborador,
hla.UAP,
hra.Referencia,
hra.QtdAbastecimento,
hra.QtdPecasPorCaixa,
hra.QtdCaixas,
A.Etiqueta,
A.Localizacao
FROM
hListasAbastecimento hla
INNER JOIN hColaboradores hc
ON hc.Id = hla.ColaboradorId
INNER JOIN
hReferenciasAbastecimento hra
ON hla.Id = hra.ListaAbastecimentoId
LEFT JOIN OPENQUERY(MACPAC,
'SELECT
FET001.ET0102 AS Referencia,
FET001.ET0101 AS Etiqueta,
FET001.ET0109 AS Localizacao
FROM
AUTO.D805DATPOR.FET001 FET001
WHERE FET001.ET0104 = ''POE''
AND FET001.ET0105 = ''DIS''
ORDER BY FET001.ET0101 ASC ') A
ON A.Referencia = hra.Referencia
WHERE hla.Id = #Id
I'm not sure what can i do to increase the performance of this. I am already using the only primary key to link both tables. I also have no control over the linked server so i cannot create new indexes
UPDATE
I found out through a co-worker that there was a table on linked server that could help me get results by "UAP" which takes only 4 seconds just to get around 1700 rows which is what i needed
ALTER PROCEDURE GenerateListaAbastecimento
#Id INT,
#UAP NVARCHAR(20)
AS
BEGIN
CREATE TABLE #tempTable
(
Id INT PRIMARY KEY,
Referencia NVARCHAR(15),
Etiqueta INT,
Localizacao NVARCHAR(20)
UNIQUE(Id, Etiqueta)
)
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL ='INSERT INTO #tempTable
SELECT
Id,
Referencia,
Etiqueta,
Localizacao
FROM OPENQUERY(MACPAC,
''SELECT
ROW_NUMBER() OVER(ORDER BY FET001.ET0101 ASC) AS Id,
A.RH6001 as Referencia,
FET001.ET0101 AS Etiqueta,
FET001.ET0109 AS Localizacao
FROM AUTO.D805DATPOR.TRP060H AS A
LEFT JOIN AUTO.D805DATPOR.FET001 FET001
ON FET001.ET0102 = A.RH6001
AND FET001.ET0104 = ''''POE''''
AND FET001.ET0105 = ''''DIS''''
AND A.RH6002 = N''' + QUOTENAME(#UAP,N'''') + N''' '')'
EXEC sp_executesql #SQL
SELECT
hla.Id,
hc.Nome AS Colaborador,
hla.UAP,
hra.Referencia,
hra.QtdAbastecimento,
hra.QtdPecasPorCaixa,
hra.QtdCaixas,
A.Etiqueta,
A.Localizacao
FROM
hListasAbastecimento hla
INNER JOIN hColaboradores hc
ON hc.Id = hla.ColaboradorId
INNER JOIN
hReferenciasAbastecimento hra
ON hla.Id = hra.ListaAbastecimentoId
LEFT JOIN #tempTable A
ON A.Referencia = hra.Referencia
WHERE hla.Id = #Id
ORDER BY A.Etiqueta ASC
DROP TABLE #tempTable
END

Not Exists not playing well with multiple columns

This has to be something simple that I have just missed...
I've got a temp table say this:
CREATE TABLE #tsa
(
AttendeeID int,
SurveyID int,
TrainingAttendeeID int
)
I get a single record using TOP 1 with something similar to this:
SELECT
TOP 1
#AttendeeID=ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID = ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
ta.AttendedTraining = 1
AND ta.ConfirmAttendedEmailOn IS NOT NULL
--only get people who didn't fill out the survey
AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
ORDER BY
ta.AttendeeID,
ts.SurveyID
As soon as I get this one record I store it into my temp table as such:
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
Then I need to go through this whole procedure of checking some data and sending an email...as soon as that email is sent I need to pick up the next record not including the record I had previously...So without showing too much code:
WHILE SomeCondition
BEGIN
--do some thing...
--pick up next one
--grab next one
SELECT
TOP 1
#AttendeeID = ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID=ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
(
--same where as original
ta.AttendedTraining = 1
AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tsa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
)
ORDER BY
ta.AttendeeID,
ts.SurveyID
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
END
Notice the where condition inside of this..I've added one more AND...namely:
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
Just to ensure I am not reusing the record I already processed in my temp table...and you'll notice I reinsert into my temp table at the end as well...
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
Every time I run this stored procedure it goes on infinitly and so I believe something is wrong with my condition at this point. I'm having a brain fart..or maybe there is just too much noise in the office. What am I missing here? I placed a print statement and it keeps processing the same record...so something tells me this last condition in my where clause is incorrect.
Edit
Here's the entire procedure...My issue is the record set only has one record in it...But the sproc continues to process this same record
PROCEDURE ScriptSendTrainingSurveyReminders
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #AttendeeID int
DECLARE #TrainingAttendeeID int
DECLARE #SurveyID int
DECLARE #Message nvarchar(MAX)
DECLARE #Subject nvarchar(255)
CREATE TABLE #tSa
(
AttendeeID int,
SurveyID int,
TrainingAttendeeID int
)
SELECT
TOP 1
#AttendeeID=ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID = ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
ta.AttendedTraining = 1
AND ta.ConfirmAttendedEmailOn IS NOT NULL
--only get people who didn't fill out the survey
AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
ORDER BY
ta.TrainingAttendeeID,
ta.AttendeeID,
ts.SurveyID
--insert into our temp table
INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
WHILE #TrainingAttendeeID IS NOT NULL AND #AttendeeID IS NOT NULL AND #SurveyID IS NOT NULL
BEGIN
DECLARE #TrainingID int
DECLARE #Title nvarchar(50)
DECLARE #StartDateTime nvarchar(50)
DECLARE #EndDateTime nvarchar(50)
DECLARE #FullName nvarchar(255)
DECLARE #EmailAddress nvarchar(255)
DECLARE #Description nvarchar(MAX)
--get the one record we are on...
SELECT
#TrainingID = t.TrainingID,
#Title = t.Title,
#StartDateTime = CAST(CONVERT(DATE, t.StartDate) AS VARCHAR(50)) + ' ' + CAST(t.StartTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.StartTimeMinutes)=1 THEN CAST(t.StartTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.StartTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.StartTimeAMorPM AS VARCHAR(50)),
#EndDateTime = CAST(CONVERT(DATE, t.EndDate) AS VARCHAR(50)) + ' ' + CAST(t.EndTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.EndTimeMinutes)=1 THEN CAST(t.EndTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.EndTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.EndTimeAMorPM AS VARCHAR(50)),
#Description = t.DescriptionHTML,
#FullName = u.FullName,
#EmailAddress = u.EmailAddress
FROM
Training t
INNER JOIN
TrainingAttendee ta
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[User] u
ON
u.UserID = ta.AttendeeID
WHERE
ta.TrainingAttendeeID = #TrainingAttendeeID
IF #EmailAddress IS NOT NULL
BEGIN
--Email goes out here....
END
--grab next one
SELECT
TOP 1
#AttendeeID = ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID=ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
(
--same where as original
(ta.AttendedTraining = 1)
AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
)
ORDER BY
ta.TrainingAttendeeID,
ta.AttendeeID,
ts.SurveyID
PRINT CAST('TrainingAttendeeID: ' + CAST(#TrainingAttendeeID as nvarchar(500)) + ' AttendeeID:' + CAST(#AttendeeID as nvarchar(500)) + ' SurveyID: ' + CAST(#SurveyID as nvarchar(500)) AS nvarchar(4000))
--insert into our temp table
INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
END
END
GO
Variables will not change if select does not return any records. I bet it processes last #AttendeeID round and round.
Another way to test it - add unique constraint to temp table. I assume cycle will fail once there are no more records to select.
One way to fix it - assign NULLs to all variables at the beginning of each iteration (at the top of while body). But I'd recommend to rewrite this code to cursor if possible (not sure what is the logic of several select statements).
Note that declaration of variables within code block makes no "block-scope" sense since it is not perl or python.

SQL query stored procedure

Create a stored procedure that passes in the SalesOrderID as a parameter.
This stored procedure will return the SalesOrderID, Date of the transaction, shipping date, city and state. It is not running
Ans:
Create PROCEDURE proc_findProductInfo
#SalesOrderID int,
#SalesOrderOut int OUTPUT,
#OrderDate datetime OUTPUT,
#ShipDate datetime OUTPUT,
#CityState varchar(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET #SalesOrderOut = #SalesOrderID
SET #OrderDate = (SELECT OrderDate FROM SALES.SalesOrderHeader )
SET #ShipDate = (SELECT ShipDate FROM Sales.SalesOrderHeader)
SET #CityState = (SELECT a.City, st.Name
FROM Sales.SalesOrderHeader s
INNER JOIN Person.Address a ON s.ShipToAddressID = a.AddressID
INNER JOIN Person.StateProvince st ON s.TerritoryID = st.TerritoryID
WHERE SalesOrderID = #SalesOrderID)
END
DECLARE #OrderNum int, #Date datetime, #qty1 int, #Date1 datetime
EXEC proc_findProductInfo 63936,
#SalesOrderOut = #OrderNum OUTPUT,
#OrderDate = #Date OUTPUT,
#ShipDate = #date1,
#CityState = #qty1 output
SELECT #OrderNum, #date, #qty1, #Date1
Error Message:
Msg 116, Level 16, State 1, Procedure proc_findProductInfo, Line 25
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS
You're making this way harder than it needs to be:
Create PROCEDURE proc_findProductInfo
#SalesOrderID int
AS
BEGIN
SET NOCOUNT ON;
SELECT s.SalesOrderID, s.OrderDate, s.ShipDate, a.City,st.Name
FROM Sales.SalesOrderHeader s
INNER JOIN Person.Address a ON s.ShipToAddressID = a.AddressID
INNER JOIN Person.StateProvince st ON s.TerritoryID=st.TerritoryID
WHERE s.SalesOrderID = #SalesOrderID
END
I'm not even sure you need the StateProvince table here... the question probably allows you to trust the Address record.
It is complaining about the following
SET #CityState = (
SELECT a.City,st.Name
You are selecting both City and State Name and trying to assign it to a variable.
You either need to concatenate or coalesce them into them into a single output or alternatively use the below type of select to assign each one to a variable.
select
#var1 = field1,
#var2 = field2,
...
As below
SET #SalesOrderOut = #SalesOrderID
SELECT #OrderDate = s.OrderDate,
#ShipDate = s.ShipDate,
#CityState = CONCAT(a.City, ", ", st.Name)
FROM Sales.SalesOrderHeader s
inner JOIN Person.Address a
ON s.ShipToAddressID = a.AddressID
inner JOIN Person.StateProvince st
on s.TerritoryID=st.TerritoryID
WHERE SalesOrderID = #SalesOrderID

SSRS monthly/daily report

I have a variable in my report which holds 2 possible values: 'monthly' and 'daily'. How can I put this variable (lets call it #reportModel). I think it should be somewhere in GROUP BY clause, but don't know how should it look like.
DECLARE #reportModel VARCHAR(10)
SET #reportModel = 'monthly'
SELECT P.product, SUM(O.price * O.quantity), O.orderDate
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
And what now?
How about a stored procedure to handle this, something like.....
CREATE PROCEDURE rpt_GetData
#reportModel VARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
IF (#reportModel = 'daily')
BEGIN
SET #Sql = N' SELECT P.product
, SUM(O.price * O.quantity) AS Total
, O.orderDate
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
GROUP BY P.product , O.orderDate'
Exec sp_executesql #Sql
END
ELSE IF (#reportModel = 'monthly')
BEGIN
SET #Sql = N' SELECT P.product
, SUM(O.price * O.quantity) AS Total
, MONTH(O.orderDate) AS [Month]
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
GROUP BY P.product, MONTH(O.orderDate)'
Exec sp_executesql #Sql
END
END
Adding this as an answer because I mentioned it in the comments in #M.Ali's answer.
So I would suggest you change thinking slightly with one of these options.
Two reports - Simply make a report for daily and another for monthly. Now you have no worries with complex SQL etc.
Make 2 stored procedures, one with the GROUP BY daily and one monthly. Then in your SSRS dataset, create an expression for you SQL that chooses the procedure based on parameter:
=IIf(Parameters!reportModel.Value = "monthly", "GetMonthlyData", "GetDailyData")
I would put it in the Group On Expression of the table or chart rather than doing it in the query.
=IIF(Parameters!reportModel.Value = "monthly", Month(Fields!orderDate.Value), Fields!orderDate.Value)
If you'd rather do it in the query and don't want to wait for DBAs to get around to deploying a Stored Procedure (not to mention maintaining it whenever there's a change), you could use your parameter in a CASE like:
SELECT P.product, SUM(O.price * O.quantity),
CASE WHEN #reportModel = 'monthly' THEN CAST(MONTH(O.orderDate) AS VARCHAR(12))
ELSE CAST(O.orderDateAS VARCHAR(12)) END AS DT
FROM WORKFLOW_SHARED.MAIN.VW_CLAIMSOVERPAYMENT
WHERE DATECOMPLETED > '7/1/2015'
GROUP BY P.product,
CASE WHEN #reportModel = 'monthly' THEN CAST(MONTH(O.orderDate) AS VARCHAR(12))
ELSE CAST(O.orderDateAS VARCHAR(12)) END
This way you don't have to maintain two separate reports.
How about something simple like this
select
P.product
,Total = sum(O.price * O.quantity)
, O.orderDate
from
Products as P
inner join Orders as O on P.ID = O.ID
where
#reportModel = 'daily'
union all
select
P.product
,Total = sum(O.price * O.quantity)
,[Month] = MONTH(O.orderDate)
from
Products as P
inner JOIN Orders as O on P.ID = O.ID
group by
P.product
,[Month] = MONTH(O.orderDate)
where
#reportModel = 'monthly'

Resources