I have quite a few SQLCLR functions and sprocs in my MSSQL 2008 DB. Debugging and logging is always a problem. I have used Microsoft Enterprise Library Logging Application Block a lot in straight C# apps, and was wondering if it was (1) crazy or (2) impossible to layer that into SQLCLR stuff. I really like using a config file to define a rolling text log, Event Log, and SMTP output for different events, so if there is another way to do so, I'm all ears...
Thanks.
It seems like it is possible. I don't know if it's advisable.
One alternative would be defining a trace listener in code, which of course can read the configuration from the database. Another would simple be logging messages to a SQL table and using triggers to enable notifications.
I also do have to question whether the real mistake is that you have CLR stored procs in your database that have so much business logic that they require logging. I'm a big fan of business logic in my database, but I'm wary of CLR stored procs.
If you wanted to use the enterprise application blocks, the config file to edit woulds be C:\Program Files\Microsoft SQL Server\MSSQL10.INSTANCE_NAME\MSSQL\Binn\sqlservr.config. However, sql server does not seem to write values to this file when you restart it. I arrived at this conclusion with the following CLF PROC and UDFs:
using System;
using System.Configuration;
using Microsoft.SqlServer.Server;
namespace LoggedClr
{
public static class AppDomainInfo
{
[SqlFunction]
public static string GetConfigFileName()
{
return AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
}
[SqlFunction]
public static string GetAppConfigValue(string key)
{
return ConfigurationManager.AppSettings[key];
}
[SqlProcedure]
public static void SetAppConfigValue(string key, string value)
{
ConfigurationManager.AppSettings[key] = value;
}
}
}
Which I loaded and ran using the following T-SQL:
CREATE DATABASE TestAssembly
GO
USE TestAssembly
GO
ALTER DATABASE TestAssembly SET TRUSTWORTHY ON;
GO
ALTER AUTHORIZATION ON DATABASE::TestAssembly TO test
GO
DROP ASSEMBLY LoggedClr
GO
CREATE ASSEMBLY LoggedClr
from 'C:\justin''s projects\TestClr\LoggedClr\LoggedClr\bin\Debug\LoggedClr.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
GO
CREATE FUNCTION GetConfigFileName () RETURNS NVARCHAR(MAX) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].GetConfigFileName
GO
CREATE FUNCTION GetAppConfigValue (#key nvarchar(max)) RETURNS nvarchar(max) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].GetAppConfigValue
GO
CREATE PROCEDURE SetAppConfigValue (#key nvarchar(max), #value nvarchar(max)) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].SetAppConfigValue
GO
SELECT dbo.GetConfigFileName()
EXEC dbo.SetAppConfigValue 'justin', 'is a developer'
SELECT dbo.GetAppConfigValue('justin')
This gave me the following results:
------------------------------------------------------------------------------
C:\Program Files\Microsoft SQL Server\MSSQL10.INSTANCE_NAME\MSSQL\Binn\sqlservr.config
(1 row(s) affected)
-------------------------------------------------------------------------
is a developer
(1 row(s) affected)
Related
I'm running big dependency scan on legacy db and see that some objects have obsolete ref links, if you run this code in SSMS for View that points to not existing table like in my case, you will get your output on Results tab AND error info in Messages . Like in my case below.
I tried to check all env things I know and output of this stored procedure, but didn't see any indication.
How I can capture this event as I'm running this in looped dynamic SQL script and capture output in my table for further processing?
Updated:
it just text in Message box ,on error, you still have output on
Results tab
this is sp, it loop thru object list I took from sys.object and run this string as my sample to get all dependencies, load all into table. This call to
sql_reference_entities is the only way to get inter database
dependency on column level. So I need stick to this 100$>
--
Select *
From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')
--
----update------
This behavior was fixed in SQL Server 2014 SP3 and SQL Server 2016 SP2:
Starting from Microsoft SQL Server 2012, errors raised by
sys.dm_sql_referenced_entities (such as when an object has undergone a
schema change) cannot be caught in a TRY...CATCH Transact-SQL block.
While this behavior is expected in SQL Server 2012 and above, this
improvement introduces a new column that's called is_incomplete to the
Dynamic Management View (DMV).
KB4038418 - Update adds a new column to DMV sys.dm_sql_referenced_entities in SQL Server 2014 and 2016
----update-------
The tldr is that you can't capture these on the server side, and must use a client program in C#, PowerShell or some other client that can process info messages.
That DMV is doing something strange that I don't fully understand. It's generating errors (which a normal UDF is not allowed to do), and those errors do not trigger a TRY/CATCH block or set ##error. EG
create table tempdb.dbo.foo(id int)
go
create view dbo.v_View_Obs_Table
as
select * from tempdb.dbo.foo
go
drop table tempdb.dbo.foo
go
begin try
Select * From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')
end try
begin catch
select ERROR_MESSAGE(); --<-- not hit
end catch
However these are real errors, as you can see running this from client code:
using System;
using System.Data.SqlClient;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
using (var con = new SqlConnection("Server=.;database=AdventureWorks;integrated security=true"))
{
con.Open();
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += (s, a) =>
{
Console.WriteLine($"{a.Message}");
foreach (SqlError e in a.Errors)
{
Console.WriteLine($"{e.Message} Number:{e.Number} Class:{e.Class} State:{e.State} at {e.Procedure}:{e.LineNumber}");
}
};
var cmd = con.CreateCommand();
cmd.CommandText = "Select * From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')";
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read() || (rdr.NextResult() && rdr.Read()))
{
Console.WriteLine(rdr[0]);
}
}
Console.ReadKey();
}
}
}
}
outputs
Invalid object name 'tempdb.dbo.foo'.
Invalid object name 'tempdb.dbo.foo'. Number:208 Class:16 State:3 at v_View_Obs_Table:4
0
The dependencies reported for entity "dbo.v_View_Obs_Table" might not include references to all columns. This is either because the entity references an object that does not exist or because of an error in one or more statements in the entity. Before rerunning the query, ensure that there are no errors in the entity and that all objects referenced by the entity exist.
The dependencies reported for entity "dbo.v_View_Obs_Table" might not include references to all columns. This is either because the entity references an object that does not exist or because of an error in one or more statements in the entity. Before rerunning the query, ensure that there are no errors in the entity and that all objects referenced by the entity exist. Number:2020 Class:16 State:1 at :1
Need some help on the below issue:
Case 1 : stored procedure is on server 1 - call is from server1
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database1_server1].[dbo].[getcountrylist]
Select * from #tempCountry
Result: successful execution
Case2 : iIf this same stored procedure is being called from a different server using linked server like this :
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
Select * from #tempCountry
Result
Msg 7391, level 16, state 2, line 2
The operation could not be performed because OLEDB provider "SQLNCLI" for linkedserver "Server2_Database2" was unable to begin a distributed transaction.
Case 3
But when tried to execute the stored procedure separately [without temp table insertion] like below
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
Result: that is executing the stored procedure without any error and returning data.
I forgot to mention that am using SQL Server 2005. As per the server administrator, the feature you've suggested that I use is not available in 2005.
You have (I believe) two options here:
To try to avoid the usage of MSDTC (and all these not pleasant things related to Distributed Transactions) by using OPENQUERY rowset function
/assume (here and below) that [database2_server2] is the name of the linked server/
declare #tempCountry table (countryname char(50))
insert into #tempCountry
select * from openquery([database2_server2], '[database1_server1].[dbo].[getcountrylist]')
select * from #tempCountry
OR
You can set the linked server's option Enable Promotion Of Distributed Transaction to False in order to prevent the local transaction to promote the distributed transaction and therefore use of MSDTC:
EXEC master.dbo.sp_serveroption
#server = N'database2_server2',
#optname = N'remote proc transaction promotion',
#optvalue = N'false'
and your original query should work fine:
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
select * from #tempCountry
It is possible to avoid Linked Servers altogether. You can create a SQLCLR stored procedure that makes a standard connection to the remote instance (i.e. Database1).
The C# code below is for a SQLCLR Stored Procedure that:
allows for an optional database name. If empty the current database will be the default database, or if provided it will change to that database after connecting (so that the current database can be different than the default database)
allows for optionally using Impersonation. Without impersonation (the default behavior) the connections are made by the Windows Login that the SQL Server Service is running under (i.e. the "Log On As" account in "Services"). This might not be desired as it does typically provide an elevated level of permissions than the caller usually has. Using Impersonation will maintain the security context of the Login executing the stored procedure, if that Login is associated with a Windows Login. A SQL Server Login does not have a security context and will hence get an error if attempting to use Impersonation.
The ability to toggle Impersonation on and off in the code provided here is for testing purposes so it is easier to see the differences between using Impersonation and not using it. When using this code in a real project, there usually would not be a reason to allow the end-user (i.e. the caller) to change the setting. It is generally safer to use Impersonation. But, the main difficulty in using Impersonation is that it is restricted to the local machine, unless the Windows Login is enabled for Delegation in Active Directory.
should be created on the instance that will be calling Server1: Server2 in Database2
requires a PERMISSION_SET of EXTERNAL_ACCESS. This is best handled by:
signing the Assembly in Visual Studio
in [master], create an Asymmetric Key from the DLL
in [master], create a Login from this new Asymmetric Key
GRANT the EXTERNAL ACCESS ASSEMBLY permission to the new Key-based Login
in [Database2], execute the following:
ALTER ASSEMBLY [NoLinkedServer] WITH PERMISSION_SET = EXTERNAL_ACCESS;
should be executed as:
EXEC dbo.RemoteExec N'Server1', N'Database1', 0;
and:
EXEC dbo.RemoteExec N'Server1', N'Database1', 1;
After each execution, run the following and pay attention to those first two fields:
SELECT [login_name], [original_login_name], *
FROM sys.dm_exec_sessions
WHERE LEFT([program_name], 14) = N'Linked Server?';
The C# code:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public class LinkedServersSuck
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void RemoteExec(
[SqlFacet(MaxSize = 128)] SqlString RemoteInstance,
[SqlFacet(MaxSize = 128)] SqlString RemoteDatabase,
SqlBoolean UseImpersonation)
{
if (RemoteInstance.IsNull)
{
return;
}
SqlConnectionStringBuilder _ConnectionString =
new SqlConnectionStringBuilder();
_ConnectionString.DataSource = RemoteInstance.Value;
_ConnectionString.Enlist = false;
_ConnectionString.IntegratedSecurity = true;
_ConnectionString.ApplicationName =
"Linked Server? We don't need no stinkin' Linked Server!";
SqlConnection _Connection =
new SqlConnection(_ConnectionString.ConnectionString);
SqlCommand _Command = new SqlCommand();
_Command.CommandType = CommandType.StoredProcedure;
_Command.Connection = _Connection;
_Command.CommandText = #"[dbo].[getcountrylist]";
SqlDataReader _Reader = null;
WindowsImpersonationContext _SecurityContext = null;
try
{
if (UseImpersonation.IsTrue)
{
_SecurityContext = SqlContext.WindowsIdentity.Impersonate();
}
_Connection.Open();
if (_SecurityContext != null)
{
_SecurityContext.Undo();
}
if (!RemoteDatabase.IsNull && RemoteDatabase.Value != String.Empty)
{
// do this here rather than in the Connection String
// to reduce Connection Pool Fragmentation
_Connection.ChangeDatabase(RemoteDatabase.Value);
}
_Reader = _Command.ExecuteReader();
SqlContext.Pipe.Send(_Reader);
}
catch
{
throw;
}
finally
{
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
}
return;
}
}
I've searched the internet thoroughly but couldn't find a clear answer to the problem. I have got the aspnet.db database. But i want to add my own tables and data to this database. If i try to connect to it with the connection string:
<add name ="ToernooiCompanionDBContext" connectionString ="Data Source= .\SQLEXPRESS; Integrated Security = SSPI; Trusted_Connection=True; Initial Catalog= aspnetdb" providerName ="System.Data.SqlClient"/>
A new database will be created (aspnetdb.mdf) in C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA.
I want the database (which is automatically generated by codefirst) to merge with the existing one in my APP_DATA folder. What am I doing wrong?
I've tried adding AttachDbFilename=|DataDirectory|aspnetdb.mdf and User Instance=true to my connection string, or using the LocalSqlServer connection string which is defined in machine.config, but in all cases this overwrites the existing database. If I remove Initial Catalog=aspnetdb then I get an error that the initial catalog is needed.
I had the same problem but this link got me on the track to something that worked at least for me. I hope this helps someone at least! :)
Create a database
Add the aspnet tables to the new database
Fix the database connections in web.config so they point to the same database
Write some sql that removes all tables except the ones that start with "aspnet_"
Add the sql to the database initializer you write by your self
Add a call to the database initializer in Global.asax.cs
1. Create a database
I usually do this with SQL Server Management Studio. The database I used for this example code is SQL Server 2008R2 but I have done the same with SQL Server Express that you use.
2. Add the aspnet tables to the new database
I use the following tool which if you use it without any command line arguments works like a wizard.
%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe
3. Fix the database connections so they point to the same database
The following two lines are from the test application I made. Notice that the name of the second connectionstring (MyHealthContext) is identical to the name of the DbContext I am using for my code first classes.
DbContext:
public class MyHealthContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<PersonAttribute> PeopleAttributes { get; set; }
}
Web.config
<add name="ApplicationServices" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
<add name="MyHealthContext" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
4. SQL that removes all but the aspnetdb-tables
DECLARE #cmdDropConstraints VARCHAR(4000)
DECLARE #cmdDropTables VARCHAR(4000)
-- ======================================================================
-- DROP ALL THE FOREIGN KEY CONSTRAINTS FROM THE TABLES WE WANT TO DROP
-- ======================================================================
DECLARE cursorDropConstraints CURSOR FOR
SELECT
'ALTER TABLE ['+ s.name + '].[' + t.name + '] DROP CONSTRAINT [' + f.name +']'
FROM
sys.foreign_keys f
INNER JOIN sys.tables t ON f.parent_object_id=t.object_id
INNER JOIN sys.schemas s ON t.schema_id=s.schema_id
WHERE
t.is_ms_shipped=0
AND t.name NOT LIKE 'aspnet_%'
AND t.name <> 'sysdiagrams'
OPEN cursorDropConstraints
WHILE 1=1
BEGIN
FETCH cursorDropConstraints INTO #cmdDropConstraints
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropConstraints)
END
CLOSE cursorDropConstraints
DEALLOCATE cursorDropConstraints;
-- ======================================================================
-- DROP ALL THE RELEVANT TABLES SO THAT THEY CAN BE RECREATED
-- ======================================================================
DECLARE cursorDropTables CURSOR FOR
SELECT
'DROP TABLE [' + Table_Name + ']'
FROM
INFORMATION_SCHEMA.TABLES
WHERE
Table_Name NOT LIKE 'aspnet_%'
AND TABLE_TYPE <> 'VIEW'
AND TABLE_NAME <> 'sysdiagrams'
OPEN cursorDropTables
WHILE 1=1
BEGIN
FETCH cursorDropTables INTO #cmdDropTables
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropTables)
END
CLOSE cursorDropTables
DEALLOCATE cursorDropTables;
5. Code for the database initializer:
Replace the "SQL CODE GOES HERE" below with the sql from step 4
public class MyHealthInitializerDropCreateTables : IDatabaseInitializer<MyHealthContext>
{
public void InitializeDatabase(MyHealthContext context)
{
bool dbExists;
using (new TransactionScope(TransactionScopeOption.Suppress))
{
dbExists = context.Database.Exists();
}
if (dbExists)
{
// Remove all tables which are specific to the MyHealthContext (not the aspnetdb tables)
context.Database.ExecuteSqlCommand(#"SQL CODE GOES HERE");
// Create all tables which are specific to the MyHealthContext (not the aspnetdb tables)
var dbCreationScript = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
context.Database.ExecuteSqlCommand(dbCreationScript);
Seed(context);
context.SaveChanges();
}
else
{
throw new ApplicationException("No database instance");
}
}
protected virtual void Seed(MyHealthContext context)
{
//TODO: Add code for seeding your database with some initial data...
}
}
6. Code that hooks in your new database initializer
To make sure that the custom database initializer isn't accidentily run in the production environment i added a #if DEBUG statement since I always compile my code in release mode before publishing.
protected void Application_Start()
{
//TODO: Comment out this database initializer(s) before going into production
#if DEBUG
Database.SetInitializer<MyHealthContext>(new MyHealthInitializerDropCreateTables()); // Create new tables in an existing database
#endif
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Open ASPNetDB db in sql server by attaching as new database
Make Creation scripts of tables / Stored proce3dures from ASPNetDB and run in your own database to create tables in your database
Open web.config of your application and attach application to your own database. Copy the name of connection string
Go to membership area and replace connectionstring name with copied one
Do above step with area as well
this one's a bit difficult to explain, especially for a non-native english speaker:
I want to use some elliptic-curve-based digital signature functions of the C# implementation of the BouncyCastle crypto library. I wrote a small C# wrapper class that has the following methods:
public static void generateKeyPair(out byte[] private_key, out byte[] public_key)
public static void SignData(byte[] data, byte[] private_key, out byte[] signature)
public static void VerifyData(byte[] signature, byte[] data, byte[] public_key, out bool ok)
which all access the actual BouncyCastle library, BouncyCastle.Crypto.dll. Then I
created a "Visual Studio Strong Name Key File" and compiled the wrapper class file with the original BouncyCastle DLL as resource and the just created keyfile.
In SQL Server I did the following:
USE ACS
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
EXEC sp_configure 'xp_cmdshell', 1
EXEC sp_configure 'CLR enabled', 1;
RECONFIGURE WITH OVERRIDE;
ALTER DATABASE ACS SET TRUSTWORTHY OFF
USE master
CREATE ASYMMETRIC KEY CLR_Key_Wrapper FROM EXECUTABLE FILE = 'C:\Wrapper.dll'
CREATE ASYMMETRIC KEY CLR_Key_BouncyCastle FROM EXECUTABLE FILE = 'C:\BouncyCastle.Crypto.dll'
CREATE LOGIN CLR_Login_Wrapper FROM ASYMMETRIC KEY CLR_Key_Wrapper
CREATE LOGIN CLR_Login_BouncyCastle FROM ASYMMETRIC KEY CLR_Key_BouncyCastle
GRANT UNSAFE ASSEMBLY TO CLR_Login_Wrapper
GRANT UNSAFE ASSEMBLY TO CLR_Login_BouncyCastle
USE ACS
CREATE ASSEMBLY CLR FROM 'C:\Wrapper.dll' WITH PERMISSION_SET = UNSAFE
GO
CREATE PROCEDURE Generate_Keypair
(
#BSI_or_NIST BIT,
#Private_Key VARBINARY(64) OUTPUT,
#Public_Key VARBINARY(128) OUTPUT
)
AS EXTERNAL NAME CLR.Wrapper.generateKeyPair
GO
CREATE PROCEDURE Sign_Data
(
#BSI_or_NIST BIT,
#Data VARBINARY(8000),
#Private_Key VARBINARY(64),
#Signature VARBINARY(128) OUTPUT
)
AS EXTERNAL NAME CLR.Wrapper.SignData
GO
CREATE PROCEDURE Verify_Data
(
#BSI_or_NIST BIT,
#Signature VARBINARY(128),
#Data VARBINARY(8000),
#Public_Key VARBINARY(128),
#OK BIT OUTPUT
)
AS EXTERNAL NAME CLR.Wrapper.VerifyData
Now, using the three procedures Generate_Keypair, Sign_Data and Verify_Data in a TSQL batch or via a procedure call from e.g. a .Net client application sometimes works fine, but sometimes it causes the MSSQLSERVER service to go into something like an endless loop while using 100% CPU, especially when I try to install all this on a different machine than my own where I developed the code above (nevertheless, on my own machine it happens too, but less often).
The only thing that can be done is to cancel the executing query and drop the assembly which takes quite long when the CPU is in 100% use.
Can anyone tell me what I have done wrong? Thanks in advance,
Jan
Your stored procedures declare a BIT parameter which the C# methods lack.
To find out whether your assemblies are causing 100% CPU usage, you should log whenever your code executes, and check the generated log file (log4net, BareTail).
Is there any regular expression library written in T-SQL (no CLR, no extended SP, pure T-SQL) for SQL Server, and that should work with shared hosting?
Edit:
Thanks, I know about PATINDEX, LIKE, xp_ sps and CLR solutions
I also know it is not the best place for regex, the question is theoretical :)
Reduced functionality is also accepted
How about the PATINDEX function?
The pattern matching in TSQL is not a complete regex library, but it gives you the basics.
(From Books Online)
Wildcard Meaning
% Any string of zero or more characters.
_ Any single character.
[ ] Any single character within the specified range
(for example, [a-f]) or set (for example, [abcdef]).
[^] Any single character not within the specified range
(for example, [^a - f]) or set (for example, [^abcdef]).
If anybody is interested in using regex with CLR here is a solution. The function below (C# .net 4.5) returns a 1 if the pattern is matched and a 0 if the pattern is not matched. I use it to tag lines in sub queries. The SQLfunction attribute tells sql server that this method is the actual UDF that SQL server will use. Save the file as a dll in a place where you can access it from management studio.
// default using statements above
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
namespace CLR_Functions
{
public class myFunctions
{
[SqlFunction]
public static SqlInt16 RegexContain(SqlString text, SqlString pattern)
{
SqlInt16 returnVal = 0;
try
{
string myText = text.ToString();
string myPattern = pattern.ToString();
MatchCollection mc = Regex.Matches(myText, myPattern);
if (mc.Count > 0)
{
returnVal = 1;
}
}
catch
{
returnVal = 0;
}
return returnVal;
}
}
}
In management studio import the dll file via programability -- assemblies -- new assembly
Then run this query:
CREATE FUNCTION RegexContain(#text NVARCHAR(50), #pattern NVARCHAR(50))
RETURNS smallint
AS
EXTERNAL NAME CLR_Functions.[CLR_Functions.myFunctions].RegexContain
Then you should have complete access to the function via the database you stored the assembly in.
Then use in queries like so:
SELECT *
FROM
(
SELECT
DailyLog.Date,
DailyLog.Researcher,
DailyLog.team,
DailyLog.field,
DailyLog.EntityID,
DailyLog.[From],
DailyLog.[To],
dbo.RegexContain(Researcher, '[\p{L}\s]+') as 'is null values'
FROM [DailyOps].[dbo].[DailyLog]
) AS a
WHERE a.[is null values] = 0
There is some basic pattern matching available through using LIKE, where % matches any number and combination of characters, _ matches any one character, and [abc] could match a, b, or c...
There is more info on the MSDN site.
In case anyone else is still looking at this question, http://www.sqlsharp.com/ is a free, easy way to add regular expression CLR functions into your database.
If you are using SQL Server 2016 or above, you can use sp_execute_external_script along with R. It has functions for Regular Expression searches, such as grep and grepl.
Here's an example for email addresses. I'll query some "people" via the SQL Server database engine, pass the data for those people to R, let R decide which people have invalid email addresses, and have R pass back that subset of people to SQL Server. The "people" are from the [Application].[People] table in the [WideWorldImporters] sample database. They get passed to the R engine as a dataframe named InputDataSet. R uses the grepl function with the "not" operator (exclamation point!) to find which people have email addresses that don't match the RegEx string search pattern.
EXEC sp_execute_external_script
#language = N'R',
#script = N' RegexWithR <- InputDataSet;
OutputDataSet <- RegexWithR[!grepl("([_a-z0-9-]+(\\.[_a-z0-9-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,4}))", RegexWithR$EmailAddress), ];',
#input_data_1 = N'SELECT PersonID, FullName, EmailAddress FROM Application.People'
WITH RESULT SETS (([PersonID] INT, [FullName] NVARCHAR(50), [EmailAddress] NVARCHAR(256)))
Note that the appropriate features must be installed on the SQL Server host. For SQL Server 2016, it is called "SQL Server R Services". For SQL Server 2017, it was renamed to "SQL Server Machine Learning Services".
Closing Thoughts
Microsoft's implementation of SQL (T-SQL) doesn't have native support for RegEx. This proposed solution may not be any more desirable to the OP than the use of a CLR stored procedure. But it does offer an additional way to approach the problem.
You can use VBScript regular expression features using OLE Automation. This is way better than the overhead of creating and maintaining an assembly. Please make sure you go through the comments section to get a better modified version of the main one.
http://blogs.msdn.com/b/khen1234/archive/2005/05/11/416392.aspx
DECLARE #obj INT, #res INT, #match BIT;
DECLARE #pattern varchar(255) = '<your regex pattern goes here>';
DECLARE #matchstring varchar(8000) = '<string to search goes here>';
SET #match = 0;
-- Create a VB script component object
EXEC #res = sp_OACreate 'VBScript.RegExp', #obj OUT;
-- Apply/set the pattern to the RegEx object
EXEC #res = sp_OASetProperty #obj, 'Pattern', #pattern;
-- Set any other settings/properties here
EXEC #res = sp_OASetProperty #obj, 'IgnoreCase', 1;
-- Call the method 'Test' to find a match
EXEC #res = sp_OAMethod #obj, 'Test', #match OUT, #matchstring;
-- Don't forget to clean-up
EXEC #res = sp_OADestroy #obj;
If you get SQL Server blocked access to procedure 'sys.sp_OACreate'... error, use sp_reconfigure to enable Ole Automation Procedures. (Yes, unfortunately that is a server level change!)
More information about the Test method is available here
Happy coding