Showing posts with label CLR Function. Show all posts
Showing posts with label CLR Function. Show all posts

Thursday, September 12, 2013

mdq.regex CLR Functions -- Part 3: Regex Check Constraints!


Intro

This is the third article in my series, mdq.regex CLR Functions. In my first two articles we reviewed mdq.RegexIsMatch and mdq.RegexMatches. In each article I demonstrated how to use these CLR functions to pass regular expressions (regex for short) to your T-SQL queries. Today I will show you how to create a couple tables with check constraints that use regex to evaluate input using mdq.RegexIsMatch.


Setup

Setting up mdq.RegexIsMatch is extremely quick and simple provided you have the right credentials. See this article for detailed instructions on how to setup mdq.RegexIsMatch.


Sample Regex Check Constraints

Creating check constraints to test for tricky string patterns such as a valid IP Address or E-mail address can be a headache using T-SQL. Luckily, regex is perfect for this sort of task; below are two examples. In the first example we create a table with a column that only accepts valid E-mail addresses, the second only accepts valid IP addresses. This is easiest to learn by example so let's get to it...

Valid E-mail Address

CREATE TABLE Customers

(      cust_name     varchar(100),

       cust_email    nvarchar(200)

              CHECK (

                     mdq.RegexIsMatch([Cust_Email],

                     '^(([a-zA-Z0-9!#\$%\^&\*\{\}''`\+=-_\|/\?]+(\.[a-zA-Z0-9!#\$%\^&\*\{\}''`\+=-_\|/\?]+)*){1,64}@(([A-Za-z0-9]+[A-Za-z0-9-_]*){1,63}\.)*(([A-Za-z0-9]+[A-Za-z0-9-_]*){3,63}\.)+([A-Za-z0-9]{2,4}\.?)+){1,255}$',0)=1

                       )

);

Valid IP Address (no leading zeros)

CREATE TABLE Computers

(      computer_name varchar(100),

       ip_address varchar(20)

              CHECK (

                     mdq.RegexIsMatch([ip_address],

                     '^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$',0)=1

                       )

);




Conclusion

As you can see, these mdq.regex functions add a whole new world of possibilities to your SQL Server instance. Thanks for reading!


--ab

The new xmlsqlninja logo ('X-M-L'-'See-Quel' 'ninja')

Last Updated: 9/12/2013 (Posted)

Tuesday, May 28, 2013

mdq.regex CLR Functions -- Part 2: mdq.RegexMatches


Updated 12/7/2013

I updated this article on 12/7/2013 to include a link to instructions for enabling CLR Integration as and an updated link for the instructions to create the [Microsoft.MasterDataServices.DataQuality] assembly.


Intro

This is the second article in my series, mdq.regex CLR Functions. In my previous article about mdq.regex functions, mdq.regex CLR Functions -- Part 1 I introduced mdq.RegexIsMatch, a scalar CLR function that returns a bit based on if a regex pattern is matched. Today we will review mdq.RegexMatches.

mdq.RegexMatches is a Table Valued CLR function. Like mdq.RegexIsMatch, setup is fast, easy and this CLR was developed, tested, tuned and QA’d by Microsoft.

Setup

To create this function you will need:

  1. A schema named, “mdq” in your target DB
  2. CLR integration enabled
  3. The [Microsoft.MasterDataServices.DataQuality] assembly

See this article for details on enabling CLR integration and this article for the steps to create the assembly.


Run the following DDL to create mdq.RegexMatches:

CREATE FUNCTION mdq.[RegexMatches](@input [nvarchar](4000), @pattern [nvarchar](4000), @mask [tinyint] = 0)

RETURNS TABLE

(      [Sequence] [int] NULL,

       [Token] [nvarchar](4000) NULL)

WITH EXECUTE AS CALLER

AS

EXTERNAL NAME [Microsoft.MasterDataServices.DataQuality].[Microsoft.MasterDataServices.DataQuality.SqlClr].[RegexMatches]

GO



Using mdq.RegexMatches

mdq.RegexMatches searches an input string for all occurrences of a regular expression and returns all the successful matches. This function uses the regular expression functionality of the Matches method in the Microsoft .NET Framework.


Syntax

mdq.RegexMatches (input,pattern,mask)


Arguments

input

Is the input string for which to find matches. input is nvarchar(4000) with no default.

pattern

Is the regular expression pattern to use for matching. pattern is nvarchar(4000) with no default.

mask

Is the RegexOptions mask that specifies the behavior of the regular expression. For more information, see mdq.RegexMask (Transact-SQL).


mdq.RegexMatches Code Samples

In the first example below I am searching for numeric patterns. For the second example I will demonstrate how to search for records based on a word pattern. We will conclude with a technique for querying a table for valid and invalid EMail addresses.

mdq.regexMatches to find numeric patterns

-- (1) Let's find this pattern: (one or more numbers)-(one or more numbers)-(one or more numbers) [e.g. 222-111-9999]

SELECT Token AS pattern

FROM clr.RegexMatches(N'XYXYXYXYYX11-222-333XYXYXYXYXY', N'\d+-\d+-\d+', 0);

GO

 

-- (2) Let's find patterns of one or more numbers

DECLARE @text varchar(1000)='1 is one, 2 is two and 3 makes three',

        @find_cons_digits varchar(20)='\d+';

SELECT Sequence, Token AS value

FROM clr.RegexMatches(@text, @find_cons_digits, 0);

GO

 

 

-- (3) Let's find sequenses of two consecutive numbers

DECLARE @text varchar(1000)='1 is one, 2 is two and 3 makes three',

        @find_cons_digits varchar(20)='\d+';

 

SELECT  @text='1 is zero, 11 is one, 22 is two... 345678 makes three, four and five',

        @find_cons_digits='\d{2}';

 

SELECT Sequence, Token AS value

FROM clr.RegexMatches(@text, @find_cons_digits, 0);

GO


mdq.regexMatches to find word patterns

-- (4) Let's perform a word count

DECLARE @quote varchar(1000)='The world is a dangerous place to live; not because of the people who are evil, but because of the people who dont do anything about it. --Albert Einstein',

                @quote2 varchar(100)='apple orange orange pear',

                @word_count varchar(20)='\b(\w+)',

                @ab_count varchar(20)='\b([ab]\S+)',

                @find_duplicates varchar(50)='\b(\w+?)\s\1\b';

 

SELECT COUNT(*) AS [word count]

FROM clr.RegexMatches(@quote,@word_count,0);

-------------------------------------------------------

 

-- (5) Let's find how many words begin with A or B

SELECT COUNT(*) AS [words that begin with A or B]

FROM clr.RegexMatches(@quote, @ab_count,0);

 

-- (6) Let's find repeated words

SELECT @quote2 AS quote, Token AS [repeated words]

FROM clr.RegexMatches(@quote2,@find_duplicates,0);

 

-- (7) Let's normalize a string, a series of numbers or both:

SELECT Sequence, Token AS val

FROM clr.RegexMatches(N'NormalizeMe', N'([a-zA-z])', ''); --string

 

SELECT Sequence, Token AS val

FROM clr.RegexMatches(N'54321', N'\d', ''); --series of numbers

 

SELECT Sequence, Token AS val

FROM clr.RegexMatches(N'text1234', N'([a-zA-z0-9])', ''); --text and numbers


Query that returns valid Email addresses and one that only returns invalid addresses:

IF OBJECT_ID('tempdb..#customer_email') IS NOT NULL

        DROP TABLE #customer_email;

 

CREATE TABLE #customer_email

(       email_id int identity primary key,

        cust_id int NOT NULL,

        email nvarchar(100) unique NOT NULL);

 

INSERT INTO #customer_email (cust_id, email)

        SELECT  1,'john.doe@a+++cme. com'       UNION ALL

        SELECT  1,'JohnDoe@somewhere.com'   UNION ALL

        SELECT  2,'Sally.Smith@Microsoft'   UNION ALL

        SELECT  3,'AlChurch@turning.net'    UNION ALL

        SELECT  4,'JaneDoe@aol.com';

GO

 

DECLARE @validEmail nvarchar(1000);

SET @validEmail='^(([a-zA-Z0-9!#\$%\^&\*\{\}''`\+=-_\|/\?]+(\.[a-zA-Z0-9!#\$%\^&\*\{\}''`\+=-_\|/\?]+)*){1,64}@(([A-Za-z0-9]+[A-Za-z0-9-_]*){1,63}\.)*(([A-Za-z0-9]+[A-Za-z0-9-_]*){3,63}\.)+([A-Za-z0-9]{2,4}\.?)+){1,255}$';

 

-- (1) This will return records with only valid EMail addresses

SELECT e. *

FROM #customer_email e

CROSS APPLY clr.RegexMatches(e.email,@validEmail, 0);

 

-- (2) This will return records with only invalid EMail addresses

SELECT e.email_id, e.cust_id, e.email

FROM #customer_email e

EXCEPT

SELECT ex.email_id, ex.cust_id, ex.email

FROM #customer_email ex

CROSS APPLY clr.RegexMatches(ex.email, @validEmail, 0);

 

DROP TABLE #customer_email;

 


... EMail Query results:



Conclusion

Today we reviewed the steps for setting up and using mdq.RegexMatches. Thanks for reading!


--ab

The new xmlsqlninja logo ('X-M-L'-'See-Quel' 'ninja')

Last Updated: 12/7/2013 (minor updates, repaired broken links)

Sunday, April 28, 2013

mdq.XmlTransform -- Part 2: Creating an MS Word Doc


Intro

mdq.xmlTransform can do amazing things. Today I will show you how to write a T-SQL query that returns a Microsoft Office Word document viewable in Word 2003 and later (and most programs that open .doc or .docx files). With mdq.xmlTransform it's very easy. If you don't yet have it, see Setting up mdq.XmlTransform for DDL and setup instructions; it takes less than a minute to setup.


Let's get right to it

Some may be intimidated at the idea of writing a query that returns an MS Word document. Don't be, it's simple. That's why I'll keep this real short and just show you.


(1) A Quick WordProcessingML (WordML) tutorial

1. Open notepad (Start > Run > notepad.exe)
2. Copy/paste the code below into your blank notepad file:

<?xml version="1.0" encoding="utf-8"?>
   
<?mso-application progid="Word.Document"?>
   
       
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
           
<w:body>
                               
           
<w:p>
               
<w:r>
                   
<w:t>Hello Word!!!</w:t>
               
</w:r>
           
</w:p>
   
            
</w:body>
       
</w:wordDocument>

3. Go to file > save as and for "Save as Type" select All Files
4. For the file name type helloWord.xml
5. Click Save then close the file.

Now open the file with Microsoft Word version 2003 or later and you should see this:

Congrats! You just created your first word document using WordML.


(2) Using mdq.xmlTransform to produce a Word document

SQL Server supports the XML Data Type. WordML is XML. mdq.xmlTransform transforms XML. Now that you have seen how WordML works lets write a SQL query that returns a Word Document.

Using a database with mdq.xmlTranform, copy/paste and execute this code into SSMS:

DECLARE @xslt xml='

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">

    <xsl:output method="xml"  xml:space="default" />

 

    <!--  This template does all the work -->

    <xsl:template match="/" xml:space="default">

        <xsl:processing-instruction name="mso-application" xml:space="default">

            <xsl:text>progid="Word.Document"</xsl:text>

        </xsl:processing-instruction>

  

        <w:wordDocument><!-- This creates the word doc -->

            <w:body>

            <w:p>

                <w:r>

                    <w:t>Created with mdq.xmlTransform (no loops)</w:t>

                </w:r>

            </w:p>

            </w:body>

        </w:wordDocument>

    </xsl:template>  

    <xsl:template match="DataSourceID"/>

</xsl:stylesheet>';

 

SELECT REPLACE(mdq.xmlTransform('',@xslt),

                      '<?mso-application progid="Word.Document"?>',

                      '<?xml version="1.0" encoding="ISO-8859-1"?>

                       <?mso-application progid="Word.Document"?>') AS xml_output;

 

You should already know what will happen if you: copy/paste the result set into a new notepad file, save it like we did earlier, and then open it with Word (but feel free to do it again if you thought it was cool).


...and now for something a little more impressive...

Hopefully some developers, DBAs and BI people see the potential here. Think of how many SQL objects that are (or can be) stored in XML format. Query plans, traces, SSRS reports (RDL) and SSIS packages (DTSX packages) to name a few. Word and Excel files, your whatever.config files, a multitude of SharePoint objects, RSS feeds, web service data, Extended Events, etc, etc... All XML. Thanks to the XML data type and mdq.xmlTransform, information from all these things can be stored, measured, analyzed and, as I will show you in a moment, stuffed into XML files or fragments.

(3) Create a Word doc with SSRS Report (RDL) data

The code below contains two XML documents. For the first (1) I grabbed an some data from an SSRS RDL file (just part of it to keep things simple). Using T-SQL REPLACE I removed the rd: namespace references. The second (2) is an XML transform that will extract the Data Provider and Connection String from an SSRS report and use it to create a Word Doc.

<!--  (1) Grab an RDL XML FRAGMENT from an SSRS report -->
<!--  (using T-SQL REPLACE to remove the rd: namespace references) -->

<DataSource Name="ReportingDemo">
   
<DataSourceID>f34d206b-ca72-4ca6-9d5c-4151cd7eaxxx</DataSourceID>
   
<ConnectionProperties>
       
<DataProvider>SQL</DataProvider>
       
<ConnectString>
            Data Source=ABC;EFG Catalog=XYZ
       
</ConnectString>
   
</ConnectionProperties>
</DataSource>

<!—(2) Create an XSLT function that puts this data into a MS Word Doc -->

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
   
<xsl:output method="xml"  xml:space="default" omit-xml-declaration="no" />


    <!--  This template does all the work -->
    <xsl:template match="/DataSource/ConnectionProperties" xml:space="preserve">
        <xsl:processing-instruction name="mso-application" xml:space="default">
            <xsl:text>progid="Word.Document"</xsl:text>
        </xsl:processing-instruction>
   
        <w:wordDocument><!-- This creates the word doc -->
            <w:body>

                <!-- These two templates collect the data -->
                <xsl:apply-templates select="DataProvider"/>
                <xsl:apply-templates select="ConnectString"/>
            </w:body>
        </w:wordDocument>
    </xsl:template>
   
    <xsl:template match="DataProvider" xml:space="preserve">
            <w:p>
                <w:r>
                    <w:t><xsl:apply-templates /></w:t>
                </w:r>
            </w:p>
    </xsl:template>

    <xsl:template match="ConnectString" xml:space="preserve">
            <w:p>
                <w:r>
                    <w:t><xsl:apply-templates /></w:t>
                </w:r>
            </w:p>
    </xsl:template>


    <!-- This disposes of DataSourceID-->
    <xsl:template match="DataSourceID"/>

</xsl:stylesheet>

If we feed that RDL data and transform to mdq.xmlTransform like this:

DECLARE @xml xml='

<DataSource Name="ReportingDemo">

    <DataSourceID>f34d206b-ca72-4ca6-9d5c-4151cd7eaxxx</DataSourceID>

    <ConnectionProperties>

        <DataProvider>SQL</DataProvider>

        <ConnectString>

            Data Source=ABC;EFG Catalog=XYZ

        </ConnectString>

    </ConnectionProperties>

</DataSource>',

 

@xslt xml='

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">

    <xsl:output method="xml"  xml:space="default" />

 

    <!--  This template does all the work -->

    <xsl:template match="/DataSource/ConnectionProperties" xml:space="default">

        <xsl:processing-instruction name="mso-application" xml:space="default">

            <xsl:text>progid="Word.Document"</xsl:text>

        </xsl:processing-instruction>

  

        <w:wordDocument><!-- This creates the word doc -->

            <w:body>

                <!-- These two templates collect the data -->

                <xsl:apply-templates select="DataProvider"/>

                <xsl:apply-templates select="ConnectString"/>

            </w:body>

        </w:wordDocument>

    </xsl:template>

  

    <xsl:template match="DataProvider" xml:space="preserve">

            <w:p>

                <w:r>

                    <w:t><xsl:apply-templates /></w:t>

                </w:r>

            </w:p>

    </xsl:template>

 

    <xsl:template match="ConnectString" xml:space="preserve">

            <w:p>

                <w:r>

                    <w:t><xsl:apply-templates /></w:t>

                </w:r>

            </w:p>

    </xsl:template>

 

    <!-- This disposes of DataSourceID-->

    <xsl:template match="DataSourceID"/>

</xsl:stylesheet>';

 

SELECT REPLACE(mdq.xmlTransform(@xml,@xslt),

                      '<?mso-application progid="Word.Document"?>',

                      '<?xml version="1.0" encoding="ISO-8859-1"?>

                       <?mso-application progid="Word.Document"?>') AS xml_output;

 

We get this:

<?xml version="1.0" encoding="utf-8"?>
   
<?mso-application progid="Word.Document"?>
   
       
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
           
<w:body>
               
           
<w:p>
               
<w:r>
                   
<w:t>SQL</w:t>
               
</w:r>
           
</w:p>
               
           
<w:p>
               
<w:r>
                   
<w:t>Data Source=ABC;EFG Catalog=XYZ</w:t>
               
</w:r>
           
</w:p>
   
            
</w:body>
       
</w:wordDocument>

...which, if we copy/paste into notepad and save as wow.xml, then open with Microsoft Office Word, we get:

Conclusion

mdq.xmlTransform is a powerful tool. Period. Today I showed you how to create a basic Word document using just T-SQL and mdq.xmlTransform. Thanks for reading!


--ab

The new xmlsqlninja logo ('X-M-L'-'See-Quel' 'ninja')

Last Updated: 4/29/2013 11:35am (Posted, fixed code issues)