Stored Procedure with multi value parameter behaving strangely - sql

I created a stored procedure in sql server to feed SSRS to allow it to accept Multiple values.
I have created it and when I used it in my report or execute it in sql server I have the following error message. Is there anything i am missing? Thanks
Msg 207, Level 16, State 1, Line 35
Invalid column name 'London'.
This is my sample data. feel free to create table with it
DECLARE #MyTables AS TABLE (ID INT, City VARCHAR(100))
INSERT INTO #MyTables VALUES
(1,'London'),
(2,'Chester'),
(3,'Luton'),
(4,'New York'),
(1,'London'),
(2,'Chester'),
(5,'Paris'),
(5,'Paris'),
(2,'Chester'),
(2,'Chester')
SELECT * FROM #MyTables
This is my code for the dynamic stores procedure
CREATE PROCEDURE dbo.CitiesGroup
#Cities NVARCHAR(Max) -- this are the parameters
AS
BEGIN
DECLARE #sqLQuery VARCHAR(MAX)
Declare #AnswersTempTable Table
( ID INT,
City VARCHAR(250)
)
SET #sqlQuery =
'SELECT
ID,
City
FROM MyTables
where Convert(nvarchar(Max),City) IN ('+#Cities+')
Insert into #AnswersTempTable
exec (#sqlQuery)
select * from #AnswersTempTable'
END
Thanks
EXEC dbo.CitiesGroup 'London'
Error meg
Msg 207, Level 16, State 1, Line 32
Invalid column name 'London'.

There is another way to do this. Instead of passing the values into a dynamic query, why not split the parameter using a function? This article written by Aaron Bertrand demonstrates different ways on how to split string in sql server.
Once you have selected one of the functions, you can simply rewrite your stored procedure without creating a dynamic query inside.
CREATE PROCEDURE dbo.CitiesGroup
#Cities NVARCHAR(Max) -- this are the parameters
AS
BEGIN
-- simplified query
-- write your complex logic here
SELECT ID, City
FROM MyTables
WHERE City IN (SELECT Item FROM dbo.SplitStrings_CTE(#Cities, N',');)
END
Usage:
EXEC dbo.CitiesGroup 'London'
GO
EXEC dbo.CitiesGroup 'London,New York,Paris'
GO
Useful Link:
Split strings the right way – or the next best way

Alternatively, if you don't need to use a stored proc, you can simply put your query directly in the dataset as
SELECT ID, City
FROM MyTables
WHERE City IN (#Cities)
There is no need for dynamic sql as SSRS will do this for you. Just makes sure the SSRS parameter name and the Variable in your SELECT statement are identical (case-sensitive)

Related

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.

How to store select statement result to table variable in sql server

Declare #T_variable table(name varchar(200))
SET #T_variable =(SELECT au_lname FROM Testing)
Error Message. Msg 137, Level 16, State 1, Line 2 Must declare the
scalar variable "#T_variable".
Note :- select statement result will give multiple rows.
I try to capture the select result in table variable.But i failed.
Is there any way to capture the select result to Table variable Dynamically.
Thanks in advance.
Please try below query instead since you have declared a table variable instead of a datatype variable.
Declare #T_variable table(name varchar(200))
insert into #T_variable
SELECT au_lname FROM Testing
Try this:
SET #T_variable :=(SELECT au_lname FROM Testing)
Adding a colon may help here.

Getting error trying to create stored procedure using type table

Trying to create a stored procedure like this:
CREATE PROCEDURE Students
#List StudentList READONLY,
#AdmissionId VARCHAR(max)
AS
BEGIN
INSERT INTO RcordsTable (StudentId, RollNumber, AdmissionId)
VALUES(#List.StudentId, #List.RollNumber, #AdmissionId)
END
Where List is type valued table which is already created in the database with 3 columns StudentList, RollNumber and Id.
When I try to create this procedure in SQL Server 2008, I am getting following error:
Must declare the scalar variable #List
You're working in T-SQL - not in C# here! The #List parameter needs to be treated like a T-SQL table variable - not like a C# object....
So you need to use T-SQL style statements - like SELECT from the table-valued parameter #List - not C# style "dot-notation" (like when accessing the fields of a .NET object):
CREATE PROCEDURE Students
#List StudentList READONLY,
#AdmissionId VARCHAR(max)
AS
BEGIN
INSERT INTO RcordsTable (StudentId, RollNumber, AdmissionId)
SELECT
StudentId, RollNumber, #AdmissionId
FROM #List
END
Side note: why is an AdmissionId defined as a Varchar(max) type?? Id sounds numeric to me --> use an appropriate numeric type. And if it's not numeric - then you should define and use a sensible length for the VARCHAR - don't just get overly lazy and use VARCHAR(MAX) for everything - it's not a good idea!
Read this excellent article for more details on that topic: What's the Point of Using VARCHAR(n) Anymore?

How to create Temporary table with Dynamic query in SQL Server 2005

I want to create Temporary table dynamically in SQL Server 2005 like below.
Create Table ##EmpMonthlyTimeReport
(
EmpID UNIQUEIDENTIFIER,
EmpName VARCHAR(100),
TaskId UNIQUEIDENTIFIER,
[07 Nov] NVARCHAR(10),
[08 Nov] NVARCHAR(10),
[09 Nov] NVARCHAR(10)
)
In the above ##EmpMonthlyTimeReport table, columns [07 Nov], [08 Nov], [09 Nov] are NOT static. They are creating dynamically through another function.
Hence, I am dynamically constructing the above table in one variable called #EmpMonthlyTimeReport and I'm executing the constructed sql string like below:
EXEC(#EmpMonthlyTimeReport)
I am getting the following error:
Msg 50000, Level 16, State 1, Procedure SVS_WorkOnWeekends, Line 157
The name 'INSERT INTO ##EmpMonthlyTimeReport(EmpID, EmpName, TaskId)
SELECT EmpId, EmpName, TaskId FROM TableData
SELECT * FROM ##EmpMonthlyTimeReport
DROP TABLE ##EmpMonthlyTimeReport' is not a valid identifier.
Very silly work around.
I have changed the #EmpMonthlyTimeReport variable declaration from VARCHAR(MAX) to NVARCHAR(MAX)
That's it. Everything is working as expected.

SQL Server - Replacing Single Quotes and Using IN

I am passing a comma-delimited list of values into a stored procedure. I need to execute a query to see if the ID of an entity is in the comma-delimited list. Unfortunately, I think I do not understand something.
When I execute the following stored procedure:
exec dbo.myStoredProcedure #myFilter=N'1, 2, 3, 4'
I receive the following error:
"Conversion failed when converting the varchar value '1, 2, 3, 4' to data type int."
My stored procedure is fairly basic. It looks like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter nvarchar(512) = NULL
AS
SET NOCOUNT ON
BEGIN
-- Remove the quote marks so the filter will work with the "IN" statement
SELECT #myFilter = REPLACE(#myFilter, '''', '')
-- Execute the query
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
t.ID IN (#myFilter)
ORDER BY
t.Name
END
How do I use a parameter in a SQL statement as described above? Thank you!
You could make function that takes your parameter, slipts it and returns table with all the numbers in it.
If your are working with lists or arrays in SQL Server, I recommend that you read Erland Sommarskogs wonderful stuff:
Arrays and Lists in SQL Server 2005
You need to split the string and dump it into a temp table. Then you join against the temp table.
There are many examples of this, here is one at random.
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx
Absent a split function, something like this:
CREATE PROCEDURE [dbo].[myStoredProcedure]
#myFilter varchar(512) = NULL -- don't use NVARCHAR for a list of INTs
AS
SET NOCOUNT ON
BEGIN
SELECT
t.ID,
t.Name
FROM
MyTable t
WHERE
CHARINDEX(','+CONVERT(VARCHAR,t.ID)+',',#myFilter) > 0
ORDER BY
t.Name
END
Performance will be poor. A table scan every time. Better to use a split function. See: http://www.sommarskog.se/arrays-in-sql.html
I would create a function that takes your comma delimited string and splits it and returns a single column table variable with each value in its own row. Select that column from the returned table in your IN statement.
I found a cute way of doing this - but it smells a bit.
declare #delimitedlist varchar(8000)
set #delimitedlist = '|1|2|33|11|3134|'
select * from mytable where #delimitedlist like '%|' + cast(id as varchar) + '|%'
So... this will return all records with an id equal to 1, 2, 33, 11, or 3134.
EDIT:
I would also add that this is not vulnerable to SQL injection (whereas dynamic SQL relies on your whitelisting/blacklisting techniques to ensure it isn't vulnerable). It might have a performance hit on large sets of data, but it works and it's secure.
I have a couple of blog posts on this as well, with a lot of interesting followup comments and dialog:
More on splitting lists
Processing list of integers