Remove quotes when passing a column name via parameter - sql

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.

Related

Generating filed name with concat

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

SQL varbinary IP compare

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)) + ''''

T-SQL: Variable Scope

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

TSQL Update stmt for each column name according to data type

For SSIS, I need to create a TSQL update workflow to overwrite the current table records in case of an import error.
I already have a set up for the whole SSIS process but I'm missing the SQL update statement.
So if something goes wrong during the import the current records (all rows) in the table should be updated with a short message - "Error DB Import" for example.
Since I have multiple tables to deal with I also get different column names and data types.
I would use this stmt to get the column names
SELECT COLUMN_NAME , *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'myTable'
but how can I set a string message for the char types and NULL for numeric and date types. Using a CASE stmt?
In pseudo-code it's probably just a loop through the columns: if column_name is of data_type "char" then...
I also need to ignore the first 4 columns of each table so that I don't overwrite ID, Date, etc.
If you can help me set up a static test update stmt I'm sure I will be able to transfer this to my SSIS project.
Thank you.
Sounds like you're looking for something like this:
SELECT
CASE DATA_TYPE
WHEN 'int' THEN NULL
WHEN 'varchar' THEN 'STRING MSG GOES HERE'
END,
COLUMN_NAME , *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'myTable'
AND ORDINAL_POSITION > 4
SQL Fiddle Demo
You can add as many WHEN clauses to the CASE statement as needed. Also, you want to use the ORDINAL_POSITION column to exclude the first 4 columns.
If you need to use this information to create an UPDATE statement, then you'll need to do that with Dynamic SQL.
EDIT -- Dynamic SQL:
create procedure updateMyTable
as
begin
declare #sql varchar(max)
SELECT #sql = 'UPDATE myTable SET ' +
STUFF(
(SELECT ', ' + COLUMN_NAME + ' = ' +
CASE DATA_TYPE
WHEN 'int' THEN 'NULL'
WHEN 'varchar' THEN '''STRING MSG GOES HERE'''
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'myTable'
for xml path(''))
,1,1,'')
exec(#sql)
end
SQL Fiddle Demo

Help with TSQL - a way to get the value in the Nth column of a row?

I hope to find a way to get the value in the Nth column of a dataset.
Thus, for N = 6 I want
SELECT (Column6Value) from MyTable where MyTable.RowID = 14
Is there a way to do this in TSQL as implemented in SQL Server 2005? Thanks.
You should be able to join with the system catalog (Information_Schema.Columns) to get the column number.
This works:
create table test (a int, b int, c int)
insert test values(1,2,3)
declare #column_number int
set #column_number = 2
declare #query varchar(8000)
select #query = COLUMN_NAME from information_Schema.Columns
where TABLE_NAME = 'test' and ORDINAL_POSITION = #column_number
set #query = 'select ' + #query + ' from test'
exec(#query)
But why you would ever do something like this is beyond me, what problem are you trying to solve?
Not sure if you're at liberty to redesign the table, but if the ordinal position of the column is significant, your data is not normalized and you're going to have to jump through lots of hoops for many common tasks.
Instead of having table MyTable with Column1... ColumnN you'd have a child table of those values you formerly stored in Column1...ColumnN each in their own row.
For those times when you really need those values in a single row, you could then do a PIVOT: Link
Edit: My suggestion is somewhat moot. Ash clarified that it's "de-normalization by design, it's a pivot model where each row can contain one of any four data types." Yeah, that kind of design can be cumbersome when you normalize it.
If you know the range of n you could use a case statement
Select Case when #n = 1 then Column1Value when #n = 2 then Column2Value end
from MyTable
As far as I know there is no dynamic way to replace a column (or table) in a select statement without resorting to dynamic sql (in which chase you should probably refactor anyways)
Implementation of #Mike Sharek's answer.
Declare #columnName varchar(255),
#tablename varchar(255), #columnNumber int, #SQL nvarchar(4000)
Set #tablename = 'MyTable'
Set #columnNumber = 6
Select #columnName = Column_Name from Information_SChema.columns
where Ordinal_position = #columnNumber and Table_Name = #tablename
Set #SQL = 'select ' + #columnName + ' from ' + #tableName + ' where RowID=14'
Exec sp_Executesql #SQL
I agree with Sambo - why are you trying to do this? If you are calling the code from C# or VB, its much easier to grab the 6th column from a resultset.