Related
I had to write a SP to generate a string with the combination of Country (7 chars) + Province (1 char) + numeric number starts from 01. for eg: JAMAICAK01
where JAMAICA (Country) , K (Province) and 01 is the numeric number that gets incremented by 1 for each transaction.
The issue I have here is the generated string length max is 10, can be lesser than 10 but not >10.
It should be handled in a way with certain rules like
the combination don't exist
When the numeric unit changes from tens to hundreds making the string
length >10, I need to remove the right chars for eg JAMAICAKKK10 to
JAMAICAK10 from the right to make the total max length 10.
In my code below I tried to check if the combination exists and I get the max of it and do the numeric increment from the last one. think it can be done in a better way.
Declare #Province char(2)
Declare #Country varchar(10)
declare #CounProv varchar(10)
Declare #SimilarCounPRov varchar(max) = '';
declare #FinalString nvarchar(12)
declare #s varchar(50)
declare #s1 varchar(50)
declare #s2 varchar(50)
Set #Province = LEFT('KINGSTON', 1)
Set #Country = LEFT('JAMAICA', 7)
Set #CounProv = #Country+#Province
Select #SimilarCounPRov = MAX(field1) from dbo.table where field1
LIKE '%JAMAICAK%'
if #SimilarCounPRov IS NOT NULL
BEGIN
Set #s = (select fn_AlphaOnly('%JAMAICAK99%')) -- returns JAMAICAK
Set #s1 = (select fn_NumericOnly('%JAMAICAK99%')) -- returns 199
set #s2= #s1 +1 -- increment by 1
if len(#FinalString) > 10
----
need help here----`
I'm not sure that I understood all your requirements but if you need to generate strings like : JAMAICAK1,JAMAICAK2,...JAMAICAK10...,JAMAICAK11,...JAMAICA100,JAMAICA101,...JAMAIC1000,JAMAIC1001...
You can try to exploit this piece of code :
Declare #Province char(2)
Declare #Country varchar(10)
Declare #CounProv varchar(10)
Declare #value int
Declare #str_value VARCHAR(100)
Set #Province = LEFT('KINGSTON', 1)
Set #Country = LEFT('JAMAICA', 7)
Set #value = 999999
Set #CounProv = #Country+#Province
Set #str_value = (select CAST(#value AS varchar(100)))
select LEFT(#CounProv,10-LEN(#str_value))+#str_value
Tell me if it helps.
I have this table:
-----------------------------------------------
code intvalue checkrole result
-----------------------------------------------
A01 14 A02-A03 true
A02 24 A04 false
A03 10 A04 false
A04 12 A02/2 true
I would like to fill the column result with a query or sp, based on the role described into the column checkrole, something like Excel, any ideas?
I apologize to everyone, I explained myself wrongly.
For example:
A01 14 A02-A03 true
in this case, I would like to interpret the role and get 24-10 = 14 ie true
UPDATE 2:
Hello everyone and thanks for your interest. HABO, I'm working in this direction "substitute the values into the expression (" 24-10 ") ....":
TBL
code intValue checkRule result
A01 14 select A02-A03 from Table_1 NULL
A02 24 select A04 from Table_1 NULL
A03 10 select A04 from Table_1 NULL
A04 12 select A02 / 2 from Table_1 NULL
SP
CREATE PROCEDURE [dbo]. [Test]
AS
BEGIN
DECLARE #code VARCHAR (50)
DECLARE #intvalue INT
DECLARE #checkrule VARCHAR (50)
DECLARE #cTbl AS CURSOR
SET #cTbl = CURSOR FOR SELECT code
, intValue
, checkRule
FROM [dbo]. [Table_1]
OPEN #cTbl FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
WHILE ## FETCH_STATUS = 0
BEGIN
declare #statement nvarchar (4000), #Result int, #Parm nvarchar (20)
SET #statement = 'select #Result = 11 + 7'
SET #Parm = '#Result int output'
EXEC sp_executesql #statement, #Parm, # Result OUT
print #Result
FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
END
CLOSE #cTbl
DEALLOCATE #cTbl
END
UPDATE 3:
It was what I was looking for. I'm not an expert, but I learn from mistakes and I thank those who teach me something new, thanks to all those who participated, and above all thanks to HOBO, good evening
I would probably write a function that would create a list of values from your checkRole (eg it would give me list 'A02', 'A03', 'A04' from A02-A04 and then I would write an update statement with 'contains' statement in it
using the table that you show you can update the values of result column with the next query
UPDATE TableName
SET result = CASE
WHEN checkrole = 'A02-A03' THEN "true"
WHEN checkrole = 'A04' THEN "false"
WHEN checkrole = 'A02/2' THEN "true"
ELSE "setDefaultValue"
END
WHERE code in (A01,A02,A03,A04);
at the last line you specify the code value of row to update. If you execute this query with the column result empty, it will complete the table and result into the same data as the one you post above.
(The query work good on Mysql)
Executive Summary: This is a terrible idea. Back away slowly and no one gets hurt.
Disclaimer: I'm not responsible for the costs of any therapy required after reading beyond this point.
From your Update 2 it appears that you have control over the format of CheckRule. It is much easier to require that the user delimit the Codes, e.g. using '«A04»' rather than 'A04', instead of trying to find them using TSQL. You don't need to worry about problems like substituting 'A02' with 66 when the CheckRule is 'A026 - BA02' and getting '666 - B66'.
What follows is simply dreadful. It demonstrates a brute force approach to taking expressions with delimited "variables", e.g. '«A04»', replacing all occurrences of each "variable" with its integer value, evaluating the resulting expression and retrieving the result.
Instead of using something as distasteful as a cursor, it uses two nested cursors to waddle through the CheckRules and Codes. It would be more efficient to parse the "variables" out of the expression and substitute values as needed rather than using a try-everything approach, but TSQL doesn't excel at implementing parsers.
For extra credit it also brings along the blessing of embracing SQL Injection. Try a CheckRule like 'A01; select 1 / 0;'. Don't try one like '-1; drop database LuckyGuess;'.
-- Sample data.
declare #Samples as Table
( SampleId Int Identity, Code NVarChar(8), IntValue Int, CheckRule NVarChar(64), Result Bit );
insert into #Samples ( Code, IntValue, CheckRule ) values
( N'A01', 14, N'«A02» - «A03»' ),
( N'A02', 24, N'«A04»' ),
( N'A03', 10, N'«A04»' ),
( N'A04', 12, N'«A02» / 2' );
select * from #Samples;
-- Process the data.
declare Oy cursor forward_only fast_forward read_only
for select SampleId, Code, IntValue, CheckRule from #Samples;
declare #SampleId as Int, #Code as NVarChar(8), #IntValue as Int, #CheckRule as NVarChar(64);
declare Vey cursor forward_only fast_forward read_only
for select Code, IntValue from #Samples;
declare #VariableCode as NVarChar(8), #VariableIntValue as Int
-- For each row's CheckRule ...
open Oy;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
while ##Fetch_Status = 0
begin
-- Copy the CheckRule so that we can build the #Expression to evaluate.
declare #Expression as NVarChar(64) = #CheckRule;
-- For each Code that could appear in an #Expression ...
open Vey;
fetch next from Vey into #VariableCode, #VariableIntValue;
while ##Fetch_Status = 0
begin
-- Replace any occurrences of the Code in the #Expression with the corresponding integer value.
set #Expression = Replace( #Expression, N'«' + #VariableCode + N'»',
Cast( #VariableIntValue as NVarChar(10) ) );
fetch next from Vey into #VariableCode, #VariableIntValue;
end;
close Vey;
-- Make the #Expression an executable statement.
set #Expression = N'set #IntResult = ' + #Expression;
declare #Result as Int;
-- Evaluate the #Expression and get the #Result .
execute sp_executesql #Expression, N'#IntResult Int output', #IntResult = #Result output;
select #SampleId as SampleId, #Code as Code, #IntValue as IntValue, #CheckRule as CheckRule,
#Expression as Expression, #Result as Result;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
end;
close Oy;
deallocate Oy;
In the event it isn't clear, don't. Just don't.
Consider the following T-SQL code snippet:
CREATE PROC dbo.SquareNum(#i INT OUTPUT)
AS
BEGIN
SET #i = #i * #i
--SELECT #i
END
GO
DECLARE #a INT = 3, #b INT = 5
EXEC dbo.SquareNum #a OUTPUT
EXEC dbo.SquareNum #b
SELECT #a AS ASQUARE, #b AS BSQUARE
GO
DROP PROC dbo.SquareNum
The result set is:
ASQUARE BSQUARE
----------- -----------
9 5
As can be seen, #b is not squared, b/c it was not passed-in as output parameter (no OUTPUT qualifier when passing in the parameter).
I would like to know if there is a way I could check within stored procedure body (dbo.SquareNum body in this case) to see if a parameter has indeed been passed in as an OUTPUT parameter?
------ THIS WILL GIVE YOU THE BOTH VALUE IN squared------
CREATE PROC dbo.SquareNum(#i INT OUTPUT)
AS
BEGIN
SET #i = #i * #i
--SELECT #i
END
GO
DECLARE #a INT = 3, #b INT = 5
EXEC dbo.SquareNum #a OUTPUT
EXEC dbo.SquareNum #b OUTPUT
SELECT #a AS ASQUARE, #b AS BSQUARE
GO
DROP PROC dbo.SquareNum
-----TO CHECK STORED PROCEDURE BODY-----
SELECT OBJECT_NAME(object_id),
OBJECT_DEFINITION(object_id)
FROM sys.procedures
WHERE OBJECT_DEFINITION(object_id) =(SP_NAME)
Actually, there is a very simple way!
Make the parameter optional by setting a default value (#Qty AS Money = 0 Below)
Then, pass a value OTHER THAN THE DEFAULT when calling the procedure. Then immediately test the value and if it is other than the default value you know the variable has been passed.
Create Procedure MyProcedure(#PN AS NVarchar(50), #Rev AS NVarchar(5), #Qty AS Money = 0 OUTPUT) AS BEGIN
DECLARE #QtyPassed AS Bit = 0
IF #Qty <> 0 SET #QtyPassed = 1
Of course that means the variable cannot be used for anything other than OUTPUT unless you have a default value that you know will never be used as an INPUT value.
You can do this by query to sys views:
select
p.name as proc_name,
par.name as parameter_name,
par.is_output
from sys.procedures p
inner join sys.parameters par on par.object_id=p.object_id
where p.name = 'SquareNum'
or check in Management Studio in database tree:
[database] -> Programmability -> Stored Procedures -> [procedure] -> Parameters
Maybe I'm wrong but I don't believe it's possible. OUTPUT is part of the stored procedure definition so you should know when a parameter is or not OUTPUT. There is no way to set it dynamically so I think it's pointless to determine by code when a parameter is output or not because you already know it.
If you are trying to write a dynamic code, Piotr Lasota's answer should drive you to the correct way to realize when a parameter is Output.
Use the following query to get the name of all the parameters and to check if it is a output parameter:
select name, is_output from sys.parameters
This SQL Server 2005 T-SQL code:
DECLARE #Test1 varchar;
SET #Test1 = 'dog';
DECLARE #Test2 varchar(10);
SET #Test2 = 'cat';
SELECT #Test1 AS Result1, #Test2 AS Result2;
produces:
Result1 = d
Result2 = cat
I would expect either
The assignment SET #Test1 =
'dog'; to fail because there isn't
enough room in #Test1
Or the SELECT to return 'dog' in the Result1 column.
What is up with #Test1? Could someone please explain this behavior?
Let me answer with some quotes from the SQL Server documentation.
char and varchar
varchar[(n)]
...
When n is not specified in a data definition or variable declaration statement, the default length is 1.
Converting Character Data
When character expressions are converted to a character data type of a different size, values that are too long for the new data type are truncated.
So, your varchar is declared as a varchar(1), and the implicit conversion in your SET statement (from a string literal of length 3 to a varchar(1)) truncates dog to d.
the varchar is defaulting to length one
DECLARE #Test1 varchar;
try this, which will uses a simple function that takes a sql_variant and returns the data type info back:
CREATE FUNCTION [dbo].[yourFunction]
(
#InputStr sql_variant --can not be varchar(max) or nvarchar(max)
)
returns
varchar(8000)
BEGIN
DECLARE #Value varchar(50)
--can use SQL_VARIANT_PROPERTY(#InputStr,'BaseType') to determine given datatype
--do whatever you want with #inputStr here
IF #InputStr IS NULL
BEGIN
SET #value= 'was null'
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='varchar'
BEGIN
--your special code here
SET #value= 'varchar('+CONVERT(varchar(10),SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='datetime'
BEGIN
--your special code here
SET #value= 'datetime - '+CONVERT(char(23),#InputStr,121)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='nvarchar'
BEGIN
--your special code here
SET #value= 'nvarchar('+CONVERT(varchar(10),CONVERT(int,SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))/2)+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE
BEGIN
--your special code here
set #value= 'unknown!'
END
RETURN #value
END
GO
DECLARE #Test1 varchar;
SET #Test1 = 'dog';
DECLARE #Test2 varchar(10);
SET #Test2 = 'cat';
SELECT #Test1 AS Result1, #Test2 AS Result2;
select [dbo].[yourFunction](#test1)
output:
Result1 Result2
------- ----------
d cat
(1 row(s) affected)
-------------------
varchar(1) - d
(1 row(s) affected)
moral of the story, don't be lazy, specify a length on all of your varchar values!!!
Is there a way to make a TSQL variable constant?
No, but you can create a function and hardcode it in there and use that.
Here is an example:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
One solution, offered by Jared Ko is to use pseudo-constants.
As explained in SQL Server: Variables, Parameters or Literals? Or… Constants?:
Pseudo-Constants are not variables or parameters. Instead, they're simply views with one row, and enough columns to support your constants. With these simple rules, the SQL Engine completely ignores the value of the view but still builds an execution plan based on its value. The execution plan doesn't even show a join to the view!
Create like this:
CREATE SCHEMA ShipMethod
GO
-- Each view can only have one row.
-- Create one column for each desired constant.
-- Each column is restricted to a single value.
CREATE VIEW ShipMethod.ShipMethodID AS
SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
,CAST(2 AS INT) AS [ZY - EXPRESS]
,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
,CAST(4 AS INT) AS [OVERNIGHT J-FAST]
,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
Then use like this:
SELECT h.*
FROM Sales.SalesOrderHeader h
JOIN ShipMethod.ShipMethodID const
ON h.ShipMethodID = const.[OVERNIGHT J-FAST]
Or like this:
SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)
My workaround to missing constans is to give hints about the value to the optimizer.
DECLARE #Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = #Constant
OPTION( OPTIMIZE FOR (#Constant = 123))
This tells the query compiler to treat the variable as if it was a constant when creating the execution plan. The down side is that you have to define the value twice.
No, but good old naming conventions should be used.
declare #MY_VALUE as int
There is no built-in support for constants in T-SQL. You could use SQLMenace's approach to simulate it (though you can never be sure whether someone else has overwritten the function to return something else…), or possibly write a table containing constants, as suggested over here. Perhaps write a trigger that rolls back any changes to the ConstantValue column?
Prior to using a SQL function run the following script to see the differences in performance:
IF OBJECT_ID('fnFalse') IS NOT NULL
DROP FUNCTION fnFalse
GO
IF OBJECT_ID('fnTrue') IS NOT NULL
DROP FUNCTION fnTrue
GO
CREATE FUNCTION fnTrue() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN 1
END
GO
CREATE FUNCTION fnFalse() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN ~ dbo.fnTrue()
END
GO
DECLARE #TimeStart DATETIME = GETDATE()
DECLARE #Count INT = 100000
WHILE #Count > 0 BEGIN
SET #Count -= 1
DECLARE #Value BIT
SELECT #Value = dbo.fnTrue()
IF #Value = 1
SELECT #Value = dbo.fnFalse()
END
DECLARE #TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, #TimeStart, #TimeEnd) AS VARCHAR) + ' elapsed, using function'
GO
DECLARE #TimeStart DATETIME = GETDATE()
DECLARE #Count INT = 100000
DECLARE #FALSE AS BIT = 0
DECLARE #TRUE AS BIT = ~ #FALSE
WHILE #Count > 0 BEGIN
SET #Count -= 1
DECLARE #Value BIT
SELECT #Value = #TRUE
IF #Value = 1
SELECT #Value = #FALSE
END
DECLARE #TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, #TimeStart, #TimeEnd) AS VARCHAR) + ' elapsed, using local variable'
GO
DECLARE #TimeStart DATETIME = GETDATE()
DECLARE #Count INT = 100000
WHILE #Count > 0 BEGIN
SET #Count -= 1
DECLARE #Value BIT
SELECT #Value = 1
IF #Value = 1
SELECT #Value = 0
END
DECLARE #TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, #TimeStart, #TimeEnd) AS VARCHAR) + ' elapsed, using hard coded values'
GO
If you are interested in getting optimal execution plan for a value in the variable you can use a dynamic sql code. It makes the variable constant.
DECLARE #var varchar(100) = 'some text'
DECLARE #sql varchar(MAX)
SET #sql = 'SELECT * FROM table WHERE col = '''+#var+''''
EXEC (#sql)
For enums or simple constants, a view with a single row has great performance and compile time checking / dependency tracking ( cause its a column name )
See Jared Ko's blog post https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/
create the view
CREATE VIEW ShipMethods AS
SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
,CAST(2 AS INT) AS [ZY - EXPRESS]
,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
, CAST(4 AS INT) AS [OVERNIGHT J-FAST]
,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
use the view
SELECT h.*
FROM Sales.SalesOrderHeader
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods )
Okay, lets see
Constants are immutable values which are known at compile time and do not change for the life of the program
that means you can never have a constant in SQL Server
declare #myvalue as int
set #myvalue = 5
set #myvalue = 10--oops we just changed it
the value just changed
Since there is no build in support for constants, my solution is very simple.
Since this is not supported:
Declare Constant #supplement int = 240
SELECT price + #supplement
FROM what_does_it_cost
I would simply convert it to
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
Obviously, this relies on the whole thing (the value without trailing space and the comment) to be unique. Changing it is possible with a global search and replace.
There are no such thing as "creating a constant" in database literature. Constants exist as they are and often called values. One can declare a variable and assign a value (constant) to it. From a scholastic view:
DECLARE #two INT
SET #two = 2
Here #two is a variable and 2 is a value/constant.
SQLServer 2022 (currently only as Preview available) is now able to Inline the function proposed by SQLMenace, this should prevent the performance hit described by some comments.
CREATE FUNCTION fnConstant() RETURNS INT AS BEGIN RETURN 2 END GO
SELECT is_inlineable FROM sys.sql_modules WHERE [object_id]=OBJECT_ID('dbo.fnConstant');
is_inlineable
1
SELECT dbo.fnConstant()
ExecutionPlan
To test if it also uses the value coming from the Function, I added a second function returning value "1"
CREATE FUNCTION fnConstant1()
RETURNS INT
AS
BEGIN
RETURN 1
END
GO
Create Temp Table with about 500k rows with Value 1 and 4 rows with Value 2:
DROP TABLE IF EXISTS #temp ;
create table #temp (value_int INT)
DECLARE #counter INT;
SET #counter = 0
WHILE #counter <= 500000
BEGIN
INSERT INTO #temp VALUES (1);
SET #counter = #counter +1
END
SET #counter = 0
WHILE #counter <= 3
BEGIN
INSERT INTO #temp VALUES (2);
SET #counter = #counter +1
END
create index i_temp on #temp (value_int);
Using the describe plan we can see that the Optimizer expects 500k values for
select * from #temp where value_int = dbo.fnConstant1(); --Returns 500001 rows
Constant 1
and 4 rows for
select * from #temp where value_int = dbo.fnConstant(); --Returns 4rows
Constant 2
Robert's performance test is interesting. And even in late 2022, the scalar functions are much slower (by an order of magnitude) than variables or literals. A view (as suggested mbobka) is somewhere in-between when used for this same test.
That said, using a loop like that in SQL Server is not something I'd ever do, because I'd normally be operating on a whole set.
In SQL 2019, if you use schema-bound functions in a set operation, the difference is much less noticeable.
I created and populated a test table:
create table #testTable (id int identity(1, 1) primary key, value tinyint);
And changed the test so that instead of looping and changing a variable, it queries the test table and returns true or false depending on the value in the test table, e.g.:
insert #testTable(value)
select case when value > 127
then #FALSE
else #TRUE
end
from #testTable with(nolock)
I tested 5 scenarios:
hard-coded values
local variables
scalar functions
a view
a table-valued function
running the test 10 times, yielded the following results:
scenario
min
max
avg
scalar functions
233
259
240
hard-coded values
236
265
243
local variables
235
278
245
table-valued function
243
272
253
view
244
267
254
Suggesting to me, that for set-based work in (at least) 2019 and better, there's not much in it.
set nocount on;
go
-- create test data table
drop table if exists #testTable;
create table #testTable (id int identity(1, 1) primary key, value tinyint);
-- populate test data
insert #testTable (value)
select top (1000000) convert(binary (1), newid())
from sys.all_objects a
, sys.all_objects b
go
-- scalar function for True
drop function if exists fnTrue;
go
create function dbo.fnTrue() returns bit with schemabinding as
begin
return 1
end
go
-- scalar function for False
drop function if exists fnFalse;
go
create function dbo.fnFalse () returns bit with schemabinding as
begin
return 0
end
go
-- table-valued function for booleans
drop function if exists dbo.tvfBoolean;
go
create function tvfBoolean() returns table with schemabinding as
return
select convert(bit, 1) as true, convert(bit, 0) as false
go
-- view for booleans
drop view if exists dbo.viewBoolean;
go
create view dbo.viewBoolean with schemabinding as
select convert(bit, 1) as true, convert(bit, 0) as false
go
-- create table for results
drop table if exists #testResults
create table #testResults (id int identity(1,1), test int, elapsed bigint, message varchar(1000));
-- define tests
declare #tests table(testNumber int, description nvarchar(100), sql nvarchar(max))
insert #tests values
(1, N'hard-coded values', N'
declare #testTable table (id int, value bit);
insert #testTable(id, value)
select id, case when t.value > 127
then 0
else 1
end
from #testTable t')
, (2, N'local variables', N'
declare #FALSE as bit = 0
declare #TRUE as bit = 1
declare #testTable table (id int, value bit);
insert #testTable(id, value)
select id, case when t.value > 127
then #FALSE
else #TRUE
end
from #testTable t'),
(3, N'scalar functions', N'
declare #testTable table (id int, value bit);
insert #testTable(id, value)
select id, case when t.value > 127
then dbo.fnFalse()
else dbo.fnTrue()
end
from #testTable t'),
(4, N'view', N'
declare #testTable table (id int, value bit);
insert #testTable(id, value)
select id, case when value > 127
then b.false
else b.true
end
from #testTable t with(nolock), viewBoolean b'),
(5, N'table-valued function', N'
declare #testTable table (id int, value bit);
insert #testTable(id, value)
select id, case when value > 127
then b.false
else b.true
end
from #testTable with(nolock), dbo.tvfBoolean() b')
;
declare #testNumber int, #description varchar(100), #sql nvarchar(max)
declare #testRuns int = 10;
-- execute tests
while #testRuns > 0 begin
set #testRuns -= 1
declare testCursor cursor for select testNumber, description, sql from #tests;
open testCursor
fetch next from testCursor into #testNumber, #description, #sql
while ##FETCH_STATUS = 0 begin
declare #TimeStart datetime2(7) = sysdatetime();
execute sp_executesql #sql;
declare #TimeEnd datetime2(7) = sysdatetime()
insert #testResults(test, elapsed, message)
select #testNumber, datediff_big(ms, #TimeStart, #TimeEnd), #description
fetch next from testCursor into #testNumber, #description, #sql
end
close testCursor
deallocate testCursor
end
-- display results
select test, message, count(*) runs, min(elapsed) as min, max(elapsed) as max, avg(elapsed) as avg
from #testResults
group by test, message
order by avg(elapsed);
The best answer is from SQLMenace according to the requirement if that is to create a temporary constant for use within scripts, i.e. across multiple GO statements/batches.
Just create the procedure in the tempdb then you have no impact on the target database.
One practical example of this is a database create script which writes a control value at the end of the script containing the logical schema version. At the top of the file are some comments with change history etc... But in practice most developers will forget to scroll down and update the schema version at the bottom of the file.
Using the above code allows a visible schema version constant to be defined at the top before the database script (copied from the generate scripts feature of SSMS) creates the database but used at the end. This is right in the face of the developer next to the change history and other comments, so they are very likely to update it.
For example:
use tempdb
go
create function dbo.MySchemaVersion()
returns int
as
begin
return 123
end
go
use master
go
-- Big long database create script with multiple batches...
print 'Creating database schema version ' + CAST(tempdb.dbo.MySchemaVersion() as NVARCHAR) + '...'
go
-- ...
go
-- ...
go
use MyDatabase
go
-- Update schema version with constant at end (not normally possible as GO puts
-- local #variables out of scope)
insert MyConfigTable values ('SchemaVersion', tempdb.dbo.MySchemaVersion())
go
-- Clean-up
use tempdb
drop function MySchemaVersion
go