Microsoft sql 2008 string formatting - sql-server

I wonder if there is something in Microsoft SQL 2008, which can do similar thing in C#
for(int i = 0; i < 10; ++i)
String.Format("ABC {0} XYZ", i.ToString());
Basically, is the String.Format() possible in MS SQL 2008?
Just make it more clear. I want to create a table dynamicaly, so need to generate string for the columns, like
"Col1 FLOAT, Col2 FLOAT. Col3 FLOAT"
The number of column is specified by a variable, so I wonder if there is a function to do String.Format(), so I can use in a loop.
Idealy, I want to write a stored procedure, and pass in
"Col{0} Float"

Declare #variable INT
Declare #columnName varchar(50)
Declare #columnType varchar(10)
Select #columnName + CONVERT(varchar(10), #variable) + ' ' + #columnType
here u go. assign variable to your int value and run in loop or you can create that loop in sql. i would do that in sql instead of going back and forth. but it depends on your logic.

You are just concatenating text to a column so you can make use of +:
SELECT "Employee Name" + EmployeeName AS EmployeeName FROM MyTable
You do have to watch out with using + though depending on the datatype, but there is always CAST and CONVERT to help you cast datatypes to a specific type.

Next version has a FORMAT function or you can roll your own CLR function in SQL Server 2008 that calls the .NET framework formatting methods but for your stated use case I would just use something like the below.
DECLARE #ColList varchar(max) =
(SELECT ',Col',number+0,' FLOAT'
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 10
FOR XML PATH(''))
SELECT STUFF(#ColList, 1, 1, '')

Related

Passing Int to dynamic stored procedure fails

I have a dynamic stored procedure in SQL Server that works well to pivot a table:
CREATE PROCEDURE dbo.DynamicPivotTableInSql
#ColumnToPivot NVARCHAR(255),
#ListToPivot NVARCHAR(255),
#SurveyID INT=10
AS
BEGIN
DECLARE #SqlStatement NVARCHAR(MAX)
SET #SqlStatement = N'SELECT *
FROM
(SELECT
[resp_id], [benefit], [weight]
FROM Segment_Responses) myResults
PIVOT
(SUM([weight])
FOR [' + #ColumnToPivot + ']
IN (' + #ListToPivot + ')) AS PivotTable';
EXEC (#SqlStatement)
END
and I call it like this
EXEC DynamicPivotTableInSql
#ColumnToPivot = 'benefit',
#ListToPivot = '[OBSERVABILITY], [COST], [EASE OF USE], [SERVICE]'
Here is where I run into problems. You'll notice I have hardcoded #SurveyID = 10 and if I try to add that as a where statement in the stored procedure like this:
FROM Segment_Responses
WHERE survey_id = ' + #SurveyID + '
and run the stored procedure again I get this error:
Conversion failed when converting the nvarchar value '
SELECT * FROM (
SELECT
[resp_id],
[benefit],
[weight]
FROM Segment_Responses where survey_id=' to data type int.
I've tried to solve this many ways (e.g., passed the Int variable instead of hard coding it) but always get the same result. Any ideas what is going on?
Just to try to add some clarity, when you add together two different types, SQL Server will (where it can) implicitly convert one to the other - the result must be a single type after all.
It decides which one to convert "to the other" based on an order of precedence.
So where you are trying to concatenate a varchar with an int, the int has the higher order of precedence. This is also a common cause of errors and bugs when using a case expression when mixing types in different execution paths of the expression.
You need to be explicit and cast the int to a varchar.
Ideally you would use a parameterised query which would also reuse the cached execution plan - this may be beneficial if the cardinality of the data is similar but sometimes making the value part of the query dynamically can be advantagous, it depends on the use-case.
This is why the syntax EXEC (#SQL) is strongly suggested against. Use sys.sp_executesql and parametrise your statement:
SET #SQL = N'SELECT ...
FROM ...
WHERE survey_id = #SurveyID ...;';
EXEC sys.sp_executesql #SQL, N'#SurveyID int',SurveyID;
The + only works with strings. If you use a number TSQL assumes you are trying to use the addition operator, and tries to convert the string argument to int.
eg this
select 1 + '2'
works and returns 3.
Use CONCAT instead of +, or use an explicit conversion on the int.
eg
WHERE survey_id = ' + cast(#SurveyID as varchar(20)) + '

What is the encode(<columnName>, 'escape') PostgreSQL equivalent in SQL Server?

In the same vein as this question, what is the equivalent in SQL Server to the following Postgres statement?
select encode(some_field, 'escape') from only some_table
As you were told already, SQL-Server is not the best with such issues.
The most important advise to avoid such issues is: Use the appropriate data type to store your values. Storing binary data as a HEX-string is running against this best practice. But there are some workarounds:
I use the HEX-string taken from the linked question:
DECLARE #str VARCHAR(100)='0x61736461640061736461736400';
--here I use dynamically created SQL to get the HEX-string as a real binary:
DECLARE #convBin VARBINARY(MAX);
DECLARE #cmd NVARCHAR(MAX)=N'SELECT #bin=' + #str;
EXEC sp_executeSql #cmd
,N'#bin VARBINARY(MAX) OUTPUT'
,#bin=#convBin OUTPUT;
--This real binary can be converted to a VARCHAR(MAX).
--Be aware, that in this case the input contains 00 as this is an array.
--It is possible to split the input at the 00s, but this is going to far...
SELECT #convBin AS HexStringAsRealBinary
,CAST(#convBin AS VARCHAR(MAX)) AS CastedToString; --You will see the first "asda" only
--If your HEX-string is not longer than 10 bytes there is an undocumented function:
--You'll see, that the final AA is cut away, while a shorter string would be filled with zeros.
SELECT sys.fn_cdc_hexstrtobin('0x00112233445566778899AA')
SELECT CAST(sys.fn_cdc_hexstrtobin(#str) AS VARCHAR(100));
UPDATE: An inlinable approach
The following recursive CTE will read the HEX-string character by character.
Furthermore it will group the result and return two rows in this case.
This solution is very specific to the given input.
DECLARE #str VARCHAR(100)='0x61736461640061736461736400';
WITH recCTE AS
(
SELECT 1 AS position
,1 AS GroupingKey
,SUBSTRING(#str,3,2) AS HEXCode
,CHAR(SUBSTRING(sys.fn_cdc_hexstrtobin('0x' + SUBSTRING(#str,3,2)),1,1)) AS TheLetter
UNION ALL
SELECT r.position+1
,r.GroupingKey + CASE WHEN SUBSTRING(#str,2+(r.position)*2+1,2)='00' THEN 1 ELSE 0 END
,SUBSTRING(#str,2+(r.position)*2+1,2)
,CHAR(SUBSTRING(sys.fn_cdc_hexstrtobin('0x' + SUBSTRING(#str,2+(r.position)*2+1,2)),1,1)) AS TheLetter
FROM recCTE r
WHERE position<LEN(#str)/2
)
SELECT r.GroupingKey
,(
SELECT x.TheLetter AS [*]
FROM recCTE x
WHERE x.GroupingKey=r.GroupingKey
AND x.HEXCode<>'00'
AND LEN(x.HEXCode)>0
ORDER BY x.position
FOR XML PATH(''),TYPE
).value('.','varchar(max)')
FROM recCTE r
GROUP BY r.GroupingKey;
The result
1 asdad
2 asdasd
Hint: Starting with SQL Server 2017 there is STRING_AGG(), which would reduce the final SELECT...
If you need this functionality, it's going to be up to you to implement it. Assuming you just need the escape variant, you can try to implement it as a T-SQL UDF. But pulling strings apart, working character by character and building up a new string just isn't a T-SQL strength. You'd be looking at a WHILE loop to count over the length of the input byte length, SUBSTRING to extract the individual bytes, and CHAR to directly convert the bytes that don't need to be octal encoded.1
If you're going to start down this route (and especially if you want to support the other formats), I'd be looking at using the CLR support in SQL Server, to create the function in a .NET language (C# usually preferred) and use the richer string manipulation functionality there.
Both of the above assume that what you're really wanting is to replicate the escape format of encode. If you just want "take this binary data and give me a safe string to represent it", just use CONVERT to get the binary hex encoded.
1Here's my attempt at it. I'd suggest a lot of testing and tweaking before you use it in anger:
create function Postgresql_encode_escape (#input varbinary(max))
returns varchar(max)
as
begin
declare #i int
declare #len int
declare #out varchar(max)
declare #chr int
select #i = 1, #out = '',#len = DATALENGTH(#input)
while #i <= #len
begin
set #chr = SUBSTRING(#input,#i,1)
if #chr > 31 and #chr < 128
begin
set #out = #out + CHAR(#chr)
end
else
begin
set #out = #out + '\' +
RIGHT('000' + CONVERT(varchar(3),
(#chr / 64)*100 +
((#chr / 8)%8)*10 +
(#chr % 8))
,3)
end
set #i = #i + 1
end
return #out
end

TSQL Passing a varchar variable of column name to SUM function

I need to write a procedure where I have to sum an unknown column name.
The only information I have access to is the column position.
I am able to get the column name using the following:
SELECT #colName = (SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='TABLENAME' AND ORDINAL_POSITION=#colNum)
I then have the following:
SELECT #sum = (SELECT SUM(#colName) FROM TABLENAME)
I then receive the following error:
Operand data type varchar is invalid for sum operator
I am confused about how to make this work. I have seen many posts on convert and cast, but I cannot cast to an float, numeric, etc because this is a name.
I would appreciate any help.
Thank you
This is not possible. You will have to assemble the SQL query and then execute it. Something like this:
SET #sqlquery = 'SELECT SUM(' + #colName + ') FROM TABLENAME'
EXECUTE ( #sqlquery )
Adjust accordingly.

SQL Server expression substitution in SELECT statements as column names

How do I evaluate a character expression to resolve to a valid column name in a SELECT statement that would return column row values? Eg valid column name = Customer_1 == 'Customer_'+'1'
You need to use dynamic SQL. An example
DECLARE #DynSQL nvarchar(max)
DECLARE #Suffix int = 1
SET #DynSQL = N'SELECT Customer_' + CAST(#Suffix as nvarchar(10)) +
N' FROM YourTable WHERE foo = #foo'
EXEC sp_executesql #DynSQL, N'#foo int', #foo=1
As always with dynamic SQL you need to consider SQL injection if any of the inputs to the process will be user supplied.
How do I evaluate a character expression to resolve to a valid column name in a SELECT statement that would return column row values? Eg valid column name = Customer_1 == 'Customer_'+'1'
You're probably doing something wrong if you need to do this, but if you have to: build the column names as rows and then pivot.

Why does SQL Server require INT to be converted to NVARCHAR?

During an ordeal yesterday, I learned that you can't pass this query to EXEC():
#SQL = #SQL + 'WHERE ID = ' + #SomeID
EXCEC(#SQL)
Where #SomeID is an INT and #SQL is NVARCHAR. This will complain about not being able to convert NVARCHAR back to INT during execution.
I realized you have to do it like
#SQL = #SQL + 'WHERE ID = ' + CONVERT(NVARCHAR(20), #SomeID)
What I didn't understand is why? Why doesn't SQL Server understand the INT when simply +:ed on to an NVARCHAR? I'm guessing it has something to do with char sets.
EDIT: Fixed typos (missed some +:s).
+ (String Concatenation)
An operator in a string expression
that concatenates two or more
character or binary strings, columns,
or a combination of strings and column
names into one expression (a string
operator).
Expression is any valid Microsoft® SQL Server™
expression of any of the data types in
the character and binary data type
category, except the image, ntext, or
text data types. Both expressions must
be of the same data type, or one
expression must be able to be
implicitly converted to the data type
of the other expression.
So... There is no implicit convertion of int to string... This is an internal question
I'm not saying this will definitely work, and I'm not near my sql management studio to try it before posting, but have you tried something like this:
#SQL = #SQL + 'Where ID = ' + #SomeID
EXEC(#SQL)

Resources