How to pass multiple values to single parameter in stored procedure - sql

I'm using SSRS for reporting and executing a stored procedure to generate the data for my reports
DECLARE #return_value int
EXEC #return_value = [dbo].[MYREPORT]
#ComparePeriod = 'Daily',
#OverrideCompareDate = NULL,
#PortfolioId = '5,6',
#OverrideStartDate = NULL,
#NewPositionsOnly = NULL,
#SourceID = 13
SELECT 'Return Value' = #return_value
GO
In the above when I passed #PortfolioId = '5,6' it is giving me wrong inputs
I need all records for portfolio id 5 and 6 also is this correct way to send the multiple values ?
When I execute my reports only giving #PortfolioId = '5' it is giving me 120 records
and when I execute it by giving #PortfolioId = '6' it is giving me 70 records
So when I will give #PortfolioId = '5,6' it should have to give me only 190 records altogether, but it is giving me more no of records I don't understand where I exactly go wrong .
Could anyone help me?
thanks
all code is too huge to paste , i'm pasting relevant code please suggest clue.
CREATE PROCEDURE [dbo].[GENERATE_REPORT]
(
#ComparePeriod VARCHAR(10),
#OverrideCompareDate DATETIME,
#PortfolioId VARCHAR(50) = '2', --this must be multiple
#OverrideStartDate DATETIME = NULL,
#NewPositionsOnly BIT = 0,
#SourceID INT = NULL
) AS
BEGIN
SELECT
Position.Date,
Position.SecurityId,
Position.Level1Industry,
Position.MoodyFacilityRating,
Position.SPFacilityRating,
Position.CompositeFacilityRating,
Position.SecurityType,
Position.FacilityType,
Position.Position
FROM
Fireball_Reporting.dbo.Reporting_DailyNAV_Pricing POSITION WITH (NOLOCK, READUNCOMMITTED)
LEFT JOIN Fireball.dbo.AdditionalSecurityPrice ClosingPrice WITH (NOLOCK, READUNCOMMITTED) ON
ClosingPrice.SecurityID = Position.PricingSecurityID AND
ClosingPrice.Date = Position.Date AND
ClosingPrice.SecurityPriceSourceID = #SourceID AND
ClosingPrice.PortfolioID IN (
SELECT
PARAM
FROM
Fireball_Reporting.dbo.ParseMultiValuedParameter(#PortfolioId, ',') )

This can not be done easily. There's no way to make an NVARCHAR parameter take "more than one value". What I've done before is - as you do already - make the parameter value like a list with comma-separated values. Then, split this string up into its parts in the stored procedure.
Splitting up can be done using string functions. Add every part to a temporary table. Pseudo-code for this could be:
CREATE TABLE #TempTable (ID INT)
WHILE LEN(#PortfolioID) > 0
BEGIN
IF NOT <#PortfolioID contains Comma>
BEGIN
INSERT INTO #TempTable VALUES CAST(#PortfolioID as INT)
SET #PortfolioID = ''
END ELSE
BEGIN
INSERT INTO #Temptable VALUES CAST(<Part until next comma> AS INT)
SET #PortfolioID = <Everything after the next comma>
END
END
Then, change your condition to
WHERE PortfolioId IN (SELECT ID FROM #TempTable)
EDIT
You may be interested in the documentation for multi value parameters in SSRS, which states:
You can define a multivalue parameter for any report parameter that
you create. However, if you want to pass multiple parameter values
back to a data source by using the query, the following requirements
must be satisfied:
The data source must be SQL Server, Oracle, Analysis Services, SAP BI
NetWeaver, or Hyperion Essbase.
The data source cannot be a stored procedure. Reporting Services does
not support passing a multivalue parameter array to a stored
procedure.
The query must use an IN clause to specify the parameter.
This I found here.

I spent time finding a proper way. This may be useful for others.
Create a UDF and refer in the query -
http://www.geekzilla.co.uk/view5C09B52C-4600-4B66-9DD7-DCE840D64CBD.htm

USE THIS
I have had this exact issue for almost 2 weeks, extremely frustrating but I FINALLY found this site and it was a clear walk-through of what to do.
http://blog.summitcloud.com/2010/01/multivalue-parameters-with-stored-procedures-in-ssrs-sql/
I hope this helps people because it was exactly what I was looking for

Either use a User Defined Table
Or you can use CSV by defining your own CSV function as per This Post.
I'd probably recommend the second method, as your stored proc is already written in the correct format and you'll find it handy later on if you need to do this down the road.
Cheers!

I think, below procedure help you to what you are looking for.
CREATE PROCEDURE [dbo].[FindEmployeeRecord]
#EmployeeID nvarchar(Max)
AS
BEGIN
DECLARE #sqLQuery VARCHAR(MAX)
Declare #AnswersTempTable Table
(
EmpId int,
EmployeeName nvarchar (250),
EmployeeAddress nvarchar (250),
PostalCode nvarchar (50),
TelephoneNo nvarchar (50),
Email nvarchar (250),
status nvarchar (50),
Sex nvarchar (50)
)
Set #sqlQuery =
'select e.EmpId,e.EmployeeName,e.Email,e.Sex,ed.EmployeeAddress,ed.PostalCode,ed.TelephoneNo,ed.status
from Employee e
join EmployeeDetail ed on e.Empid = ed.iEmpID
where Convert(nvarchar(Max),e.EmpId) in ('+#EmployeeId+')
order by EmpId'
Insert into #AnswersTempTable
exec (#sqlQuery)
select * from #AnswersTempTable
END

Related

Nvarchar value overflowing an int column where there is no int column?

How is my nvarchar value overflowing an int column when I'm not defining any int values?
I'm getting the following error when I run my stored procedure:
The conversion of the nvarchar value '17191925814' overflowed an int column.
The statement I was trying to execute:
EXECUTE [dbo].[updateUser] #status = 'active', ..... ,#srcID = '17191925814'
The problem is that I never define that value as an int. It is always handled as a string, as far as I can tell. In my stored procedure the #srcID parameter is defined as an nvarchar(255):
CREATE PROCEDURE [dbo].[updateUser] ... , #srcID nvarchar(255), ...
And within that stored procedure, I try to UPDATE a value in a column that is also defined as nvarchar(255):
IF #srcID NOT IN ('', '0') AND #srcID IS NOT NULL
UPDATE [dbo].[Users] SET [Source System ID] = #srcID WHERE ...
ELSE IF #srcID = '0'
UPDATE [dbo].[Users] SET [Source System ID] = '' WHERE ...
The target table:
CREATE TABLE [dbo].[Users] ( ..., [Source System ID] [nvarchar](255) NULL, ...)
Where, or why, is SQL Server trying to convert '17191925814' to an int, and how can I prevent it?
Edit: I've included the full code of the stored procedure where the parameter #srcID appears.
This doesn't want to be specific answer (I agree with Gordon, Zohar and Damien), but should be difficult to write in a comment.
Follow a "reduced" procedure to show the case.
I hope it can help you as a method when you have similar problems (always try to reduce the scope), and serve as a confirmation of what Gordon, Zohar and other (I know, there is no need of confirmation as they have great reputation :-) already said:
CREATE TABLE X ( SourceSystemID nvarchar(255) NULL);
INSERT INTO X VALUES ('17191925814');
GO
CREATE PROCEDURE Upd_X #srcID NVARCHAR(255)
AS
BEGIN
IF #srcID NOT IN ('', '0') AND #srcID IS NOT NULL UPDATE X SET SourceSystemID = #srcID
ELSE IF #srcID = '0' UPDATE X SET SourceSystemID = ''
END
;
GO
SELECT * FROM X
EXEC Upd_X '17191925814';
SELECT * FROM X;
Output:
SourceSystemID
---------------
17191925814
SourceSystemID
-----------------------------------------
17191925814
The problem, as pointed out by Damian in the comments, was that I had a trigger active on the database, that was copying that data into an integer column.
So if you have this same issue: check triggers on your database!

Difficulty printing one particular query in MSSQL

I'm trying to construct a small query which will pull data from individual fields in a DB and print them in a human readable list format (it's what the operators are used to seeing). The code I have here is far from complete but It seems to me that it should work.
DECLARE #PSUCARD VARCHAR(20)
DECLARE #EQUIPMENT VARCHAR(50)
DECLARE #T1 VARCHAR
SET #PSUCARD = 'PSU-888'
SET #EQUIPMENT = '123_POUCH'
PRINT #PSUCARD + ':'
PRINT #EQUIPMENT
PRINT ''
IF (SELECT TEMPERATURE_MAIN FROM PSU WHERE PSU.PART_ID = #PSUCARD AND PSU.OPERATION_RESOURCE_ID = #EQUIPMENT)IS NOT NULL BEGIN
SET #T1 = (SELECT TEMPERATURE_MAIN FROM PSU WHERE PSU.PART_ID = #PSUCARD AND PSU.OPERATION_RESOURCE_ID = #EQUIPMENT)
PRINT 'Temperature: ' + #T1
--(SELECT TEMPERATURE_MAIN FROM PSU WHERE PSU.PART_ID = #PSUCARD AND PSU.OPERATION_RESOURCE_ID = #EQUIPMENT)
END
If I execute the code as is, #T1 returns a * rather than a value. If I remove comments from the line below I am reassured that there is indeed a value there.
I have other code very similar to this which works fine. Any ideas?
Also, I don't know if this helps in diagnosing the problem, but despite the temperature field in the DB being an INT, I get a conversion message if I try to treat #T1 an an INT.
This is because you declared #T1 as VARCHAR without a length. According to this:
When n is not specified in a data definition or variable declaration
statement, the default length is 1. When n is not specified when using
the CAST and CONVERT functions, the default length is 30.
You should always specify a length when declaring a VARCHAR variable:
DECLARE #T1 VARCHAR(50)
You need to give length for varchar datatype else it is going to take only one character
DECLARE #T1 VARCHAR(50)

SQL query help - declaration of variables within a function

I'm trying to write a SQL function but an having problems with declaring the variables I need for use in the WHERE clause.
Here's the code:
CREATE FUNCTION fn_getEmployeePolicies(#employeeid smallint)
RETURNS TABLE
AS
DECLARE #empLoc varchar
DECLARE #empBusA varchar
DECLARE #empType varchar
#empLoc = SELECT Location FROM fn_getEmployeeDetails(#employeeid)
#empBusA = SELECT BusinessArea FROM fn_getEmployeeDetails(#employeeid)
#empType = SELECT Type FROM fn_getEmployeeDetails(#employeeid)
RETURN select PolicyId, PolicyGroupBusinessArea.BusinessArea, policysignoff.PolicyGroupLocation.Location, policysignoff.PolicyGroupEmployeeType.EmployeeType
from policysignoff.PolicyGroupPolicy
LEFT JOIN policysignoff.PolicyGroupBusinessArea on policysignoff.PolicyGroupBusinessArea.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupLocation on policysignoff.PolicyGroupLocation.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupEmployeeType on policysignoff.PolicyGroupEmployeeType.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
where BusinessArea = #empBusA
AND EmployeeType = #empType
AND Location = #empLoc
GO
The logic I am trying to build in is:
'given an employeeId, return all "applicable" policies'
An "Applicable" policy is one where the Business Area, Location and EmployeeType match that of the user.
I am trying to use another function (fn_getEmployeeDetails) to return the BusArea, Loc & EmpType for the given user.
Then with the results of that (stored as variables) I can run my select statement to return the policies.
The problem i am having is trying to get the variables declared correctly within the function.
Any help or tips would be appreciated.
Thanks in advance!
Without knowing what your error actually is, I can only say that you're properly not after using varchar as datatype without specifying length.
DECLARE #empLoc varchar will declare a varchar with length 1.
Chances are it should be something like varchar(255) or similar.
Second to set variables you'll either need to use SET and use paranthisis for selects or set it into the statement:
SET #empLoc = (SELECT Location FROM fn_getEmployeeDetails(#employeeid))
or
SELECT #empLoc = Location FROM fn_getEmployeeDetails(#employeeid)
There are subtle differences between these two methods, but for your purpose right now I don't think it's important.
EDIT:
Based on your comment you lack a BEGIN after AS, and an END before GO.
Basically - your function syntax is mixing up "inline" table function with "multi-statement" function.
Such a function "template" should look something like this:
CREATE FUNCTION <Table_Function_Name, sysname, FunctionName>
(
-- Add the parameters for the function here
<#param1, sysname, #p1> <data_type_for_param1, , int>,
<#param2, sysname, #p2> <data_type_for_param2, , char>
)
RETURNS
<#Table_Variable_Name, sysname, #Table_Var> TABLE
(
-- Add the column definitions for the TABLE variable here
<Column_1, sysname, c1> <Data_Type_For_Column1, , int>,
<Column_2, sysname, c2> <Data_Type_For_Column2, , int>
)
AS
BEGIN
-- Fill the table variable with the rows for your result set
RETURN
END
GO
(script taken from sql server management studio)

update table with dynamic sql query

For a project, we are using a table (named txtTable) that contains all the texts. And each column contains a different language (for example column L9 is English, column L7 is German, etc..).
TextID L9 L7 L16 L10 L12
------------------------------------------------------
26 Archiving Archivierung NULL NULL NULL
27 Logging Protokollierung NULL NULL NULL
28 Comments Kommentar NULL NULL NULL
This table is located in a database on a Microsoft SQL Server 2005. The big problem is that this database name changes each time the program is restarted. This is a behavior typically for this third-party program and cannot be changed.
Next to this database and on the same server is our own database. In this database are several tables that point to the textID for generating data for reporting (SQL Server Reporting Services) in the correct language. This database contains also a table "ProjectSettings" with some properties like the name of the texttable database, and the stored procedures to generate the reporting data.
The way we now are requesting the right texts of the right language from this table with the changing database name is by creating a dynamic SQL query and execute it in a stored procedure.
Now we were wondering if there is a cleaner way to get the texts in the right language. We were thinking about creating a function with the textID and the language as a parameter, but we cannot find a good way to do this. We thought about a function so we just can use it in the select statement, but this doesn’t work:
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #TextLibraryDatabaseName varchar(1000)
DECLARE #nvcSqlQuery varchar(1000)
-- get the report language database name
SELECT #TextLibraryDatabaseName = TextLibraryDatabaseName FROM ProjectSettings
SET #nvcSqlQuery = 'SELECT #ResultVar =' + #LanguageColumn + ' FROM [' + #TextLibraryDatabaseName + '].dbo.TXTTable WHERE TEXTID = ' + cast(#TextID as varchar(30))
EXEC(#nvcSqlQuery)
-- Return the result of the function
RETURN #ResultVar
END
Is there any way to work around this so we don’t have to use the dynamic sql in our stored procedures so it is only ‘contained’ in 1 function?
Thanks in advance & kind regards,
Kurt
Yes, it is possible with the help of synonym mechanism introduced with SQL Server 2005. So, you can create synonym during your setting up procedure based on data from ProjectSettings table and you can use it in your function. Your code will look something like this:
UPDATE: The code of function is commented here because it still contains dynamic SQL which does not work in function as Kurt said in his comment. New version of function is below this code.
-- Creating synonym for TXTTable table
-- somewhere in code when processing current settings
-- Suppose your synonym name is 'TextLibrary'
--
-- Drop previously created synonym
IF EXISTS (SELECT * FROM sys.synonyms WHERE name = N'TextLibrary')
DROP SYNONYM TextLibrary
-- Creating synonym using dynamic SQL
-- Local variables
DECLARE #TextLibraryDatabaseName varchar(1000)
DECLARE #nvcSqlQuery varchar(1000)
-- get the report language database name
SELECT #TextLibraryDatabaseName = TextLibraryDatabaseName FROM ProjectSettings
SET #nvcSqlQuery = 'CREATE SYNONYM TextLibrary FOR [' + #TextLibraryDatabaseName + '].dbo.TXTTable'
EXEC(#nvcSqlQuery)
-- Synonym created
/* UPDATE: This code is commented but left for discussion consistency
-- Function code
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #nvcSqlQuery varchar(1000)
SET #nvcSqlQuery = 'SELECT #ResultVar =' + #LanguageColumn + ' FROM TextLibrary WHERE TEXTID = ' + cast(#TextID as varchar(30))
EXEC(#nvcSqlQuery)
-- Return the result of the function
RETURN #ResultVar
END
*/
UPDATE This is one more attempt to solve the problem. Now it uses some XML trick:
-- Function code
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #XmlVar XML
-- Select required record into XML variable
-- XML has each table column value in element with corresponding name
SELECT #XmlVar = ( SELECT * FROM TextLibrary
WHERE TEXTID = #TextID
FOR XML RAW, ELEMENTS )
-- Select value of required element from XML
SELECT #ResultVar = Element.value('(.)[1]', 'varchar(255)')
FROM #XmlVar.nodes('/row/*') AS T(Element)
WHERE Element.value('local-name(.)', 'varchar(50)') = #LanguageColumn
-- Return the result of the function
RETURN #ResultVar
END
Hope this helps.
Credits to answerer of this question at Stackoverflow - How to get node name and values from an xml variable in t-sql
To me, it sounds like a total PITA... However, how large is this database of "words" you are dealing with. Especially if it is not changing much and remains pretty constant. Why not have on some normal cycle (such as morning), just have one dynamic query generated that queries the one that changes and synchronize it to a "standard" table name in YOUR database that won't change. Then, all your queries run against YOUR version and completely remove the constant dynamic queries every time. Yes there would need to be this synchronizing stored procedure to run, but if it can be run on a schedule, you should be fine, and again, how large is the table of "words" for proper language context.

SQL Delete Where Not In

I have a relation mapping table like this:
attributeid bigint
productid bigint
To clean relations that are not used any more, I want to delete all recors where productid = x and attributeid not in (#includedIds), like the following example:
#attributetypeid bigint,
#productid bigint,
#includedids varchar(MAX)
DELETE FROM reltable
WHERE productid = #productid AND
attributetypeid = #attributetypeid AND
attributeid NOT IN (#includedids);
When running the SQL with the includedids param containing more than 1 id - like this: 25,26 - I get a SqlException saying:
Error converting data type varchar to bigint.
And that is of course due to the , in that varchar(max) param...
How should I construct my delete statement to make it work?
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[ListToTable] (
/*
FUNCTION ListToTable
Usage: select entry from listtotable('abc,def,ghi') order by entry desc
PURPOSE: Takes a comma-delimited list as a parameter and returns the values of that list into a table variable.
*/
#mylist varchar(8000)
)
RETURNS #ListTable TABLE (
seqid int not null,
entry varchar(255) not null)
AS
BEGIN
DECLARE
#this varchar(255),
#rest varchar(8000),
#pos int,
#seqid int
SET #this = ' '
SET #seqid = 1
SET #rest = #mylist
SET #pos = PATINDEX('%,%', #rest)
WHILE (#pos > 0)
BEGIN
set #this=substring(#rest,1,#pos-1)
set #rest=substring(#rest,#pos+1,len(#rest)-#pos)
INSERT INTO #ListTable (seqid,entry) VALUES (#seqid,#this)
SET #pos= PATINDEX('%,%', #rest)
SET #seqid=#seqid+1
END
set #this=#rest
INSERT INTO #ListTable (seqid,entry) VALUES (#seqid,#this)
RETURN
END
Run that script in your SQL Server database to create the function ListToTable. Now, you can rewrite your query like so:
#attributetypeid bigint,
#productid bigint,
#includedids varchar(MAX)
DELETE FROM reltable
WHERE productid = #productid AND
attributetypeid = #attributetypeid AND
attributeid NOT IN (SELECT entry FROM ListToTable(#includedids));
Where #includedids is a comma delimited list that you provide. I use this function all the time when working with lists. Keep in mind this function does not necessarily sanitize your inputs, it just looks for character data in a comma delimited list and puts each element into a record. Hope this helps.
Joel Spolsky answered a very similar question here: Parameterize an SQL IN clause
You could try something similar, making sure to cast your attributetypeid as a varchar.
You can't pass a list as an parameter (AFAIK).
Can you rewrite the sql to use a subquery, something like this:
delete from reltable
WHERE productid = #productid AND
attributetypeid = #attributetypeid AND
attributeid NOT IN (select id from ... where ... );
?
That comma delimited list can be sent to a user defined function which will return it as a simple table. That table can then be queried by your NOT IN.
If you need the fn I can provide.. It's been about 5 yrs since I used sql much and I'll have to dust off that section of my brain..
Erland has the definitive guide for dealing with lists to table in SQL 2005, SQL 2008 gives you table based params.
On a side note I would avoid a NOT IN pattern for large lists, cause it does not scale, instead look at using left joins.