Rounding a decimal value with dynamic variable - sql

I need to round a value of a column in table A based on another column in table B in a SQL function.
DECLARE #currencyround INT
SELECT #currencyround = ISNULL(currencyround, 2)
FROM dbo.PRTL_currencySettings
When I put value of #currencyround directly in the query like the following, it's working fine:
SELECT
CAST(POS.BALANCE AS DECIMAL(18, 2)) AS DBAmount
FROM
dbo.POS_SALES POS
When I put value of #currencyround like the following, it's showing error:
Incorrect syntax near '#currencyround'.
SELECT
CAST(POS.BALANCE AS DECIMAL(18, #currencyround)) AS DBAmount
FROM
dbo.POS_SALES POS

If you need specific metadata you could use dynamic SQL:
DECLARE #currencyround int;
SELECT #currencyround=ISNULL(currencyround,2) FROM dbo.PRTL_currencySettings;
DECLARE #sql NVARCHAR(MAX) =
N'select CAST(POS.BALANCE AS DECIMAL(18,<currencyround>)) AS DBAmount
FROM dbo.POS_SALES POS';
SET #sql = REPLACE(#sql, '<currencyround>', #currencyround);
EXEC(#sql);
But personally I would not write such code. I would rather format the number in the application layer.

What do you not understand? Type definitions don't allow variables. You could do this with dynamic SQL but that seems like overkill.
If you care about what how the variable is output, use str() or format() to create the format that you want.

If you need to round the values, then how about this:
ROUND(POS.BALANCE, #currencyround)

Related

query inside the variable

Is it possible in SQL to use a variable to store query.
For example to save time when subquery is used multiple times inside the main query.
Example:
DECLARE #my_query as varchar(250) = select x from my_table where my_table = y.your_table
SELECT
a,b,c,(#my_query),d,e,f
FROM my_table_1
Is it possible in SQL to use a variable to store query.
Depend on your definition of "query". If you mean store the text which we use to execute the command, then the answer is YES. If you mean an object type query, then the answer is not - since there is no data type that fit this.
What I mean is that a variable can store a value which is string. The string can be any query command that you want. Therefore, you can store for example the text "select col1,col2 from table1".
Next you need to ask how can we use this text in order to execute it as part of a query, which is done using dynamic query.
We can execute a text of a query using the build-in stored procedure sp_executesql, which is build for such needs.
For example:
-- DECLARE VARIABLE
DECLARE #MyQuery NVARCHAR(MAX)
-- SET the value of the variable
SET #MyQuery = 'SELECT ''Yes I can'''
-- Executing a dynamic query
EXECUTE sp_executesql #MyQuery
Here is another example which look more close to your question:
-- First let's create a table
CREATE TABLE T(ID INT)
INSERT T(ID) VALUES (1),(2)
GO
-- And here is what you sked about:
-- DECLARE VARIABLE
DECLARE #MyQuery NVARCHAR(MAX)
-- SET the value of the variable
SET #MyQuery = 'select ID from T where ID = ''1'''
-- Let's combine the text to a full query now
DECLARE #FullQuery NVARCHAR(MAX)
SET #FullQuery = '
SELECT
ID,(' + #MyQuery + ')
FROM T
'
PRINT #FullQuery
-- Executing a dynamic query
EXECUTE sp_executesql #FullQuery
NOTE! Your specific sample of query will return error, which is not related to the question "Is it possible in SQL to use a variable to store query". This is a result of the "query" is not well formatted.
Important! It is HIGHLY recommended to read the document about this stored procedure and learn a bit more of the options it provides us.
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver15

using user variable in 'IN' clause of sql

declare #A varchar(5000)
declare #B varchar(5000)
select #A=value from drp.Parameter where parameteridname='IV01'
--result of this query ('DIM003966','DIM000736','DIM025297',
-- 'DIM025302','DIM027583')
select [InventLocationId],[WMSLocationId] from ItemKit.InventDim
where inventdimid in (#A)
i am not getting any result out of second query but if i run them individually and use result of 1st query in second i am getting it. is there any way around to run them together
That is because it evaluates #a as is (as text, not a list of values).
You should create a sql statement on the fly and execute it:
declare #sqlStatement nvarchar(4000)
#sqlStatement = 'select [InventLocationId],[WMSLocationId] from ItemKit.InventDim where inventdimid in (' + #A + ')'
execute sp_executesql #sqlStatement
But as said by others, don't use unless really necessary.
You should use this code:
select [InventLocationId],[WMSLocationId] from ItemKit.InventDim
where inventdimid in (select value from drp.Parameter where parameteridname='IV01')
As you have a single string value (holding all the values) you could either:
Parse sql on the fly
Note: I do not recommend this, as it can allow sql injection
(see #Patrick Hofman's answer for good example of this)
Use like
declare #A varchar(5000)
declare #B varchar(5000)
select #A=value from drp.Parameter where parameteridname='IV01'
--result of this query ('DIM003966','DIM000736','DIM025297',
-- 'DIM025302','DIM027583')
SELECT #A /* Here you will see the problem, as it is not multiple values,
it is a single string with a comma & quote (') delimited list */
SELECT
[InventLocationId], [WMSLocationId]
FROM
ItemKit.InventDim
WHERE
/* parse up the inventdimid to include the quote (') around it */
#A like '%''' + #inventdimid + '''%'

How can I declare a table variable with a decimal variable as the identity seed? [duplicate]

This question already has answers here:
Programmatically set identity seed in a table variable
(3 answers)
Closed 9 years ago.
I'm using this, but I can't figure out why this doesn't work. SSMS won't give me a useful message other than syntax incorrect:
DECLARE #columnSeed DECIMAL
SELECT #columnSeed = MAX([seeded_column]) + 1 FROM [table] (nolock) WHERE [conditions]
DECLARE #Temp_Table TABLE ([seeded_column] varchar(35) IDENTITY(#columnSeed, 1), [more columns])
I want to take the maximum value from a column in one table and create a temporary table variable with an identity column seeded with that previous maximum value.
Edit: OK, after digging around for into about dynamic SQL I think I've got what should work, but it still isn't:
DECLARE #columnSeed DECIMAL
[#columnSeed set properly]
EXECUTE sp_executesql
N'DECLARE #Temp TABLE (seeded_column decimal IDENTITY(#seed, 1) NOT NULL [more columns])',
N'#seed decimal',
#seed = #columnSeed;
All the info I get now is that I've incorrect syntax near '#seed'
You can't use a variable as a seed. It is invalid syntax. The table variable is already implicitly created before the batch is executed and the variable assigned anyway.
The only way of doing this would be to concatenate the desired query and execute it. All usages of the table variable would need to be in the child scope.
DECLARE #columnSeed DECIMAL(18,0) = 10
DECLARE #sql NVARCHAR(MAX) = N'DECLARE #Temp TABLE (seeded_column decimal IDENTITY(' + CAST(#columnSeed AS NVARCHAR(19)) +', 1) NOT NULL)
INSERT INTO #Temp DEFAULT VALUES;
SELECT * FROM #Temp;'
EXECUTE sp_executesql
#sql,
N'#seed decimal',
#seed = #columnSeed;
I'm sure there is a better way of doing whatever it is you are doing anyway though.
You could just declare the table variable in the outer scope with a seed of 0 and add the desired offset to your SELECT queries from it for example.
DECLARE #columnSeed DECIMAL(18,0) = 10
DECLARE #Temp TABLE (seeded_column decimal(18,0) IDENTITY(0, 1) NOT NULL)
INSERT INTO #Temp DEFAULT VALUES;
INSERT INTO #Temp DEFAULT VALUES;
SELECT #columnSeed + seeded_column AS psuedo_seeded_column
FROM #Temp;
Though the whole need for this seems suspect. You shouldn't normally care what the IDENTITY values are. If this is to prepare data that later is inserted into the table you are calculating #columnSeed from maybe just inserting it and using the OUTPUT clause to get the ID values inserted might be more appropriate and less at risk of concurrency issues.
I think you can't use parameters in DDL. In other words, you won't be able to use #seed in the IDENTITY clause. Convert the seed to a string and shove it into your DDL manually. Something like this should work. (I don't have a SQL Server instance handy, so my apologies if there are any additional errors. The point is: Don't use parameters in DDL statements.)
DECLARE #columnSeed DECIMAL
DECLARE #sql NVARCHAR(1024)
[#columnSeed set properly]
SET #sql = N'DECLARE #Temp TABLE (seeded_column decimal IDENTITY(' || CONVERT(NVARCHAR, #seed) || N', 1) NOT NULL [more columns])';
EXECUTE sp_executesql #sql

Retrieve parameter value from parameter name as a string in SQL

I am trying to use some dynamic sql. I have generated over 100 parameters, and to speed things up I am trying to use dynamic sql to only insert values into a table that I have to based off of information retrieved from other tables. I have tried many things like adding a cast etc.
Example of what I mean:
DECLARE #var1 NVARCHAR(MAX)
-- Loop through and add various values
SET #var1 = #var1 + #parameterName
-- The parameter name is retrieved from a table that holds this information
the problem is that when I add the parameter name which would be like "#myFirstParameter" into my final expression so something like this:
DECLARE #finalString NVARCHAR(MAX)
SET #finalString = 'INSERT INTO myTableName ([myFirstParameter]) VALUES (#myFirstParameter)'
EXEC(#finalString)
The "#myFirstParameter" does not get replaced by it's value and I get the following error:
Must declare the scalar variable "#myFirstParameter".
Does anyone know of a way to go from the string name of a parameter to the actual value? I am trying to avoid hardcoding all the values and any work around I have attempted has failed and given me errors which appear to be much worse than what I have stated above.
The first way is to add the parameter's value, instead of its name, to the SQL string:
SET #finalString = 'INSERT INTO myTableName ([myFirstParameter]) VALUES (' +
#myFirstParameter + ')'
This assumes the parameter has a string value. If not, you have to cast it, like cast(#myFirstParameter as varchar(128)).
The second way is to use sp_executesql instead of exec:
exec sp_executesql
N'INSERT INTO myTableName ([myFirstParameter]) VALUES (#myFirstParameter)',
N'#myFirstParameter varchar(128)',
#myFirstParameter = #myFirstParameter;

Dynamic SQL Comma-Delimited Value Query

[Update: Using SQL Server 2005]
Hi, what I want to do is query my stored procedure with a comma-delimited list of values (ids) to retrieve rows of data.
The problem I am receiving is a conversion error:
Conversion failed when converting the varchar value ' +
#PassedInIDs + ' to data type int.
The statement in my where-clause and error is:
...
AND (database.ID IN (' + #PassedInIDs + '))
Note: database.ID is of int type.
I was following the article at:
http://www.sql-server-helper.com/functions/comma-delimited-to-table.aspx
but did not complete because of the error.
In my execution script I have:
...
#PassedInIDs= '1,5'
Am I doing something wrong here?
Thank you for your help.
I would strongly suggest that you use the second method from that link. Create a user-defined function that turns your comma-delimited string into a table, which you can then select from easily.
If you do a Google on Erland and "Dynamic SQL" he has a good writeup of the pitfalls that it entails.
For one, you are passing a string to the IN function in SQL. If you look back at the original article, you'll see that instead of issuing a direct SQL statement, it instead is building a string which is the SQL statement to execute.
There is no string evaluation in SQL. This:
database.ID IN (' + #PassedInIDs + ')
will not be turned to:
database.ID IN (1,2,3)
just because the #PassedInIDs parameter happens to contain '1,2,3'. The parameter is not even looked at, because all you have is a string containing " + #PassedInIDs + ". Syntactically, this is equivalent to:
database.ID IN ('Bob')
To make it short, you can't do what you attempt here in SQL. But there are four other possibilities:
you construct the SQL string in the calling language and abandon the stored procedure altogether
you use a dynamic prepared statement with as many parameters in the IN clause as you pan to use
you use a fixed prepared statement with, say, 10 parameters: IN (?,?,?,?,?,?,?,?,?,?), filling only as many as you need, setting the others to NULL
you create a stored procedure with, say, 10 parameters and pass in as many as you need, setting the others to NULL: IN (#p1, #p2, ..., #p10).
I would create a CLR table-valued function:
http://msdn.microsoft.com/en-us/library/ms131103.aspx
In it, you would parse the string apart and perform a conversion to a set of rows. You can then join on the results of that table, or use IN to see if an id is in the list.
You need to treat ufn_CSVToTable like it's a table. So you can join the function:
JOIN ufn_CSVToTable(#PassedInIDs) uf ON database.ID = uf.[String]
I suggest using XML for this in SQL 2005. Somewhat bulkier, but it can be easier. It allows you to select the XML into a table which can then be joined or inserted etc.
Look at Sql Server's OPENXML() if you haven't already.
For example, you could pass in something like:
'12...'
and then use:
exec sp_xml_preparedocument #doc OUTPUT, #xmlParam
SELECT element
FROM OPENXML (#doc, 'Array/Value', 2) WITH (element varchar(max) 'text()')
That should be a start
this may be solved by 6 ways as mentioned in Narayana's article Passing a list/array to an SQL Server stored procedure
And my most strait forward implementation is
declare #statement nvarchar(256)
set #statement = 'select * from Persons where Persons.id in ('+ #PassedInIDs +')'
exec sp_executesql #statement
-
Here is what I have found and tested:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE FUNCTION [dbo].[SplitStrings] ( #IDsList VARCHAR(MAX) )
RETURNS #IDsTable TABLE ( [ID] VARCHAR(MAX) )
AS
BEGIN
DECLARE #ID VARCHAR(MAX)
DECLARE #Pos VARCHAR(MAX)
SET #IDsList = LTRIM(RTRIM(#IDsList)) + ','
SET #Pos = CHARINDEX(',', #IDsList, 1)
IF REPLACE(#IDsList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #ID = LTRIM(RTRIM(LEFT(#IDsList, #Pos - 1)))
IF #ID <> ''
BEGIN
INSERT INTO #IDsTable
( [ID] )
VALUES ( CAST(#ID AS VARCHAR) )
END
SET #IDsList = RIGHT(#IDsList, LEN(#IDsList) - #Pos)
SET #Pos = CHARINDEX(',', #IDsList, 1)
END
END
RETURN
END
GO
Here is how function Call:
SELECT * FROM dbo.SplitStrings('123,548,198,547,965')
Try this:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE ','+#Ids+',' LIKE '%,'+CONVERT(VARCHAR(50),tableid)+',%';