I'm trying to generate a subset that I need to construct a much larger query, it seems though that the declaration I have sets the variable to null (or doesn't set it at all) I've been trying to figure out where I am going wrong but print statements don't seem to work here. (hence my suspicion that they were NULL)
DECLARE #selectCols nvarchar(4000)
DECLARE #cols nvarchar(4000)
SET #selectCols =
'SELECT '+#cols+' = isnull('+#cols+' + '','', '') + ''['' + CONVERT(nvarchar, TimeIndex) + '']''
from (select top 6 timeindex from (select distinct top 6 TimeIndex from '+#db_id+'.[webhistory].[Prediction_FillpercentageDays]
order by TimeIndex desc)as a1 order by TimeIndex asc) as a2'
EXECUTE (#selectCols);
SELECT CAST('<root><![CDATA[' + #selectCols + ']]></root>' AS XML)
My question is: what am I missing? I feel like I am missing something incredibly simple. (Yes #db_id has been set)
In tsql, null is undefined and anything + null is null.
You haven't assigned a value to #cols and so your #selectCols returns null. Assign a value to #cols and try.
DECLARE #cols nvarchar(4000) = 'Assign your value here'
Related
I have a scalar value function that returns a VarChar(MAX) In my stored procedure I do this
declare #p_emailAddr varchar(MAX) = (select db.dbo.GetEmails(10))
If I do print #p_emailAddr it shows me it was populated with the correct information but the rest of the code doesn't work correctly using it. (I have no clue why, it doesn't make sense!)
Now if I change it like this
declare #p_emailAddr varchar(MAX) = 'test#email.com;'
The rest of my code works perfect as it should!
What is the difference between the two methods of setting #p_emailAddr that is breaking it?
This is get emails code
ALTER FUNCTION [dbo].[GetEmails](#p_SubID int)
RETURNS varchar(max)
AS
BEGIN
DECLARE #p_Emails varchar(max)
SELECT #p_Emails = COALESCE(#p_Emails + ';', '') + E.EmailAddress
FROM db.dbo.UserEmailAddr E JOIN
db.dbo.EmailSubscriptionUsers S on e.ClockNumber = s.Clock AND S.SubID = #p_SubID
SET #p_Emails = #p_Emails + ';'
RETURN #p_Emails
END
What's coming back from GetEmails(10)? varchar(max) is a string value and is expecting a single value. you could have a table variable or if dbo.getemails(10) is a table just join it where you're expecting to use #p_emailaddr
best
select *
from table1 t1
join dbo.GetEmails(10) e
on e.email = t1.email
alternative
create table #GetEmails (emails varchar(max))
insert into #GetEmails values ('email#test.com'), ('test#email.com')
declare #p_emailAddr table (emails varchar(max))
insert into #p_emailAddr(emails)
select *
from #GetEmails
select *
from #p_emailAddr
In SQL Server 2008 (TSQL), I've created a stored procedure like this:
CREATE PROCEDURE SP_1_10_2
AS
declare #mostValuableBook nvarchar(255)
SELECT #mostValuableBook = Name
FROM books
WHERE price =
( SELECT MAX(price)
FROM books
WHERE izd LIKE '%BHV%' );
return #mostValuableBook
GO
But, when I'm trying to execute it:
declare #x nvarchar(255)
EXECUTE #x = SP_1_10_2;
SELECT 'The most expensive BHV book:', #x AS 'Name'
GO
I'm getting an error:
Conversion failed when converting the nvarchar value 'Internet
Explorer 3 original' to data type int.
It seems like the problem is in the line
EXECUTE #x = SP_1_10_2;
Can you please tell me what's wrong? Why is it trying to convert to int?
RETURN cannot be used to return nvarchar / varchar such as you have. RETURN is used to return an integer, this can be expressed as some sort of status code 1=True / 0=False. Read more about return here: http://msdn.microsoft.com/en-us/library/ms174998.aspx
In your case, you simply need to use OUTPUT variables which is similiar to pass-by-ref in C# or C++. You pass the variable to the sproc, the sproc modifies it, and you get the expected results after a SELECT....
Change it so that your parameters becomes an output parameter:
CREATE PROCEDURE SP_1_10_2
#mostValueableBook nvarchar(255) output
AS
SELECT #mostValuableBook = Name
FROM books
WHERE price =
( SELECT MAX(price)
FROM books
WHERE izd LIKE '%BHV%' );
SELECT #mostValuableBook
GO
Call it like so:
DECLARE #theValBook nvarchar(255)
EXECUTE SP_1_10_2 #mostValuableBook = #theValBook output
Then you can say:
SELECT 'Most expensive book is', #theValBook
You can also create a function to return the value you desire instead of relying on numeric return codes. SQL Functions come in quite handy. See example below which returns the last name with the highest client id using the LIKE operator
Use MYDB
GO
CREATE Function fn_LastClientIdByName
(
#nameLike NVARCHAR(10)
)
RETURNS NVARCHAR(100)
AS
BEGIN
DECLARE #result nvarchar(100)
DECLARE #clientName NVARCHAR(100)
SELECT top 1 #clientName = [clientLast] + ' ' + [clientFirst]
FROM [dbo].[duiClientOnly]
WHERE clientLast like #nameLike + '%'
order by clid desc
select #result = #clientName
return #result
END
I'm trying to write a UDF to translate a string that is either a guid or a project code associated with that guid into the guid:
CREATE FUNCTION fn_user_GetProjectID
(
#Project nvarchar(50)
)
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier
BEGIN TRY
set #ProjectID = cast(#Project as uniqueidentifier)
END TRY
BEGIN CATCH
set #ProjectID = null
END CATCH
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID
END
This works fine if the above code is embedded in my Stored Procedures, but I'd like to make a function out of it so that I follow DRY.
When I try to create the Function, I get errors like this:
Msg 443, Level 16, State 14, Procedure fn_user_GetProjectID, Line 16
Invalid use of side-effecting or time-dependent operator in 'BEGIN TRY' within a function.
Does anyone have an idea how I can get around this error?
Edit: I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?
Apparently you can't use TRY-CATCH in a UDF.
According to this bug-reporting page for SQL Server:
Books Online documents this behaviour,
in topic "CREATE FUNCTION
(Transact-SQL)": "The following
statements are valid in a function:
[...] Control-of-Flow statements
except TRY...CATCH statements. [...]"
But they were giving hope for the future back in 2006:
However, this is a severe limitation
that should be removed in a future
release. You should post a suggestion
in this regard and I will
wholeheartedly vote for it.
From MSDN:
A column or local variable of
uniqueidentifier data type can be
initialized to a value in the
following ways:
By using the NEWID function.
By converting from a string constant
in the form
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
in which each x is a hexadecimal digit
in the range 0-9 or a-f.
For example,
6F9619FF-8B86-D011-B42D-00C04FC964FF
is a valid uniqueidentifier value.
You can use pattern matching to verify the string. Note that this won't work for specific encoding that reduces the size of the GUID:
declare #Project nvarchar(50)
declare #ProjectID uniqueidentifier
declare #HexPattern nvarchar(268)
set #HexPattern =
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]'
/* Take into account GUID can have curly-brackets or be missing dashes */
/* Note: this will not work for GUIDs that have been specially encoded */
set #Project = '{' + CAST(NEWID() AS VARCHAR(36)) + '}'
select #Project
set #Project = REPLACE(REPLACE(REPLACE(#Project,'{',''),'}',''),'-','')
/* Cast as uniqueid if pattern matches, otherwise return null */
if #Project LIKE #HexPattern
select #ProjectID = CAST(
SUBSTRING(#Project,1,8) + '-' +
SUBSTRING(#Project,9,4) + '-' +
SUBSTRING(#Project,13,4) + '-' +
SUBSTRING(#Project,17,4) + '-' +
SUBSTRING(#Project,21,LEN(#Project)-20)
AS uniqueidentifier)
select #ProjectID
I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?
Starting from SQL Server 2012 you could use TRY_CAST/TRY_CONVERT functions:
Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
CREATE FUNCTION fn_user_GetProjectID(#Project nvarchar(50))
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier = TRY_CAST(#Project as uniqueidentifier);
IF(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project;
END
return #ProjectID;
END
Not sure, but why not flip it around... at first glance I would simplify it like this:
select #ProjectID =
ISNULL((select ProjectID from Project where
projectcode = #Project)
,(cast #Project as uniqueidentifier))
If this doesn't provide enough error handling, I'm sure there's a better way to pre-check that the cast can work without using try/catch...
My brute force method was to create my own ToGuid() function that verifies it can be converted to a GUID first, if not, it returns null. It may not be very fast but it does the job, and it is probably faster to convert the guid if it is one than to try to look it up in the table. EDIT: I meant to give credit to this blog, where I got the basis of my code for this function: http://jesschadwick.blogspot.com/2007/11/safe-handling-of-uniqueidentifier-in.html
CREATE FUNCTION [dbo].[ToGuid]
(
#input NVARCHAR(MAX)
)
RETURNS uniqueidentifier
AS
BEGIN
DECLARE #isValidGuid BIT;
DECLARE #temp NVARCHAR(MAX);
SET #isValidGuid = 1;
SET #temp = UPPER(LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(#input, '-', ''), '{', ''), '}', ''))));
IF(#temp IS NOT NULL AND LEN(#temp) = 32)
BEGIN
DECLARE #index INT;
SET #index = 1
WHILE (#index <= 32)
BEGIN
IF (SUBSTRING(#temp, #index, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'))
BEGIN
SET #index = #index + 1
END
ELSE
BEGIN
SET #isValidGuid = 0
BREAK;
END
END
END
ELSE
BEGIN
SET #isValidGuid = 0
END
DECLARE #ret UNIQUEIDENTIFIER
IF(#isValidGuid = 1)
set #ret = cast(#input AS UNIQUEIDENTIFIER)
ELSE
set #ret = NULL
RETURN #ret
END
I'm still very interested if there is a better answer than this.
Verify if #Project is a number using the ISNUMERIC function.
your code should looks like that:
declare #ProjectID uniqueidentifier
set #ProjectID = null
IF ISNUMERIC(#Project) > 0
BEGIN
set #ProjectID = cast(#Project as uniqueidentifier)
END
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID
I have a variable which contains the following string: AL,CA,TN,VA,NY
I have no control over what I get in that variable (comes from reporting services)
I need to make it look like this: 'AL','CA','TN','VA','NY'
How do I do this?
declare #x varchar(50) = 'AL,CA,TN,VA,NY'
select '''' + REPLACE(#x, ',', ''',''') + ''''
I ended up doing something very similar that I thought I'd post. (I'll give credit to Mitch however)
This takes care of the middle:
SET #StateList = REPLACE(#StateList, ',', ''',''')
Then quote the edges:
SET #WhereClause1 = #WhereClause1 + 'AND customerState IN (''' + #StateList + ''') '
For a more generic answer, when you don't know what your output will look like exactly, use regular expressions.
This would let you you match on something like [A-Z]{2} and replace it with '$&'.
A commenter suggested this is overkill for this task - agreed, if you can guarantee you will always get a string like that. However, other people find these question pages later with similar, but not exact, problems, so other options are helpful to have.
Don't bother with dynamic sql.
You need to convert the string to a table
so
A,B,C,D
becomes
Value
A
B
C
D
using a function like
http://www.sqlusa.com/bestpractices/training/scripts/splitcommadelimited/
then you can use CROSS APPLY (which is like joining to a table, but a table created by a function) or you can just put it in a table variable and join to that
I want to know y does the following script run in SQL and not in T-SQL
DECLARE #tblName varchar(30)
SET #tblName = CONVERT(VARCHAR(20),GETDATE(),112) + 'Table'
DECLARE #sql nvarchar(4000)
SELECT #sql =
'CREATE TABLE "' + #tblName + '"
(
ID VARCHAR(15),
Name VARCHAR(15)
)'
EXEC(#sql)
go
it gives you the error
Msg 170, Sev 15: Line 1: Incorrect syntax near '20090714Table'. [SQLSTATE 42000]
I have the following statement in a stored procedure. I am passing the name of the column as parameter and also the value to be checked in another variable. Is it possible to accomplish this in SQL server. Please let me know.
SELECT CaseId FROM app_Case
where #SearchCat=#keywords
ORDER BY CreatedDate DESC
I think the only way to do this would be to generate a dynamic SQL statement. The other option would be to take all column values as parameters, default them to null, and check for that.
ie
WHERE (cola = #cola OR #cola IS NULL) AND (colb = #colb OR #colb IS NULL) etc.
You need to create a string of SQL inside the SP and execute it.
Declare #SQL As VARCHAR(8000)
SET #SQL = 'SELECT CaseId FROM app_Case where ' +
#SearchCat + ' = '' + #keywords +
'' ORDER BY CreatedDate DESC'
EXEC(#SQL)
You can build a dynamic query Essentially you build a string and then execute it. (Watch out for SQL injection attacks).
Another approach would be to use a case statement which if you don't have a lot of options might be worth trying:
select CaseId from app_Case
where case when #searchCat='field1'
then field1
else #searchVal
end = #searchVal and
case when #searchCat='field2'
then field2
else #searchVal
end = #searchVal
Another approach is do the same thing using or clauses:
select CaseId from app_Case
where (#searchCat='Field1' and Field1=#searchVal) OR
(#serachCat='Field2' and Field2=#searchVal)