I have a SQL table that stores IPs as varbinary(16). So '10.240.200.9' will be stored as 0x0AF0C809.
I'm writing a stored procedure which has to create dynamic sql due to the nature of the input variables. One of the input variables is an IP address.
Let's just take the previously mentioned IP and its hex. When I run the query below, it gives me the following error
The data types varchar and varbinary are incompatible in the add operator.
I understand that it's happening due to #hex being concatenated to a nvarchar string.
I'm trying to make this SQL work
set #sql = 'select * from [table] where ip = ' + [hexvalue]
although that answer will convert it to varchar i do not believe the output is what you are looking for. this will work if your input is a varbinary(16) parameter
declare #s varbinary(16) = 0x0AF0C809
set #sql = 'select * from [table] where ip = ' + + UPPER(master.sys.fn_varbintohexstr(#s))
You can explicitely cast the value to a compatible type such as varchar.
SELECT CAST(CAST('test' AS varbinary(4)) AS varchar(4)) + 'test'
With your example:
set #sql = 'select * from [table] where ip = ''' + CAST([hexvalue] AS varchar(16)) + ''''
Related
My table has column names m1,m2,m3...,m12.
I'm using iterator to select them and insert them one by one in another table.
In this iterator I'm trying to generate filed names with:
'['+concat('m',cast(#P_MONTH as nvarchar))+']'
where #P_MONTH is incrementing in each loop.
so for #P_MONTH = 1 this suppose to give [m1] which works fine.
But when I run query I get:
Conversion failed when converting the nvarchar value '[m1]' to data
type int.
And if I put simply [m1] in that select it works ok.
How to concat filed name so it can be actually interpreted as filed name from certain table?
EDIT
Here is full query:
DECLARE #SQLString nvarchar(500),
#P_YEAR int,
#P_MONTH int = 1
set #P_YEAR = 2018
WHILE #P_MONTH < 13
BEGIN
SET #SQLString =
'INSERT INTO [dbo].[MASTER_TABLE]
(sector,serial,
date, number, source)'+
'SELECT ' + '[SECTOR],[DEPARTMENT]' +
QUOTENAME(cast(CONVERT(datetime,CONVERT(VARCHAR(4),#P_YEAR)+RIGHT('0'+CONVERT(VARCHAR(2),#P_MONTH),2)+'01',5) as nvarchar))+
QUOTENAME ('M',cast(#P_MONTH as nvarchar)) +
'EMPLOYED' +
'FROM [dbo].[STATS]'+
'where YEAR= #P_YEAR'
EXECUTE sp_executesql #SQLString
SET #P_MONTH = #P_MONTH + 1
END
It's still not working. It executes successfully but it does nothing.
Good day,
Let's create a simple table for the sake of the explanation
DROP TABLE IF EXISTS T
GO
CREATE TABLE T(a1 INT)
GO
INSERT T(a1) VALUES (1),(2)
GO
SELECT a1 FROM T
GO
When we are using a query like bellow, the server parse the text as a value and not as a column name
DECLARE #String NVARCHAR(10)
SELECT #String = '1'
--
SELECT '['+concat('a',cast(#String as nvarchar))+']'
FROM T
GO
This mean that the result will be 2 rows with no name for the column and the value will be "[a1]"
Moreover, the above query uses the brackets as part of the string.
One simple solution is to use the function QUOTENAME in order to add brackets around a name.
Another issue in this approach is the optional risk of SQL Injection. QUOTENAME might not be perfect solution but can help in this as well.
If we need to use entities name dynamically like in this case the column name then for most cases using dynamic query is the best solution. This mean to use the Stored Procedure sp_executesql as bellow
DECLARE #String INT
SELECT #String = 1
DECLARE #SQLString nvarchar(500);
SET #SQLString =
'SELECT ' + QUOTENAME(concat('a',cast(#String as nvarchar))) + ' FROM T'
EXECUTE sp_executesql #SQLString
GO
Hi I am trying to do the following:
SELECT #IDENTITY_COLUMN = (
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tbltest1'
SELECT #LAST_VALUE_USED = (
SELECT ISNULL(MAX(#IDENTITY_COLUMN),0)
FROM tbltest1
)
If there are no rows, it works fine but when there is a row, the second query is returning a string that has the column name and I believe it is because #IDENTITY_COLUMNhas quotes in it. Hence I am getting Conversion failed when converting the nvarchar value 'columnname' to data type int. How can I solve this problem?
Help appreciated!
I think you're trying to get the maximum value of an IDENTITY column from a table if such a column exists. You'd need dynamic SQL for that. Something like:
DECLARE #identity_column sysname;
DECLARE #query nvarchar(MAX);
SET #identity_column = (SELECT column_name
FROM information_schema.columns
WHERE table_name = 'tbltest1');
IF #identity_column IS NOT NULL
BEGIN
SET #query = '
SELECT isnull(max(' + quotename(#identity_column) + '), 0)
FROM tbltest1;
';
EXECUTE (#query);
END;
Note:
For object names use sysname, that's an extra type for them. Don't use varchar or navarchar etc..
Always use quotename() if embedding object names in a dynamic query. That'll prevent funny things from happen, if the object name contains non alpha numeric characters or is odd in other ways.
I need to convert string to integers, but I'm getting a type error.
declare #stringinteger nvarchar(255) null;
set #stringinteger='1,2,3,4'
select *
from user
where id in (#stringinteger)
The error I get:
Conversion failed when converting the nvarchar value '1,2,3,4' to data type int
You have two methods to handle this, dynamic SQL and splitting the string.
For the latter, you can use string_split() (introduced in SQL Server 2016) or a similar function (they are all over the web, google "string split sql server"):
select *
from user
where id in (select cast(value as int) from string_split(#stringinteger, ',')) ;
The dynamic SQL looks like:
declare #stringinteger nvarchar(255) null;
set #stringinteger = '1,2,3,4';
declare #sql nvarchar(max);
set 'select *
from user
where id in (#stringinteger)';
set #sql = replace(#sql, '#stringinteger', #stringinteger);
exec sp_executesql #sql;
Note that in SQL Server, you should always provide a length for character types. If you leave it out, then the default varies by context -- and your code may not do what you expect.
Can someone explain why this doesn't work:
DECLARE #PROCESS_TABLE varchar(100) = 'Table Name'
DECLARE #SRIDCount int
EXEC('SELECT ' + #SRIDCount + ' = COUNT(DISTINCT Geom.STSrid) FROM ' + #PROCESS_TABLE + '')
Print #SRIDCount
Incorrect syntax near '='.
but this does:
DECLARE #PROCESS_TABLE varchar(100) = 'Table Name'
DECLARE #SRIDCount int
DECLARE #SQLString nvarchar(max)
SET #SQLString = 'SELECT #SRIDCount = COUNT(DISTINCT Geom.STSrid) FROM ' + #PROCESS_TABLE + ''
EXECUTE SP_executesql #SQLString, N'#SRIDCount int OUTPUT', #SRIDCount = #SRIDCount OUTPUT
Print #SRIDCount
1
Mostly I want to understand why I can't set a variable in an exec(). Using SP_executesql seems cumbersome and complicated by comparison and so far I have gotten away with not using it.
First of all, please try avoiding dynamic sql whenever possible. Think about sql injection, readability, error handling, maintainability... There are many reasons to avoid dynamic sql. I claim that your above use case can be solved without the use of dynamic sql.
Next you need to understand that 'SELECT ' + #SRIDCount is trying to add the value of the #SRIDCount (which is NULL btw) to the leading varchar, not the name of the variable. You could easily fix by adding the variable to the querystring: SELECT #SRIDCount = COUNT(DISTINCT Geom.STSrid)...
Your second mistake is to assume that EXEC() can interfer with variables outside of the inner batch, which is treated as an own transaction and has no access to your declared variables.
Your example provided using SP_executesql is also an enclosed transaction, by specifying the output you are able to use the result of the query.
Rewriting your first query would work if you include the variables within:
DECLARE #PROCESS_TABLE VARCHAR(100) = QUOTENAME('Table Name')
EXEC(N'DECLARE #SRIDCount INT
SELECT #SRIDCount = COUNT(DISTINCT Geom.STSrid)
FROM ' + #PROCESS_TABLE + '
PRINT #SRIDCount')
But I assume you want to work with the count in a further query. So following workaround should do the job:
DECLARE #PROCESS_TABLE VARCHAR(100) = 'Table Name'
DECLARE #SRID TABLE (SRIDCount int)
DECLARE #SRIDCount INT
INSERT #SRID
INSERT INTO #SRID
EXEC(N'SELECT COUNT(DISTINCT Geom.STSrid)
FROM ' + #PROCESS_TABLE )
SELECT #SRIDCount = SRIDCount
FROM #SRID
But, like I stated in the beginning, rethink about what you try to achieve and how you could do it without dynamic sql.
DECLARE #PROCESS_TABLE varchar(100) = 'Table Name'
DECLARE #SRIDCount int
EXEC('SELECT ' + #SRIDCount + ' = COUNT(DISTINCT Geom.STSrid) FROM ' + #PROCESS_TABLE + '')
Print #SRIDCount
Incorrect syntax near '='.
Since you haven't assigned a value to SRIDCount, the statement would literally be
'SELECT ' + NULL + ' = Count(...'
Concatenating a string and a null results in a Null. You are in essence trying to run
Exec (null);
if you were to assign a value to SRIDCount, you will get a data type conversion error because a number cannot be added to a string (string + int + string). If you convert your SRIDCount to a character datatype, you can substitute properly in the string. However, that is not likely what you want to do.
Exec runs the query in a new batch. Memory is not shared between batches, the batch within the Exec statement has no visibility to the outer statement.
Try printing your #SRIDCount before you execute it. Once your printed statement looks good, add the Execute statement and run again. If you include the Exec before you've validated your statement, you are risking running a query that doesn't do what you expect.
Note how different you (have to) make #SRIDCount work together with the queries:
'SELECT ' + #SRIDCount + ' = COUNT....' will concatenate a String SELECT with the VALUE of #SRIDCount and 'COUNT ...'.
As the variable was newly instantiated just one line above you will most probably end up with:
'SELECT 0 = COUNT ...
On the other hand the later example is a String that still contains the variables NAME and not it's VALUE and thus this one works as intended. That is also but not only because with
N'#SRIDCount int OUTPUT'
you explicitly mark this variable as some value you want to be returned from the query.
It doesn't work because exec runs your sql command within a batch and variables do not exist outside it's own batch.
execute
Executes a command string or character string within a Transact-SQL
batch...
variable scope
The scope of a variable is the range of Transact-SQL statements that
can reference the variable. The scope of a variable lasts from the
point it is declared until the end of the batch or stored procedure in
which it is declared...
I am trying to store the results of an SQL query into a variable.The query simply detects the datatype of a column, hence the returned result is a single varchar.
SET #SQL =
'declare ##x varchar(max) SET ##x = (select DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = ' +char(39)+#TabName+char(39) +
' AND column_name = ' +char(39)+#colName+char(39) + ')'
EXECUTE (#SQL)
Anything within the 'SET declaration' cannot access any variables outside of it and vice versa, so I am stuck on how to store the results of this query in a varchar variable to be accessed by other parts of the stored procedure.
You dont need a dynamic query to achieve what you want, below query will give the same result as yours.
declare #x varchar(max)
declare #tableName varchar(100), #ColumnName varchar(50)
set #tableName = 'Employee'
set #ColumnName = 'ID'
select #x = DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
where
Table_Name = #tableName
and column_name = #ColumnName
select #x
All user-defined variables in T-SQL have private local-scope only. They cannot be seen by any other execution context, not even nested ones (unlike #temp tables, which can be seen by nested scopes). Using "##" to try to trick it into making a global-variable doesn't work.
If you want to execute dynamic SQL and return information there are several ways to do it:
Use sp_ExecuteSQL and make one of the parameters an OUTPUT parameter (recommended for single values).
Make a #Temp table before calling the dynamic SQL and then have the Dynamic SQL write to the same #Temp table (recommended for multiple values/rows).
Use the INSERT..EXEC statement to execute your dynamic SQL which returns its information as the output of a SELECT statement. If the INSERT table has the same format as the dynamic SQL's SELECT output, then the data output will be inserted into your table.
If you want to return only an integer value, you can do this through the RETURN statement in dynamic SQL, and receive it via #val = EXEC('...').
Use the Session context-info buffer (not recommended).
However, as others have pointed out, you shouldn't actually need dynamic SQL for what you are showing us here. You can do just this with:
SET #x = ( SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = #TabName
AND column_name = #colName )
You may want to consider using the sp_executesql stored procedure for dynamic sql.
The following link provides a good usage example of sp_executesql procedure with output parameters:
http://support.microsoft.com/kb/262499