Passing parameter to stored procedure using dynamic SQL into pivot data - sql

I am trying to pass a parameter into Stored procedure to filter data in my select statement but when i use the parameter it gives error Message: Invalid column name 'SessionId2075'. when I use static value in the where clause the procedure works fine. Can you please give me fix the issue. I checked all the previous answers and could not find the working solution.
Alter PROCEDURE [dbo].GetPivotFeeReport
(
#SessionId varchar(50)
)
as
begin
DECLARE #SQL as VARCHAR(MAX)
DECLARE #Columns as VARCHAR(MAX)
SET NOCOUNT ON;
SELECT #Columns =
COALESCE(#Columns + ', ','') + QUOTENAME(GroupHeaderValue)
FROM
(SELECT DISTINCT mgh.GroupHeaderValue
FROM StudentFeeDetail sf
INNER JOIN MasterGroupHeaderValue mgh
ON mgh.GroupHeaderValueId = sf.FeeForId
) AS B
ORDER BY B.GroupHeaderValue
SET #SQL = 'SELECT ClassName,' + #Columns + ',TOTAL
FROM
(
SELECT
distinct mc.className,
sf.FinalAmount,
mgh.GroupHeaderValue,
Sum (isnull(sf.FinalAmount,0)) over (partition by ClassName) AS TOTAL
--0 AS TOTAL
FROM StudentFeeDetail sf
INNER JOIN StudentAdmission sa
ON sa.AdmissionId = sf.AdmissionId
INNER JOIN MasterClass mc
ON mc.ClassId = sa.ClassId
INNER JOIN MasterGroupHeaderValue mgh
ON mgh.GroupHeaderValueId = sf.FeeForId
WHERE sa.SessionId = (' + #SessionId + ') -- this is where I am trying to use the parameter when used static value like this ''SessionId2075'' the procedure works fine
and sf.FeeAmt >0
GROUP BY className, FinalAmount, GroupHeaderValue
) as PivotData
PIVOT
(
sum(FinalAmount)
FOR GroupHeaderValue IN (' + #Columns + ')
) AS PivotResult
ORDER BY (ClassName)
'
EXEC ( #sql)
end

Related

Insertion failed when converting the nvarchar value to data type int in sql server

I have a XML transposing rows into column query. I am trying to insert the data to a table that is created dynamically. However, I am getting error saying conversion failed when converting nvarchar value to data type int when I execute '#query'
This is my current code:
DECLARE #cols NVARCHAR(MAX), #cols1 NVARCHAR(MAX), #query NVARCHAR(MAX);
SET #cols = STUFF(
(
SELECT
','+QUOTENAME(c.[destfieldname] ) + c.datatype
FROM #specnorm c
left join #rawtemp d on c.fieldname = d.fieldname
group by c.destfieldname, c.datatype
order by min(sourceSequenceTypical)
for xml path ('')),1,1,'')+ ', [sequenceID] int, [subsequenceID] int';
SET #cols1 = STUFF(
(
SELECT
','+QUOTENAME(c.[destfieldname] )
FROM #specnorm c
left join #rawtemp d on c.fieldname = d.fieldname
group by c.destfieldname
order by min(sourceSequenceTypical)
for xml path ('')),1,1,'')+ ', [sequenceID],
[subsequenceID] ';
SET #query = ' SELECT '+#cols1+' from (SELECT
c.[rownumber],
c.[contents],
d.[destfieldname]
FROM #rawtemp c
left join #specnorm d on c.fieldname = d.fieldname
)x pivot (max(contents) for destfieldname in ('+#cols1+')) p'
exec ('CREATE Table dbo.sample' + ' (' +#cols + ')');
exec ('Insert into dbo.sample' + #query)
try:
SET #query = 'SELECT '+#cols+' into YOURTABLE from (SELECT
[rownumber],
[contents],
[fieldname]
FROM #rawtemp
) x pivot (max(contents) for fieldname in ('+#cols+')) p'
it creates YOURTABLE on the fly

Dynamic SQL - Use declared VARCHAR in SET SQL string

How to use the declared variable #CodeID inside the SQL string? When I run following statement I get the "Invalid object name (..)" error.
WHILE #FolderID <= #FolderMaxID
BEGIN
SELECT #Db = Db
FROM #Folders
WHERE ID = #FolderID
SET #Sql = N'
DECLARE #CodeID NVARCHAR(256)
SELECT TOP(1) #CodeID=CodeType
FROM ' + #Db + '.bla.Field
WHERE Name= ''Example''
SELECT DISTINCT C.Name
FROM ' + #Db + '.Document
INNER JOIN ' + #Db + '.bla.Code_#CodeID C ON D.ID = C.ID'
EXEC ( #Sql )
SET #FolderID = #FolderID + 1
END
It looks to me that you need two levels of dynamic SQL, with the first level inserting the database name (from #folders), and the second level inserting a constructed table name (based on the CodeType column of the database-local bla.Field table).
I do not know of any way to parameterize database names or table names using sp_executesql, so I'm sticking with build-up dynamic SQL and EXEC (). (If someone makes a case for preferring sp_executesql over EXEC when not useing parameters, then it may be worth the switch.)
Try something like:
WHILE #FolderID <= #FolderMaxID
BEGIN
SELECT #Db = Db
FROM #Folders
WHERE ID = #FolderID
SET #Sql = N'
DECLARE #CodeID NVARCHAR(256)
SELECT TOP(1) #CodeID=CodeType
FROM ' + QUOTENAME(#Db) + '.bla.Field
WHERE Name= ''Example''
DECLARE #Sql2 NVARCHAR(MAX) = N''
SELECT DISTINCT C.Name
FROM ' + QUOTENAME(#Db) + '.bla.Document D
INNER JOIN ' + QUOTENAME(#Db) + '.bla.'' + QUOTENAME(''Code_'' + #CodeID) + '' C ON D.ID = C.ID
''
EXEC #sql2
'
EXEC ( #Sql )
SET #FolderID = #FolderID + 1
END
This implements dynamic SQL within dynamic SQL. Doubled quotes in the outer sql template become single quotes in the inner sql. The original posted code seemed to be missing a schema qualifier and alias for the Document table, so I inserted them ("bla" and "D"). I also added QUOTENAME around the injected names as suggested by Larnu.
The first level of dynamic sql would generate something like:
SELECT TOP(1) #CodeID=CodeType
FROM [db1].bla.Field
WHERE Name= 'Example'
DECLARE #Sql2 NVARCHAR(MAX) = N'
SELECT DISTINCT C.Name
FROM [db1].bla.Document D
INNER JOIN [db1].bla.' + QUOTENAME('Code_' + #CodeID) + ' C ON D.ID = C.ID
'
EXEC #sql2
The second level would generate something like:
SELECT DISTINCT C.Name
FROM [db1].bla.Document D
INNER JOIN [db1].bla.[Code_Table1] C ON D.ID = C.ID
Note that each loop iteration will generate a separate result. If you wish to combine results, you will need to define a #temp table, insert the individual results into that table, and then select the combined results at the end of your script.
Note that I haven't tested the specific code above, so it might need some debugging (add "PRINT #sql2" before the EXEC) if it doesn't work straight out.
ADDENDUM
Per #trenton-ftw comments below, an out parameter can be used to capture the result of the first query so that it may be included in the second query without the need for nesting. Two executions are still required. Below is a revised example.
DECLARE #Folders TABLE (ID INT IDENTITY(1,1), Db sysname)
INSERT #Folders VALUES ('db1'), ('db2')
DECLARE #SearchName NVARCHAR(256) = 'Example'
DECLARE #Db sysname
DECLARE #Sql NVARCHAR(MAX)
DECLARE #CodeID NVARCHAR(256)
DECLARE #FolderMaxID INT = (SELECT MAX(ID) FROM #Folders)
DECLARE #FolderID INT = 1
WHILE #FolderID <= #FolderMaxID
BEGIN
SELECT #Db = Db
FROM #Folders
WHERE ID = #FolderID
SET #Sql = N'
SET #CodeID = #SearchName + ''-Test''
--SELECT TOP(1) #CodeID = CodeType
--FROM ' + QUOTENAME(#Db) + '.bla.Field
--WHERE Name = #SearchName'
PRINT #Sql
EXEC sp_executesql #Sql,
N'#SearchName NVARCHAR(256), #CodeID NVARCHAR(256) OUTPUT',
#SearchName, #CodeID OUTPUT
SET #Sql = N'
--SELECT DISTINCT C.Name
--FROM ' + QUOTENAME(#Db) + '.bla.Document D
-- INNER JOIN ' + QUOTENAME(#Db) + '.bla.' + QUOTENAME('Code_' + #CodeID) + ' C ON D.ID = C.ID'
PRINT #Sql
EXEC sp_executesql #sql
SET #FolderID = #FolderID + 1
END
For demo purposes, I also parameterized the search name as an input parameter and added some temporary code to make it stand-alone testable. A final version would uncomment the actual sql, and remove the print statements and the test #CodeID assignemnt.

How to create temp table with dynamic SQL query result

I have this stored procedure:
Declare #MarketID AS NVARCHAR(MAX) = '1.136529848';
Declare #UserID AS NVARCHAR(MAX) = '6a309d84-d1c6-434d-b9df-4f96a74da912';
DECLARE #colsSelect AS NVARCHAR(MAX);
DECLARE #colsTemp AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT
#colsSelect = STUFF((SELECT distinct ',' +
'''''' + ' as ' + QUOTENAME(name)
FROM RunnersInfoes AS t
WHERE marketID = #MarketID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') , 1, 1, '');
PRINT #colsSelect
SET #query= ';WITH cte AS
(
SELECT
id, ParentId, 0 AS Level, Share, AccountTypeName, FirstName
FROM
dbo.View_UserProfile
WHERE
View_UserProfile.id = ' + '''' + #UserID + '''' +'
UNION ALL
SELECT
t.id, t.ParentId, Level + 1 AS Level, t.Share, t.AccountTypeName, t.FirstName
FROM
View_UserProfile t
INNER JOIN
cte ON t.ParentId = cte.id
)
SELECT
ID, AccountTypeName AS Type, FirstName AS Name, ' + #colsSelect + '
FROM cte AS t'
EXECUTE (#query)
and it's generating this result:
I want to create temp table or variable type table for following result , remember the column of this result are dynamically rendered. Sometimes result returns more columns and sometimes with less but first 3 columns remain the same for every result. So kindly help for creating dynamic table inside the stored procedure.
You can do:
SELECT ID
, AccountTypeName AS Type
, FirstName AS Name
, ' + #colsSelect + '
INTO ##TEMPTABLE
FROM cte AS t
Since you execute this dynamically, you cannot use #TEMPTABLE because a local temp table will only exist in the scope of the query that defines it. Using ## creates a global temp table which will be accessible outside the scope of the dynamic query.
Please use the SELECT - INTO clause for your use case as given below
SELECT * INTO #temptable FROM cte
To create a temp table that is filled by a dynamic query, use global temp tables like this example.
For the select ... into ... statement to work, you need to make sure every column from the select has a name.
declare #query varchar(1000) = 'select 1 as ID, ''test'' as Column_1 into ##mytable'
exec (#Query)
select * from ##mytable
drop table ##mytable
Do not forget to drop the temp table when your done.

How do I use loop to generate column names dynamically?

I have table sdata and it has 35 columns (id, name, TRx1, TRx2, TRx3, TRx4,..., TRx30, city, score, total)
I want to fetch data from the TRx1,...TRx30 columns.
Can I use loop here?
I did following code:
DECLARE #flag INT
DECLARE #sel varchar(255)
DECLARE #frm varchar(255)
SET #flag = 1;
SET #sel = 'select TRx';
SET #frm = ' from sdata';
exec(#sel +
(WHILE #flag <=5
#flag
SET #flag = #flag + 1)
+ #frm)
What wrong am I doing? And how can I resolve this?
If your table name is sdata, this code should work for you:
-- Grab the names of all the remaining columns
DECLARE #sql nvarchar(MAX);
DECLARE #columns nvarchar(MAX);
SELECT #columns = STUFF ( ( SELECT N'], [' + name
FROM sys.columns
WHERE object_id = (select top 1 object_id FROM sys.objects where name = 'sdata')
AND name LIKE 'TRx%' -- To limit which columns
ORDER BY column_id
FOR XML PATH('')), 1, 2, '') + ']';
PRINT #columns
SELECT #sql = 'SELECT ' + #columns + ' FROM sdata';
PRINT #sql;
EXEC (#sql);
Note I included PRINT statements so you could see what's going on. You might want to comment out the EXEC while testing.
This would be much easier to do by just copy/pasting the column names and changing them to be the correct one. However if you must do it this way, I do not advise using a loop at all. This method uses a tally table to generate the columns you want to select (in this example, columns 1 through 30, but that can be changed), then generates a dynamic SQL statement to execute against the SData table:
Declare #From Int = 1,
#To Int = 30,
#Sql NVarchar (Max)
Declare #Columns Table (Col Varchar (255))
;With Nums As
(
Select *
From (Values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) As V(N)
), Tally As
(
Select Row_Number() Over (Order By (Select Null)) As N
From Nums A --10
Cross Join Nums B --100
Cross Join Nums C --1000
)
Insert #Columns
Select 'TRx' + Cast(N As Varchar)
From Tally
Where N Between #From And #To
;With Cols As
(
Select (
Select QuoteName(Col) + ',' As [text()]
From #Columns
For Xml Path ('')
) As Cols
)
Select #Sql = 'Select ' + Left(Cols, Len(Cols) - 1) + ' From SData'
From Cols
--Select #Sql
Execute (#Sql)
Note: The --Select #Sql section is there to preview the generated query before executing it.
You can select the column names like this:
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'my name here'

Writing a dynamic SQL function

I am having a few problems trying to return a table from a SQL function, where the SQL to create the table is written dynamically.
So far I have:
CREATE FUNCTION dbo.SEL_PCD
(
#COBDate AS DATETIME,
#FileName AS VARCHAR(50),
#PC AS VARCHAR(50),
#MyList AS VARCHAR(max),
DECLARE #SQL VARCHAR(max)
SET #SQL = 'SELECT * FROM
(SELECT tab1.TID FROM
(SELECT TID FROM dbo.SEL_RT('+#COBDate+','+#FileName+') WHERE BID IN ('+ #MyList +')) tab1
JOIN
(SELECT TID FROM CT WHERE (Col_Name LIKE %' + #PC + '% OR Bk LIKE %' + #PC + '%) AND FileName = ' + #FileName + ' AND COBDate = #COBDate) tab2
ON tab1.TID = tab2.TID) tab3
JOIN
(SELECT TID, Value FROM CR WHERE BID IN (' + #MyList + ') AND COBDate = ' + #COBDate + ' AND FileName = ' + #FileName + 'AND ScenID = 266) tab7
ON tab3.TID = tab7.TID'
)
RETURNS TABLE AS
RETURN
(
EXEC sp_executesql #SQL
)
GO
I am getting errors declaring the SQL variable. Am I ok to return the table via the execute command?
Have a try with this one:
CREATE PROCEDURE dbo.SEL_PCD
(
#COBDate DATETIME,
#FileName VARCHAR(50),
#PC VARCHAR(50),
#MyList VARCHAR(max)
) AS
DECLARE #SQL VARCHAR(max)
SELECT #SQL = 'SELECT * FROM
(SELECT tab1.TID FROM
(SELECT TID FROM dbo.SEL_RT('+#COBDate+','+#FileName+') WHERE BID IN ('+ #MyList +')) tab1
JOIN
(SELECT TID FROM CT WHERE (Col_Name LIKE %' + #PC + '% OR Bk LIKE %' + #PC + '%) AND FileName = ' + #FileName + ' AND COBDate = #COBDate) tab2
ON tab1.TID = tab2.TID) tab3
JOIN
(SELECT TID, Value FROM CR WHERE BID IN (' + #MyList + ') AND COBDate = ' + #COBDate + ' AND FileName = ' + #FileName + 'AND ScenID = 266) tab7
ON tab3.TID = tab7.TID'
EXEC(#SQL)
Functions
can be used with Select statement
Not returning output parameter but returns Table variables
You can join UDF
Cannot be used to change server configuration
Cannot be used with XML FOR clause
Cannot have transaction within function
Stored Procedure
have to use EXEC or EXECUTE
return output parameter
can create table but won’t return Table Variables
you can not join SP
can be used to change server configuration
can be used with XML FOR Clause
can have transaction within SP
You can't call stored procedures from within a function, including the stored procedures EXECUTE or SP_EXECUTESQL. This means that you can't have dynamic sql embedded within a function.
The reason you can't call stored procedures is because functions are not allowed to have side-effects (calling them can't in itself change any data - they can't insert, update or delete). But stored procedures can. This means that a function that calls a stored procedure would suddenly become able to have side-effects.
SP's can call Functions, not the other way around.
Also, SQL is compiled to an execution plan. At that time the tables and indexes that are being used all become fixed. If a function includes dynamic sql that would be possible; the tables, etc, that are to be used are not known at compile time, and SQL does not have that capability.
In your case the only part of your query that seems to need Dynamic SQL is that you are passing a comma delimited list as the #myList parameter. There is, however, an alternative approach.
Look for one of the many dbo.fn_split() functions that are available on line (and many on SO). Then use that function to join on the data...
CREATE FUNCTION dbo.SEL_PCD( #COBDate AS DATETIME,
#FileName AS VARCHAR(50),
#PC AS VARCHAR(50),
#MyList AS VARCHAR(max)
)
RETURNS TABLE
AS
RETURN
SELECT
CR.TID,
CR.Value
FROM
dbo.SEL_RT(#COBDate, #FileName) AS RT
INNER JOIN
CT
ON CT.TID = RT.TID
INNER JOIN
CR
ON CR.TID = RT.TID
WHERE
(CT.Col_Name LIKE '%'+#PC+'%' OR CT.Bk LIKE '%'+#PC+'%')
AND CT.FileName = #FileName
AND CT.COBDate = #COBDate
AND CR.FileName = #FileName
AND CR.COBDate = #COBDate
AND CR.ScenID = 266
AND RT.BID IN (SELECT id FROM dbo.fn_split(#myList, ',') AS my_list)
AND CR.BID IN (SELECT id FROM dbo.fn_split(#myList, ',') AS my_list)
you cannot use execute command in function
CREATE PROCEDURE dbo.SEL_PCD4
(
#COBDate AS DATETIME,
#FileName AS VARCHAR(50),
#PC AS VARCHAR(50),
#MyList AS VARCHAR(max)
) AS
DECLARE #SQL VARCHAR(max)
SET #SQL = 'SELECT * FROM
(SELECT tab1.TID FROM
(SELECT TID FROM dbo.SEL_RT('+#COBDate+','+#FileName+') WHERE BID IN ('+ #MyList +')) tab1
JOIN
(SELECT TID FROM CT WHERE (Col_Name LIKE %' + #PC + '% OR Bk LIKE %' + #PC + '%) AND FileName = ' + #FileName + ' AND COBDate = #COBDate) tab2
ON tab1.TID = tab2.TID) tab3
JOIN
(SELECT TID, Value FROM CR WHERE BID IN (' + #MyList + ') AND COBDate = ' + #COBDate + ' AND FileName = ' + #FileName + 'AND ScenID = 266) tab7
ON tab3.TID = tab7.TID'
EXEC sp_executesql #SQL
GO