Related
I have following query :
Set #OrderStatuses = 'Cancelled,Complete'
Select *
From TableName
Where Status in (#OrderStatuses)
The Status is what I am receiving externally and it fails to fetch any result as, what we actually need to process is:
Select *
From TableName
Where Status in ('Cancelled', 'Complete');
#OrderStatuses can contain any number of comma-separated values.
One method is dynamic SQL:
declare #sql nvarchar(max);
set #sql = 'Select * from TableName Where Status in (#list)';
set #sql = replace(#sql, '#list', '#OrderStatuses');
exec sp_executesql #sql;
Note: You cannot pass a list in as a parameter.
You can also use like:
Select *
from TableName
Where ',' + #OrderStatuses + ',' like '%,' + Status + ',%';
However, this cannot use an index for the the comparison.
You will need to use split string function and process the rest..
;with cte
as
(
select * from split_strings(#orderstatus,',')
)
select * from table where status in (select item from cte)
You must add the list as a Table-Valued Parameter
Here are two methods I use to convert any IEnumerable list of items (in this case, integers) into a Table Valued Parameter. You have to have created/defined a User Defined type (UDT) for the resultant table in your database. In the example below the UDT is named dbo.keyIds and is created (once) with this SQL:
CREATE TYPE [dbo].[KeyIds]
AS TABLE(pkId int NOT NULL, PRIMARY KEY CLUSTERED
( [pkId] ASC) WITH (IGNORE_DUP_KEY = OFF)
)
The c# code is:
public class DbParamList : List<IDbDataParameter>
{
public void AddSQLTableParm<T>(
string parmName, IEnumerable<T> values)
{
var parm = new SqlParameter(parmName, CreateDataTable(values))
{
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.keyIds"
};
Add(parm);
}
internal static DataTable CreateDataTable<T>(IEnumerable<T> values)
{
var dt = new DataTable();
var props = typeof (T).GetProperties();
if (props.Length > 0)
{
foreach (var col in props)
dt.Columns.Add(col.Name, col.PropertyType);
foreach (var id in values)
{
var newRow = dt.NewRow();
foreach (var prop in id.GetType().GetProperties())
newRow[prop.Name] = prop.GetValue(id, null);
dt.Rows.Add(newRow);
}
}
else
{
dt.Columns.Add("ids");
foreach (var id in values) dt.Rows.Add(id);
}
return dt;
}
}
I want use where when input variable is not null.
For example, when #custid is not null create this query:
Select *
from customers
where custid = #custid
and when #custid is null, then use this query instead:
Select *
from customers
How can I do this?
I searched on google, but haven't found any similar answer. Thanks
You can use the following query:
Select *
from customers
where #custid IS NULL OR custid=#custid
In this case if your variable is null only the condition #custid IS NULL will be evaluated and you get query identical to Select * from customers
If your variable is not null then condition #custid IS NULL is false and your query is identical to Select * from customers where custid=#custid
I use a SqlBuilder class and provide filters that attach the WHERE clauses programmatically.
So the SqlBuilder class would look like this:
public class Program
{
public static void Main()
{
Console.WriteLine(SqlBuilder.GetSql(null));
Console.WriteLine(SqlBuilder.GetSql("CUSTOMER_ID"));
}
public class SqlBuilder{
private static string _baseSql = "select * from customers";
public static string GetSql(string customerId){
var final = _baseSql;
if(customerId != null)
final += " WHERE custId=#custId";
return final;
}
}
}
This can also be made generic through the use of parameters and enums.
How do I automatically create an enum and subsequently use its values in C# based on values in a database lookup table (using enterprise library data layer)?
For example, If I add a new lookup value in the database, I don't want to have to manually add the extra static enum value declaration in code - I'd like to keep the enum in sync with the database.
Is there such a thing as this?
I don't want to create a code generated static enum (as per The Code Project article Enum Code Generator - Generating enum code automatically from database look up tables) and would prefer it to be completely automatic.
I'm doing this exact thing, but you need to do some kind of code generation for this to work.
In my solution, I added a project "EnumeratedTypes". This is a console application which gets all of the values from the database and constructs the enums from them. Then it saves all of the enums to an assembly.
The enum generation code is like this:
// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName("MyEnums");
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndSave);
// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name,
name.Name + ".dll");
// Define a public enumeration with the name "MyEnum" and an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum("EnumeratedTypes.MyEnum",
TypeAttributes.Public, typeof(int));
// Get data from database
MyDataAdapter someAdapter = new MyDataAdapter();
MyDataSet.MyDataTable myData = myDataAdapter.GetMyData();
foreach (MyDataSet.MyDataRow row in myData.Rows)
{
myEnum.DefineLiteral(row.Name, row.Key);
}
// Create the enum
myEnum.CreateType();
// Finally, save the assembly
assemblyBuilder.Save(name.Name + ".dll");
My other projects in the solution reference this generated assembly. As a result, I can then use the dynamic enums in code, complete with intellisense.
Then, I added a post-build event so that after this "EnumeratedTypes" project is built, it runs itself and generates the "MyEnums.dll" file.
By the way, it helps to change the build order of your project so that "EnumeratedTypes" is built first. Otherwise, once you start using your dynamically generated .dll, you won't be able to do a build if the .dll ever gets deleted. (Chicken and egg kind of problem -- your other projects in the solution need this .dll to build properly, and you can't create the .dll until you build your solution...)
I got most of the above code from this msdn article.
Enums must be specified at compile time, you can't dynamically add enums during run-time - and why would you, there would be no use/reference to them in the code?
From Professional C# 2008:
The real power of enums in C# is that behind the scenes they are instantiated as structs derived from the base class, System.Enum . This means it is possible to call methods against them to perform some useful tasks. Note that because of the way the .NET Framework is implemented there is no performance loss associated with treating the enums syntactically as structs. In practice, once your code is compiled, enums will exist as primitive types, just like int and float .
So, I'm not sure you can use Enums the way you want to.
Does it have to be an actual enum? How about using a Dictionary<string,int> instead?
for example
Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
I've done this with a T4 template. It is fairly trivial to drop a .tt file into your project, and set up Visual Studio to run the T4 template as a pre-build step.
The T4 generates a .cs file, which means you can have it just query the database and build an enum in a .cs file from the result. Wired up as a pre-build task, it would re-create your enum on every build, or you can run the T4 manually as needed instead.
Let's say you have the following in your DB:
table enums
-----------------
| id | name |
-----------------
| 0 | MyEnum |
| 1 | YourEnum |
-----------------
table enum_values
----------------------------------
| id | enums_id | value | key |
----------------------------------
| 0 | 0 | 0 | Apple |
| 1 | 0 | 1 | Banana |
| 2 | 0 | 2 | Pear |
| 3 | 0 | 3 | Cherry |
| 4 | 1 | 0 | Red |
| 5 | 1 | 1 | Green |
| 6 | 1 | 2 | Yellow |
----------------------------------
Construct a select to get the values you need:
select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0
Construct the source code for the enum and you'll get something like:
String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";
(obviously this is constructed in a loop of some kind.)
Then comes the fun part, Compiling your enum and using it:
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;
CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);
Type enumType = result.CompiledAssembly.GetType(enumName);
Now you have the type compiled and ready for use.
To get a enum value stored in the DB you can use:
[Enum].Parse(enumType, value);
where value can be either the integer value (0, 1, etc.) or the enum text/key (Apple, Banana, etc.)
Just showing the answer of Pandincus with "of the shelf" code and some explanation:
You need two solutions for this example ( I know it could be done via one also ; ), let the advanced students present it ...
So here is the DDL SQL for the table :
USE [ocms_dev]
GO
CREATE TABLE [dbo].[Role](
[RoleId] [int] IDENTITY(1,1) NOT NULL,
[RoleName] [varchar](50) NULL
) ON [PRIMARY]
So here is the console program producing the dll:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;
namespace DynamicEnums
{
class EnumCreator
{
// after running for first time rename this method to Main1
static void Main ()
{
string strAssemblyName = "MyEnums";
bool flagFileExists = System.IO.File.Exists (
AppDomain.CurrentDomain.SetupInformation.ApplicationBase +
strAssemblyName + ".dll"
);
// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName ( strAssemblyName );
AssemblyBuilder assemblyBuilder =
currentDomain.DefineDynamicAssembly ( name,
AssemblyBuilderAccess.RunAndSave );
// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as
// the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule (
name.Name, name.Name + ".dll" );
// Define a public enumeration with the name "MyEnum" and
// an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum (
"EnumeratedTypes.MyEnum",
TypeAttributes.Public,
typeof ( int )
);
#region GetTheDataFromTheDatabase
DataTable tableData = new DataTable ( "enumSourceDataTable" );
string connectionString = "Integrated Security=SSPI;Persist " +
"Security Info=False;Initial Catalog=ocms_dev;Data " +
"Source=ysg";
using (SqlConnection connection =
new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT [RoleId], " +
"[RoleName] FROM [ocms_dev].[dbo].[Role]" );
Console.WriteLine ( "command.CommandText is " +
command.CommandText );
connection.Open ();
tableData.Load ( command.ExecuteReader (
CommandBehavior.CloseConnection
) );
} //eof using
foreach (DataRow dr in tableData.Rows)
{
myEnum.DefineLiteral ( dr[1].ToString (),
Convert.ToInt32 ( dr[0].ToString () ) );
}
#endregion GetTheDataFromTheDatabase
// Create the enum
myEnum.CreateType ();
// Finally, save the assembly
assemblyBuilder.Save ( name.Name + ".dll" );
} //eof Main
} //eof Program
} //eof namespace
Here is the Console programming printing the output ( remember that it has to reference the dll ). Let the advance students present the solution for combining everything in one solution with dynamic loading and checking if there is already build dll.
// add the reference to the newly generated dll
use MyEnums ;
class Program
{
static void Main ()
{
Array values = Enum.GetValues ( typeof ( EnumeratedTypes.MyEnum ) );
foreach (EnumeratedTypes.MyEnum val in values)
{
Console.WriteLine ( String.Format ( "{0}: {1}",
Enum.GetName ( typeof ( EnumeratedTypes.MyEnum ), val ),
val ) );
}
Console.WriteLine ( "Hit enter to exit " );
Console.ReadLine ();
} //eof Main
} //eof Program
Aren't we coming to this from the wrong direction?
If the data is likely to change at all during the lifetime of the deployed release then an enum is just not appropriate, and you need to use a dictionary, hash or other dynamic collection.
If you know the set of possible values is fixed for the life of the deployed release, then an enum is preferable.
If you must have something in your database that replicates the enumerated set, then why not add a deployment step to clear and repopulate the database table with the definitive set of enum values?
I always like to write my own "custom enum". Than I have one class that is a little bit more complex, but I can reuse it:
public abstract class CustomEnum
{
private readonly string _name;
private readonly object _id;
protected CustomEnum( string name, object id )
{
_name = name;
_id = id;
}
public string Name
{
get { return _name; }
}
public object Id
{
get { return _id; }
}
public override string ToString()
{
return _name;
}
}
public abstract class CustomEnum<TEnumType, TIdType> : CustomEnum
where TEnumType : CustomEnum<TEnumType, TIdType>
{
protected CustomEnum( string name, TIdType id )
: base( name, id )
{ }
public new TIdType Id
{
get { return (TIdType)base.Id; }
}
public static TEnumType FromName( string name )
{
try
{
return FromDelegate( entry => entry.Name.Equals( name ) );
}
catch (ArgumentException ae)
{
throw new ArgumentException( "Illegal name for custom enum '" + typeof( TEnumType ).Name + "'", ae );
}
}
public static TEnumType FromId( TIdType id )
{
try
{
return FromDelegate( entry => entry.Id.Equals( id ) );
}
catch (ArgumentException ae)
{
throw new ArgumentException( "Illegal id for custom enum '" + typeof( TEnumType ).Name + "'", ae );
}
}
public static IEnumerable<TEnumType> GetAll()
{
var elements = new Collection<TEnumType>();
var infoArray = typeof( TEnumType ).GetFields( BindingFlags.Public | BindingFlags.Static );
foreach (var info in infoArray)
{
var type = info.GetValue( null ) as TEnumType;
elements.Add( type );
}
return elements;
}
protected static TEnumType FromDelegate( Predicate<TEnumType> predicate )
{
if(predicate == null)
throw new ArgumentNullException( "predicate" );
foreach (var entry in GetAll())
{
if (predicate( entry ))
return entry;
}
throw new ArgumentException( "Element not found while using predicate" );
}
}
Now I just need to create my enum I want to use:
public sealed class SampleEnum : CustomEnum<SampleEnum, int>
{
public static readonly SampleEnum Element1 = new SampleEnum( "Element1", 1, "foo" );
public static readonly SampleEnum Element2 = new SampleEnum( "Element2", 2, "bar" );
private SampleEnum( string name, int id, string additionalText )
: base( name, id )
{
AdditionalText = additionalText;
}
public string AdditionalText { get; private set; }
}
At last I can use it like I want:
static void Main( string[] args )
{
foreach (var element in SampleEnum.GetAll())
{
Console.WriteLine( "{0}: {1}", element, element.AdditionalText );
Console.WriteLine( "Is 'Element2': {0}", element == SampleEnum.Element2 );
Console.WriteLine();
}
Console.ReadKey();
}
And my output would be:
Element1: foo
Is 'Element2': False
Element2: bar
Is 'Element2': True
You want System.Web.Compilation.BuildProvider
I also doubt the wisdom of doing this, but then there maybe a good use case that I can't think of.
What you're looking for are Build Providers i.e. System.Web.Compilation.BuildProvider
They're used very effectively by SubSonic, you can download the source and see how they use them, you won't need anything half as intricate as what they're doing.
Hope this helps.
Using dynamic enums is bad no matter which way. You will have to go through the trouble of "duplicating" the data to ensure clear and easy code easy to maintain in the future.
If you start introducing automatic generated libraries, you are for sure causing more confusion to future developers having to upgrade your code than simply making your enum coded within the appropriate class object.
The other examples given sound nice and exciting, but think about the overhead on code maintenance versus what you get from it. Also, are those values going to change that frequently?
Word up, I as well got tired of writing out enumerations based on Id / Name db table columns, copying and pasting stuff from queries in SSMS.
Below is a super dirty stored procedure that takes as input a table name, the column name you want to use for the c# enumeration name, and the column name that you want to use for the c# enumeration value.
Most of theses table names I work with a) end with "s" b) have a [TABLENAME]Id column and c) have a [TABLENAME]Name column, so there are a couple if statements that will assume that structure, in which case, the column name parameters are not required.
A little context for these examples - "Stonk" here doesn't really mean "stock" but kinda, the way I'm using "stonk" it means "a thing that has some numbers associated to it for a time period" But that's not important, it's just an example of table with this Id / Name schema. It looks like this:
CREATE TABLE StonkTypes (
StonkTypeId TINYINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
StonkTypeName VARCHAR(200) NOT NULL CONSTRAINT UQ_StonkTypes_StonkTypeName UNIQUE (StonkTypeName)
)
After I create the proc, this statement:
EXEC CreateCSharpEnum 'StonkTypes'
Selects this string:
public enum StonkTypes { Stonk = 1, Bond = 2, Index = 3, Fund = 4, Commodity = 5,
PutCallRatio = 6, }
Which I can copy and paste into a C# file.
I have a Stonks table and it has StonkId and StonkName columns so this exec:
EXEC CreateCSharpEnum 'Stonks'
Spits out:
public enum Stonks { SP500 = 1, DowJonesIndustrialAverage = 2, ..... }
But for that enum I want to use the "Symbol" column for the enum name values so this:
EXEC CreateCSharpEnum 'Stonks', 'Symbol'
Does the trick and renders:
public enum Stonks { SPY = 1, DIA = 2, ..... }
Without further ado, here is this dirty piece of craziness. Yeah, very dirty, but I'm kind of pleased with myself - it's SQL code that constructs SQL code that constructs C# code. Couple layers involved.
CREATE OR ALTER PROCEDURE CreateCSharpEnum
#TableName VARCHAR(MAX),
#EnumNameColumnName VARCHAR(MAX) = NULL,
#EnumValueColumnName VARCHAR(MAX) = NULL
AS
DECLARE #LastCharOfTableName VARCHAR(1)
SELECT #LastCharOfTableName = RIGHT(#TableName, 1)
PRINT 'Last char = [' + #LastCharOfTableName + ']'
DECLARE #TableNameWithoutS VARCHAR(MAX)
IF UPPER(#LastCharOfTableName) = 'S'
SET #TableNameWithoutS = LEFT(#TableName, LEN(#TableName) - 1)
ELSE
SET #TableNameWithoutS = #TableName
PRINT 'Table name without trailing s = [' + #TableNameWithoutS + ']'
IF #EnumNameColumnName IS NULL
BEGIN
SET #EnumNameColumnName = #TableNameWithoutS + 'Name'
END
PRINT 'name col name = [' + #EnumNameColumnName + ']'
IF #EnumValueColumnName IS NULL
SET #EnumValueColumnName = #TableNameWithoutS + 'Id'
PRINT 'value col name = [' + #EnumValueColumnName + ']'
-- replace spaces and punctuation
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', '' '', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''&'', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''.'', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''('', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', '')'', '''')'
PRINT 'name col name with replace sql = [' + #EnumNameColumnName + ']'
DECLARE #SqlStr VARCHAR(MAX) = 'SELECT ' + #EnumNameColumnName
+ ' + '' = '''
+ ' + LTRIM(RTRIM(STR(' + #EnumValueColumnName + '))) + '','' FROM ' + #TableName + ' ORDER BY ' + #EnumValueColumnName
PRINT 'sql that gets rows for enum body = [' + #SqlStr + ']'
CREATE TABLE #EnumRowsTemp (s VARCHAR(MAX))
INSERT
INTO #EnumRowsTemp
EXEC(#SqlStr)
--SELECT * FROM #EnumRowsTemp
DECLARE #csharpenumbody VARCHAR(MAX)
SELECT #csharpenumbody = COALESCE(#csharpenumbody + ' ', '') + s FROM #EnumRowsTemp
--PRINT #csharpenumbody
DECLARE #csharpenum VARCHAR(MAX) = 'public enum ' + #TableName + ' { ' + #csharpenumbody + ' }'
PRINT #csharpenum
SELECT #csharpenum
DROP TABLE #EnumRowsTemp
Please, be critical. One funky thing I didn't understand, how come I have to create and drop this #EnumRowsTemp table and not just "SELECT INTO #EnumRowsTemp" to create the temp table on the fly? I don't know the answer, I tried that and it didn't work. That's probably the least of the problems of this code...
As dirty as it may be... I hope this saves some of you fellow dorks a little bit of time.
I don't think there is a good way of doing what you want. And if you think about it I don't think this is what you really want.
If you would have a dynamic enum, it also means you have to feed it with a dynamic value when you reference it. Maybe with a lot of magic you could achieve some sort of IntelliSense that would take care of this and generate an enum for you in a DLL file. But consider the amount of work it would take, how uneffective it would be to access the database to fetch IntelliSense information as well as the nightmare of version controlling the generated DLL file.
If you really don't want to manually add the enum values (you'll have to add them to the database anyway) use a code generation tool instead, for example T4 templates. Right click+run and you got your enum statically defined in code and you get all the benefits of using enums.
One way to keep the Enums and to create a Dynamic list of values at the same time is to use the Enums that you currently have with a Dynamically created Dictionary.
Since most Enums are used in the context that they are defined to be used, and the "dynamic enums" will be supported by dynamic processes, you can distinguish the 2.
The first step is to create a table/collection that houses the IDs and References for the Dynamic Entries. In the table you will autoincrement much larger than your largest Enum value.
Now comes the part for your dynamic Enums, I am assuming that you will be using the Enums to create a set of conditions that apply a set of rules, some are dynamically generated.
Get integer from database
If Integer is in Enum -> create Enum -> then run Enum parts
If Integer is not a Enum -> create Dictionary from Table -> then run Dictionary parts.
enum builder class
public class XEnum
{
private EnumBuilder enumBuilder;
private int index;
private AssemblyBuilder _ab;
private AssemblyName _name;
public XEnum(string enumname)
{
AppDomain currentDomain = AppDomain.CurrentDomain;
_name = new AssemblyName("MyAssembly");
_ab = currentDomain.DefineDynamicAssembly(
_name, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = _ab.DefineDynamicModule("MyModule");
enumBuilder = mb.DefineEnum(enumname, TypeAttributes.Public, typeof(int));
}
/// <summary>
/// adding one string to enum
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public FieldBuilder add(string s)
{
FieldBuilder f = enumBuilder.DefineLiteral(s, index);
index++;
return f;
}
/// <summary>
/// adding array to enum
/// </summary>
/// <param name="s"></param>
public void addRange(string[] s)
{
for (int i = 0; i < s.Length; i++)
{
enumBuilder.DefineLiteral(s[i], i);
}
}
/// <summary>
/// getting index 0
/// </summary>
/// <returns></returns>
public object getEnum()
{
Type finished = enumBuilder.CreateType();
_ab.Save(_name.Name + ".dll");
Object o1 = Enum.Parse(finished, "0");
return o1;
}
/// <summary>
/// getting with index
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public object getEnum(int i)
{
Type finished = enumBuilder.CreateType();
_ab.Save(_name.Name + ".dll");
Object o1 = Enum.Parse(finished, i.ToString());
return o1;
}
}
create an object
string[] types = { "String", "Boolean", "Int32", "Enum", "Point", "Thickness", "long", "float" };
XEnum xe = new XEnum("Enum");
xe.addRange(types);
return xe.getEnum();
You could use CodeSmith to generate something like this:
http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx
I use INFORMATION_SCHEMA.PARAMETERS for getting information about a stored procedure parameter now. I need to know the default values of parameters. Is there a solution for getting the default value of the parameters of a given stored procedure?
Parse the SQL code if you are doing it via SQL commands...
The information isn't stored in a system table. From sys.parameters (where you'd expect it), has_default_value and default_value columns, we're told to parse SQL:
SQL Server only maintains default values for CLR objects in this catalog view; therefore, this column has a value of 0 for Transact-SQL objects. To view the default value of a parameter in a Transact-SQL object, query the definition column of the sys.sql_modules catalog view, or use the OBJECT_DEFINITION system function.
If has_default_value is 1, the value of this column is the value of the default for the parameter; otherwise, NULL.
To prove:
CREATE PROC dbo.Paramtest (#foo int = 42)
AS
SET NOCOUNT ON;
GO
SELECT OBJECT_NAME(object_id), has_default_value, default_value
FROM sys.parameters
WHERE name = '#foo' AND object_id = OBJECT_ID('dbo.Paramtest')
-- gives Paramtest, 0, NULL
Declare #pSProcName NVARCHAR(MAX)='ProcedureName'
DECLARE #SQLTEXT NVARCHAR(MAX),#start int ,#end int,#SearchCode NVARCHAR(MAX)
SELECT #SQLTEXT =OBJECT_DEFINITION(OBJECT_ID(#pSProcName))
SELECT #start=CHARINDEX('#',#SQLTEXT,1)
SELECT #end =min(val) FROM (
SELECT PATINDEX('%'+CHAR(10)+'AS'+CHAR(10)+'%',#SQLTEXT ) AS val
UNION ALL SELECT PATINDEX('%'+CHAR(10)+'AS'+CHAR(13)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(13)+'AS'+CHAR(10)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(13)+'AS'+CHAR(13)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(10)+'AS'+CHAR(32)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(32)+'AS'+CHAR(10)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(32)+'AS'+CHAR(32)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(13)+'AS'+CHAR(32)+'%',#SQLTEXT )
UNION ALL SELECT PATINDEX('%'+CHAR(32)+'AS'+CHAR(13)+'%',#SQLTEXT )
) S
Where s.val <> 0
SELECT #SearchCode=SUBSTRING(#sqltext,#start,#end - #start)
SELECT S2.parameter_id,S2.ParameterName,S2.DataType,S1.Default_Value FROM
(
SELECT CASE WHEN Data like '%=%' then RIGHT(Data,len(Data)-CHARINDEX('=',Data,1)) ELSE '' END as Default_Value,
CASE WHEN Data like '%=%' then 1 ELSE 0 END as Has_default_Value
,Data FROM(
SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) AS Data
FROM
(
SELECT CAST ('<M>' + REPLACE(#SearchCode, ',', '</M><M>') + '</M>' AS XML) AS Data
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a))s
)S1
INNER JOIN
(
Select p.parameter_id,p.name as ParameterName,UPPER(t.name) AS DataType from sys.all_parameters p
Inner JOIN sys.types t
on t.user_type_id = p.user_type_id
where Object_name(OBJECT_ID) = #pSProcName
) S2
ON S1.Data LIKE '%'+S2.ParameterName+'%'+S2.DataType+'%'
#N.Dinesh.Reddy has an amazing answer. I re-worked it a little bit to provide support for new-lines between parameter definitions, as well as supporting parameters whose default values containing XML characters (e.g. #Parameter1 NVARCHAR(MAX) = N'<').
DECLARE #ProcedureName NVARCHAR(MAX) = N'dbo.TestProcedure';
DECLARE #ObjectId INT = OBJECT_ID(#ProcedureName);
DECLARE #DefinitionText NVARCHAR(MAX) = REPLACE(REPLACE(OBJECT_DEFINITION(#ObjectId), CHAR(10), N' '), CHAR(13), N' ');
DECLARE #FirstParameterIndex INT = CHARINDEX('#',#DefinitionText, 1);
-- Pull out only the parameters, and xml-encode them.
SET #DefinitionText = (SELECT SUBSTRING(#DefinitionText, #FirstParameterIndex, PATINDEX(N'% AS %', #DefinitionText) - #FirstParameterIndex) FOR XML PATH(N''));
-- Find the parameter names.
SELECT b.parameter_id, b.name, b.system_type_id, b.user_type_id, b.max_length, b.is_output, a.has_default_value FROM (
SELECT LEFT(ParameterDefinition, CHARINDEX(N' ', ParameterDefinition, 1)) parameter_name, CAST(CASE WHEN EqualSignIndex = 0 THEN 0 ELSE 1 END AS BIT) has_default_value FROM (
SELECT ParameterDefinition, CHARINDEX(N'=', ParameterDefinition, 1) EqualSignIndex FROM (
SELECT LTRIM(RTRIM(Split.ParameterDefinition.value(N'.', N'NVARCHAR(100)'))) ParameterDefinition FROM (
SELECT CAST(CONCAT('<a>', REPLACE(#DefinitionText, ',', '</a><a>'), '</a>') AS XML) Xml
) a CROSS APPLY Xml.nodes('/a') AS Split(ParameterDefinition)
) a
) a
) a
FULL JOIN sys.all_parameters b ON a.parameter_name = b.name
WHERE b.object_id = #ObjectId;
I poked around the dissassembly of SQL Server Management Studio to find out how Microsoft themselves do it, because I wanted to ensure my approach was fully-correct.
As the other posters have surmised, and to my surprise (read: appalled shock) SSMS runs a RegEx against sys.sql_modules.definition to extract parameter information that isn't available in sys.parameters.
As of SQL Server Management Studio 18, the logic for this is in Microsoft.SqlServer.SqlEnum.dll, specifically, the class Microsoft.SqlServer.Management.Smo.PostProcessParam. You'll find other useful regexes in there too.
Here's the patterns:
If your procedure was saved with SET QUOTED_IDENTIFIER then this pattern is used:
new Regex( "(/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)|(--[^\n]*)|(\"((\"\")|[^\"])*\")|(//[^\n]*)|(?<delim>\\b((AS)|(RETURNS))\\b)|(?:(?<param>#[\\w_][\\w\\d_$$##]*)((\\s)|((--[^\n]*))|((/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)))*(AS){0,1})|(?<val>(((\"((\"\")|[^\"])*\"))|((N{0,1}'(('')|[^'])*)')|((0x[0-9a-f]+))|(((\\+|\\-){0,1}((\\d+\\.\\d*)|(\\d*\\.\\d+)|(\\d+))(e((\\+)|(\\-))\\d+){0,1}))|((\\[((\\]\\])|[^\\]])*\\]))|(([\\w_][\\w;\\d_]*))))|(?<comma>,)|(?<eq>=)|(\\([\\d, ]*\\))", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
If your procedure was not saved with SET QUOTED_IDENTIFIER then this pattern is used:
new Regex( "(/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)|(--[^\n]*)|(//[^\n]*)|(?<delim>\\b((AS)|(RETURNS))\\b)|(?:(?<param>#[\\w_][\\w\\d_$$##]*)((\\s)|((--[^\n]*))|((/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)))*(AS){0,1})|(?<val>(((\"((\"\")|[^\"])*\"))|((N{0,1}'(('')|[^'])*)')|((0x[0-9a-f]+))|(((\\+|\\-){0,1}((\\d+\\.\\d*)|(\\d*\\.\\d+)|(\\d+))(e((\\+)|(\\-))\\d+){0,1}))|((\\[((\\]\\])|[^\\]])*\\]))|(([\\w_][\\w;\\d_]*))))|(?<comma>,)|(?<eq>=)|(\\([\\d, ]*\\))", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
To get the default values of each parameter, run the appropriate regex against your sql.sql_modules_definition and watch for the val group.
TL;DR:
Here's the full instructions needed to get this to work:
Requirements:
On-Prem SQL Server (tested with SQL Server 2014, 2016 and 207). This does not work with Azure SQL because Azure SQL does not support SQL-CLR.
You do not need Visual Studio - just a command-line access to csc.exe so you can compile the SQL-CLR assembly yourself.
Copy and paste the GetParameterDefaults.cs file described below.
Run this command in a terminal/command-prompt to compile GetParameterDefaults.cs to a SQL-CLR assembly ProcParamDefs.dll:
csc.exe /noconfig /nowarn:1701,1702,2008 /fullpaths /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /errorendlocation /preferreduilang:en-US /highentropyva+ /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" /debug+ /debug:full /optimize- /out:ProcParamDefs.dll /subsystemversion:6.00 /target:library /warnaserror- /utf8output /langversion:7.3 GetParameterDefaults.cs
Locate the ProcParamDefs.dll you created in Step 2 and convert it to a hex-bin string (Base 16).
There are websites that will do it for free, or use this PowerShell command:
( Get-Content ".\ProcParamDefs.dll" | Format-Hex | Select-Object -Expand Bytes | ForEach-Object { '{0:x2}' -f $_ }) -join ''
Copy and paste the Install.sql file (at the end of this post) into an SSMS session.
Replace <PASTE BASE-16 (HEX) DLL HERE KEEP THE LEADING 0x PREFIX> with the Base16/hex from step 3. Note the leading 0x needs to be present, as does the trailing ;, so it should look something like this:
CREATE ASSEMBLY [ProcParamDefs]
AUTHORIZATION [dbo]
FROM 0x0x4D5A90000300000004000000FFFF0000B800etc
;
Run it and you should be all-set to use the UDF dbo.GetParameterDefaults, dbo.[GetParameterDefaultsByProcedureObjectId, and dbo.ParseParameterDefaultValues.
For example, see this screenshot:
Note that it only lists parameters with defaults. It will not list parameters without any default defined.
GetParameterDefaults.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
/// <summary>
/// This SQL-CLR table-valued UDF will parse and expose the default-values for SQL Server stored procedures as this information is not normally available through INFORMATION_SCHEMA nor sys.parameters.<br />
/// By Dai Rees on StackOverflow: https://stackoverflow.com/questions/6992561/is-there-a-solution-for-getting-the-default-value-of-the-parameters-of-a-given-s<br />
/// This also may find its way onto my GitHub eventually too.<br />
/// The crucial regular-expressions inside this UDF were copied from Microsoft's SqlEnum.dll (they're *the exact same* regexes that SSMS uses to show parameter information) btw.<br />
/// I guess the regexes are Microsoft's copyright, but SSMS is given-away for free and Microsoft made no attempt to obscure/hide them, so I guess this is fair-use? If so, then consider my code as MIT licensed, so happy forking!
/// </summary>
public partial class UserDefinedFunctions
{
private static readonly Regex _paramRegexQI = new Regex("(/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)|(--[^\n]*)|(\"((\"\")|[^\"])*\")|(//[^\n]*)|(?<delim>\\b((AS)|(RETURNS))\\b)|(?:(?<param>#[\\w_][\\w\\d_$$##]*)((\\s)|((--[^\n]*))|((/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)))*(AS){0,1})|(?<val>(((\"((\"\")|[^\"])*\"))|((N{0,1}'(('')|[^'])*)')|((0x[0-9a-f]+))|(((\\+|\\-){0,1}((\\d+\\.\\d*)|(\\d*\\.\\d+)|(\\d+))(e((\\+)|(\\-))\\d+){0,1}))|((\\[((\\]\\])|[^\\]])*\\]))|(([\\w_][\\w;\\d_]*))))|(?<comma>,)|(?<eq>=)|(\\([\\d, ]*\\))", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled );
private static readonly Regex _paramRegex = new Regex("(/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)|(--[^\n]*)|(//[^\n]*)|(?<delim>\\b((AS)|(RETURNS))\\b)|(?:(?<param>#[\\w_][\\w\\d_$$##]*)((\\s)|((--[^\n]*))|((/\\*(([^/\\*])|(\\*(?=[^/]))|(/(?=[^\\*])))*|(/\\*(?>/\\*(?<DEPTH>)|\\*/(?<-DEPTH>)|(.|[\n])?)*(?(DEPTH)(?!))\\*/)\\*/)))*(AS){0,1})|(?<val>(((\"((\"\")|[^\"])*\"))|((N{0,1}'(('')|[^'])*)')|((0x[0-9a-f]+))|(((\\+|\\-){0,1}((\\d+\\.\\d*)|(\\d*\\.\\d+)|(\\d+))(e((\\+)|(\\-))\\d+){0,1}))|((\\[((\\]\\])|[^\\]])*\\]))|(([\\w_][\\w;\\d_]*))))|(?<comma>,)|(?<eq>=)|(\\([\\d, ]*\\))", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled );
private const String _tableDefinition = #"ParameterName nvarchar(100), DefaultValueExpr nvarchar(4000)";
[SqlFunction(
DataAccess = DataAccessKind.Read,
FillRowMethodName = nameof(FillRow),
IsDeterministic = false,
Name = nameof(GetParameterDefaults),
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = _tableDefinition
)]
public static IEnumerable/*<(String parameterName, String defaultValueExpr)>*/ GetParameterDefaults( String procedureName )
{
// Despite the fact the function returns an IEnumerable and the SQLCLR docs saying how great streaming is, it's actually very difficult to use `yield return`:
// See here: https://stackoverflow.com/questions/591191/sqlfunction-fails-to-open-context-connection-despite-dataaccesskind-read-present
// SQLCLR will handle ArgumentExceptions just fine:
// https://learn.microsoft.com/en-us/archive/blogs/sqlprogrammability/server-side-error-handling-part-2-errors-and-error-messages
// https://learn.microsoft.com/en-us/archive/blogs/sqlprogrammability/exception-handling-in-sqlclr
if( procedureName is null ) throw new ArgumentNullException( paramName: nameof(procedureName) );
if( String.IsNullOrWhiteSpace( procedureName ) ) throw new ArgumentException( message: "Value cannot be empty nor whitespace.", paramName: nameof(procedureName) );
//
( Boolean ok, String definition, Boolean isQuotedId ) = TryGetProcedureDefinitionFromName( procedureName );
if( !ok )
{
throw new ArgumentException( message: "Could not find the definition of a procedure with the name \"" + procedureName + "\".", paramName: nameof(procedureName) );
}
// We can't do this, boo:
// foreach( var t in ParseParams( definition, quotedId ? _paramRegexQI : _paramRegex ) ) yield return t;
return ParseParameterDefaultValues( definition, isQuotedId );
}
[SqlFunction(
DataAccess = DataAccessKind.Read,
FillRowMethodName = nameof(FillRow),
IsDeterministic = false,
Name = nameof(GetParameterDefaultsByProcedureObjectId),
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = _tableDefinition
)]
public static IEnumerable/*<(String parameterName, String defaultValueExpr)>*/ GetParameterDefaultsByProcedureObjectId( Int32 procedureObjectId )
{
( Boolean ok, String definition, Boolean isQuotedId ) = TryGetProcedureDefinitionFromId( procedureObjectId );
if( !ok )
{
throw new ArgumentException( message: "Could not find the definition of a procedure with OBJECT_ID = " + procedureObjectId.ToString(), paramName: nameof(procedureObjectId) );
}
// We can't do this, boo:
// foreach( var t in ParseParams( definition, quotedId ? _paramRegexQI : _paramRegex ) ) yield return t;
return ParseParameterDefaultValues( definition, isQuotedId );
}
[SqlFunction(
DataAccess = DataAccessKind.Read,
FillRowMethodName = nameof(FillRow),
IsDeterministic = false,
Name = nameof(ParseParameterDefaultValues),
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = _tableDefinition
)]
public static IEnumerable/*<(String parameterName, String defaultValueExpr)>*/ ParseParameterDefaultValues( String procedureDefinition, Boolean isQuotedId )
{
List<(String parameterName, String defaultValueExpr)> list = new List<(String parameterName, String defaultValueExpr)>();
foreach( (String name, String value) t in ParseParams( procedureDefinition, isQuotedId ? _paramRegexQI : _paramRegex ) )
{
list.Add( t );
}
return list;
}
private static ( Boolean ok, String definition, Boolean quotedId ) TryGetProcedureDefinitionFromName( String procedureName )
{
using( SqlConnection c = new SqlConnection( "context connection=true" ) )
{
c.Open();
using( SqlCommand cmd = c.CreateCommand() )
{
cmd.CommandText = #"
SELECT
c.[definition],
CONVERT( bit, OBJECTPROPERTY( c.object_id, N'ExecIsQuotedIdentOn' ) ) AS IsQuotedId
FROM
sys.sql_modules AS c
WHERE
c.object_id = OBJECT_ID( #procedureName );";
_ = cmd.Parameters.Add( new SqlParameter( "#procedureName", SqlDbType.NVarChar ) { Value = procedureName } );
using( SqlDataReader rdr = cmd.ExecuteReader() )
{
if( rdr.Read() )
{
String definition = rdr.GetString(0);
Boolean quotedId = rdr.GetBoolean(1);
return ( ok: true, definition, quotedId );
}
else
{
// Validate the object-name:
return ( ok: false, definition: null, quotedId: default );
}
}
}
/*
using( SqlCommand cmdPostMortem = c.CreateCommand() )
{
cmdPostMortem.CommandText = #"
SELECT OBJECT_ID( #procedureName ) AS oid";
_ = cmdPostMortem.Parameters.Add( new SqlParameter( "#procedureName", SqlDbType.NVarChar ) { Value = procedureName } );
Object objectId = cmdPostMortem.ExecuteScalar();
if( objectId is null || objectId == DBNull.Value )
{
}
}
*/
}
}
private static ( Boolean ok, String definition, Boolean quotedId ) TryGetProcedureDefinitionFromId( Int32 procedureObjectId )
{
using( SqlConnection c = new SqlConnection( "context connection=true" ) )
{
c.Open();
using( SqlCommand cmd = c.CreateCommand() )
{
cmd.CommandText = #"
SELECT
c.[definition],
CONVERT( bit, OBJECTPROPERTY( c.object_id, N'ExecIsQuotedIdentOn' ) ) AS IsQuotedId
FROM
sys.sql_modules AS c
WHERE
c.object_id = #objId;";
_ = cmd.Parameters.Add( new SqlParameter( "#objId", SqlDbType.Int ) { Value = procedureObjectId } ); // `OBJECT_ID` returns `int` values btw.
using( SqlDataReader rdr = cmd.ExecuteReader() )
{
if( rdr.Read() )
{
String definition = rdr.GetString(0);
Boolean quotedId = rdr.GetBoolean(1);
return ( ok: true, definition, quotedId );
}
else
{
return ( ok: false, definition: null, quotedId: default );
}
}
}
}
}
private static IEnumerable<(String name, String value)> ParseParams( String definition, Regex r )
{
Boolean inParam = false;
String currentParameterName = null;
Match m = r.Match( definition );
while( m.Success && !m.Groups["delim"].Success )
{
if( m.Groups["eq"].Success )
{
inParam = true;
}
if( m.Groups["comma"].Success )
{
inParam = false;
currentParameterName = null;
}
if( inParam && currentParameterName != null && m.Groups["val"].Success )
{
String defaultValue = m.Groups["val"].Value;
inParam = false;
yield return ( currentParameterName, defaultValue );
}
if( m.Groups["param"].Success )
{
currentParameterName = m.Groups["param"].Value;
}
m = m.NextMatch();
}
}
public static void FillRow( Object tupleObj, out SqlString parameterName, out SqlString defaultValueExpr )
{
if( tupleObj is ValueTuple<String,String> vt )
{
parameterName = vt.Item1;
defaultValueExpr = vt.Item2;
}
else if( tupleObj is null )
{
throw new ArgumentNullException( paramName: nameof(tupleObj) );
}
else
{
throw new ArgumentException( message: "Expected first argument to be of type ValueTuple<String,String> but encountered " + tupleObj.GetType().FullName, paramName: nameof(tupleObj) );
}
}
}
Install.sql
/* You may need to run these statements too:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'clr strict security', 0;
RECONFIGURE;
EXEC sp_configure 'clr enabled', 1;
RECONFIGURE;
GO
*/
CREATE ASSEMBLY [ProcParamDefs] AUTHORIZATION [dbo]
FROM 0x<PASTE BASE-16 (HEX) DLL HERE KEEP THE LEADING 0x PREFIX>
WITH PERMISSION_SET = SAFE
GO
CREATE FUNCTION [dbo].[GetParameterDefaults] (#procedureName [nvarchar](MAX))
RETURNS TABLE (ParameterName nvarchar(100), DefaultValueExpr nvarchar(4000))
AS EXTERNAL NAME [ProcParamDefs].[UserDefinedFunctions].[GetParameterDefaults];
GO
CREATE FUNCTION [dbo].[GetParameterDefaultsByProcedureObjectId] (#procedureObjectId [int])
RETURNS TABLE (ParameterName nvarchar(100), DefaultValueExpr nvarchar(4000))
AS EXTERNAL NAME [ProcParamDefs].[UserDefinedFunctions].[GetParameterDefaultsByProcedureObjectId];
GO
CREATE FUNCTION [dbo].[ParseParameterDefaultValues] (#procedureDefinition [nvarchar](MAX), #isQuotedId [bit])
RETURNS TABLE (ParameterName nvarchar(100), DefaultValueExpr nvarchar(4000))
AS EXTERNAL NAME [ProcParamDefs].[UserDefinedFunctions].[ParseParameterDefaultValues];
GO
As said before, this is not supported via T-SQL.
You'll have to use some kind of program language to implement this (unless you want to deal with text parsing with T-SQL).
I found it very easy to use SMO for this.
For example, in Powershell:
param
(
[string]$InstanceName = $env:COMPUTERNAME,
[string]$DBName = "MyDB",
[string]$ProcedureSchema = "dbo",
[string]$ProcedureName = "MyProcedure"
)
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$serverInstance = New-Object ('Microsoft.SqlServer.Management.Smo.Server') $InstanceName
$serverInstance.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.StoredProcedure], $false)
$procedure = $serverInstance.Databases[$DBName].StoredProcedures[$ProcedureName, $ProcedureSchema];
$procedure.Parameters | Select-Object Parent, Name, DataType, DefaultValue, #{Name="Properties";Expression={$_.Properties | Select Name, Value }}
Or, using C#:
Server svr = new Server(new ServerConnection(new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString)));
svr.SetDefaultInitFields(typeof(StoredProcedure), false);
StoredProcedure sp = svr.Databases["MyDatabase"].StoredProcedures["mySproc", "myScheme"];
Dictionary<string, string> defaultValueLookup = new Dictionary<string, string>();
foreach (StoredProcedureParameter parameter in sp.Parameters)
{
string defaultValue = parameter.DefaultValue;
string parameterName = parameter.Name;
defaultValueLookup.Add(parameterName, defaultValue);
}
(source for the last one: https://stackoverflow.com/a/9977237/3114728)
I want to remove a particular character while retrieving a varchar from a table. The query looks like this:
SELECT ServiceName,
case isNumeric(Ramusage)
when 1 then
cast ( ltrim ( rtrim ( ramusage )) as int )
else
0
end as test,
priority
FROM ProcessInfo
Here the problem is ramusage is a varchar and it stores values like 12,500K, 4,321K.
Here I want to remove the "K" from the entry and display the number alone.
You want to use the REPLACE function:
REPLACE(ramusage, 'k', '')
Reference:
SQL Server
Oracle
MySQL
Use REPLACE
DECLARE #ProcessInfo TABLE(
ServiceName VARCHAR(MAX),
Ramusage VARCHAR(MAX),
priority INT
)
INSERT INTO #ProcessInfo (ServiceName,Ramusage,priority)
SELECT 'TEST','12,123K',1
SELECT ServiceName,
case isNumeric(REPLACE(REPLACE(Ramusage,'K',''),',',''))
when 1 then
cast ( ltrim ( rtrim ( REPLACE(REPLACE(Ramusage,'K',''),',',''))) as int )
else
0
end as test,
priority
FROM #ProcessInfo
what we have done is to create a CLR function to take a string amd remove all non numeric items
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString RemoveNonNumeric(SqlString strValue)
{
string strNew = strValue.ToString();
char[] chrArray = strNew.ToCharArray();
strNew = null;
for (int iChar = 0; iChar < chrArray.Length; iChar++)
{
if (Char.IsNumber(chrArray[iChar]))
strNew += chrArray[iChar];
}
// Put your code here
return new SqlString(strNew);
}
substr( ltrim( rtrim( ramusage ) ), length( ramusage ) - 1)
edit: replace, as mentioned in the other answers, is probably better.