We have a stored procedure
CREATE PROCEDURE [dbo].[usp_s_eval_expr]
#formula NVARCHAR(max),
#result SQL_VARIANT OUTPUT
AS
BEGIN
DECLARE #sql NVARCHAR(max)
DECLARE #cal_value FLOAT
SET #sql = N'DECLARE #x xml=''''SELECT #cal_value=CAST(#x.query('''+#formula+''') as nvarchar(max))'
EXEC sp_executesql #sql,N'#cal_value FLOAT OUTPUT',#cal_value OUTPUT
SET #result = #cal_value
SELECT #result
END
GO
It is created to evaluate mathematical expressions, I understand that there are better ways to solve the expression, it is deployed in production and I don't have the right to modify it.
It works with most of the functions but is throwing errors while using it to find the absolute value of a number.
For example,
DECLARE #formula NVARCHAR(max) = ' abs(-1.5) '
DECLARE #result SQL_VARIANT
EXEC [usp_s_eval_expr] #formula, #result OUTPUT
SELECT #result
This throws the error following error whereas ceiling and round functions work well.
Msg 50000, Level 16, State 1, Procedure usp_s_eval_expr, Line 32 [Batch Start Line 0]
XQuery [query()]: There is no function '{http://www.w3.org/2004/07/xpath-functions}:abs()'
Is there any way to achieve the absolute value of a number order than checking less than 0, the reason why I don't want to check less than is the expression that results in -1.5 is very big, and I don't want to use it twice to achieve the abs functionality.
According to the documentation available by download from https://learn.microsoft.com/en-us/openspecs/sql_standards/MS-SQLSTANDLP/89fb00b1-4b9e-4296-92ce-a2b3f7ca01d2 SQL Server does not support the fn:abs() function.
Also: are you sure about the namespace {http://www.w3.org/2004/07/xpath-functions}. That's from an early draft of XPath 2.0, not the final W3C spec. But I don't think SQL Server ever implemented the final spec anyway.
Another way to get the absolute value might be to convert to a string and strip the minus sign.
Related
create or alter proc printThisAsTableHeader
#n as int
as
begin
select 'values' as '#n is good'
end
go
exec printThisAsTableHeader 2
the above code's output is:
#n is good
values
I want #n should be replaced with its value.
I try many ways like format, concat, convert, cast, and concatenation operator as well, but unable to do that.
objective:
I just want to make a procedure that tacks an int parameter #n
and print nth highest salary.
n=7
7th highest salary
salary value
As I mention in my comment, this has a strong smell of an XY Problem. The above doesn't appear to actually achieve anything on it's own, so you're clearly trying to solve a different problem, that very likely doesn't even need dynamic SQL.
If you "must" (which I doubt) do this, then you you can achieve this like below:
CREATE OR ALTER PROC dbo.printThisAsTableHeader #n int AS
BEGIN
DECLARE #SQL nvarchar(MAX) = N'SELECT ''values'' AS ' + QUOTENAME(CONCAT(#n,N' is good')) + N';'
EXEC sys.sp_executesql #SQL;
END;
DB<>Fiddle
I am using SQL Server 2012 (v11.0.2100) and I want to create a sequence that starts with a random (dynamic) number but I wasn't able to do this, also I put my effort to find a good solution for this but I haven't found something that will satisfy me.
The case that I tried and failed:
DECLARE #sth bigint
SET #sth = 1000
ALTER SEQUENCE StreamEntrySequence
RESTART WITH #sth;
Error :
Incorrect syntax near '#sth'
An ugly solution
declare #sth bigint;
declare #i bigint;
SET #sth = 100000 ;
while #i<#sth;
BEGIN
SET #i= next value for StreamEntrySequence;
END
Is there other way to set the current value or the start value to a random value? Maybe using server procedures?
As has been mentioned, this would require dynamic SQL since alter sequence requires a constant for the restart argument.
You might do something like this, then:
DECLARE #sth bigint;
SET #sth = 1000;
DECLARE #sql nvarchar(max);
SET #sql = N'ALTER SEQUENCE StreamEntrySequence RESTART WITH ' + cast(#sth as nvarchar(20)) + ';';
EXEC SP_EXECUTESQL #sql;
Try
ALTER SEQUENCE foo.fee
RESTART
Or:
ALTER SEQUENCE foo.fee
RESTART WITH 1
http://msdn.microsoft.com/en-us/library/ff878572.aspx
Just use
SELECT setval('seq',0,false);
It sets the value of the sequence "seq" to 0.
I have a very basic question about declaring some variables in TSQL.
When i declare a numeric variable in TSQL like this, everthing is ok:
DECLARE #Value AS NUMERIC(18,2) = 1.23
But what if i want the decimals to be set with a parameter?
DECLARE #NrOfDecimals AS INTEGER = 2
DECLARE #Value AS NUMERIC(18,#NrOfDecimals) = 1.23
--This second line throws a compile error "Expecting INTEGER"
So the second line throws a compile error "Expecting INTEGER".
But isn't #NrOfDecimals an Integer? So why is the compiler complaining??
Am i missing something??
You could dynamically create your queries and execute them.
This will work for the situation in your question:
DECLARE #NrOfDecimals AS INTEGER = 2
DECLARE #myCommand as NVARCHAR(1000)
SET #myCommand = 'DECLARE #value as numeric(18,' + CAST(#NrOfDecimals as VARCHAR) + ') = 1.23'
execute sp_executesql #myCommand
In order to do this for every situation, you should create a table with all queries you will have to run.
In order to create all of these queries you will have to use a cursor.
Documentation for cursors you can find here:
http://msdn.microsoft.com/en-us/library/ms180169.aspx
http://www.mssqltips.com/sqlservertip/1599/sql-server-cursor-example/
Also, I have answered another question and gave an example with a cursor:
SQL Query to find a column name throughout the Database
I was wondering if I can make a stored procedure that insert values into dynamic table.
I tried
create procedure asd
(#table varchar(10), #id int)
as
begin
insert into #table values (#id)
end
also defining #table as table var
Thanks for your help!
This might work for you.
CREATE PROCEDURE asd
(#table nvarchar(10), #id int)
AS
BEGIN
DECLARE #sql nvarchar(max)
SET #sql = 'INSERT INTO ' + #table + ' (id) VALUES (' + CAST(#id AS nvarchar(max)) + ')'
EXEC sp_executesql #sql
END
See more here: http://msdn.microsoft.com/de-de/library/ms188001.aspx
Yes, to implement this directly, you need dynamic SQL, as others have suggested. However, I would also agree with the comment by #Tomalak that attempts at universality of this kind might result in less secure or less efficient (or both) code.
If you feel that you must have this level of dynamicity, you could try the following approach, which, although requiring more effort than plain dynamic SQL, is almost the same as the latter but without the just mentioned drawbacks.
The idea is first to create all the necessary insert procedures, one for every table in which you want to insert this many values of this kind (i.e., as per your example, exactly one int value). It is crucial to name those procedures uniformly, for instance using this template: TablenameInsert where Tablename is the target table's name.
Next, create this universal insert procedure of yours as follows:
CREATE PROCEDURE InsertIntValue (
#TableName sysname,
#Value int
)
AS
BEGIN
DECLARE #SPName sysname;
SET #SPName = #TableName + 'Insert';
EXECUTE #SPName #Value;
END;
As can be seen from the manual, when invoking a module with the EXECUTE command, you can specify a variable instead of the actual module name. The variable in this case should be of a string type and is supposed to contain the name of the module to execute. This is not dynamic SQL, because the syntax is not the same. (For this to be dynamic SQL, the variable would need to be enclosed in brackets.) Instead, this is essentially parametrising of the module name, probably the only kind of natively supported name parametrisation in (Transact-)SQL.
Like I said, this requires more effort than dynamic SQL, because you still have to create all the many stored procedures that this universal SP should be able to invoke. Nevertheless, as a result, you get code that is both secure (the #SPName variable is viewed by the server only as a name, not as an arbitrary snippet of SQL) and efficient (the actual stored procedure being invoked already exists, i.e. it is already compiled and has a query plan).
You'll need to use Dynamic SQL.
To create Dynamic SQL, you need to build up the query as a string. Using IF statements and other logic to add your variables, etc.
Declare a text variable and use this to concatenate together your desired SQL.
You can then execute this code using the EXEC command
Example:
DECLARE #SQL VARCHAR(100)
DECLARE #TableOne VARCHAR(20) = 'TableOne'
DECLARE #TableTwo VARCHAR(20) = 'TableTwo'
DECLARE #SomeInt INT
SET #SQL = 'INSERT INTO '
IF (#SomeInt = 1)
SET #SQL = #SQL + #TableOne
IF (#SomeInt = 2)
SET #SQL = #SQL + #TableTwo
SET #SQL = #SQL + ' VALUES....etc'
EXEC (#SQL)
However, something you should really watch out for when using this method is a security problem called SQL Injection.
You can read up on that here.
One way to guard against SQL injection is to validate against it in your code before passing the variables to SQL-Server.
An alternative way (or probably best used in conjecture) is instead of using the EXEC command, use a built-in stored procedure called sp_executesql.
Details can be found here and usage description is here.
You'll have to build your SQL slightly differently and pass your parameters to the stored procedure as arguments as well as the #SQL.
I have a conditional expression stored inside a string variable,
For example :
declare #x int=501,#y int=0,#str varchar(max)
set #str='if #x > 500 set #y=1000 else set #y=500'
Can anybody anybody tell me how can I calculate this string and get the result in SQL Server 2008 from inside a stored procedure.
Thanks in advance
SD
You need sp_executesql, but it has some nuances especially with passing variables in and getting values out.
declare #x int=501,#y int=0,#str Nvarchar(max)
set #str='if #x > 500 set #y=1000 else set #y=500'
exec sp_executesql #str, N'#x int, #y int output', #x, #y output
select #y
-- result
1000
Note that #str has been redefined as NVARCHAR. sp_executesql expects the query to be NVARCHAR.
2nd thing to note is that the output clause has to be used twice for any variable that needs to keep the value changed inside the sp_executesql call
You would need to use dynamic SQL using sp_executesql
Using sp_executesql
[Although, I would examine why you need to do this...]