Problems displaying this newsletter? View online.
SQL Server Central
Featured Contents
Question of the Day
Featured Script
Redgate SQL Monitor
The Voice of the DBA
 

Fragmented or Centralized Data

I read a piece recently that talks about the hassles of copying data multiple times for different applications. In my experience, I haven't seen this to be the main problem with data, where we might replicate, in a general sense, data across different data stores to support different applications. Certainly lots of ETL jobs exist to copy data to new stores for different purposes, which perhaps is what the author is implying.

The idea of protecting data is one that is becoming a greater concern for many organizations. In fact, I'd argue that a number of the recent high profile data breaches in the last couple years involve copying data from some RDBMS store to an ElasticSearch server that isn't secure. Any movement of sensitive data, whether to warehouse or Power BI report, should be in a secure way.

For years we've had minor issues with data security in Excel worksheets; a similar problem continues to exist with both data stores and reporting tools that might contain copies of data. In some sense, this is actually no different than the problems of losing paper reports in the distant past.

The solution given in the article is to share data from a single store among more applications. That's been the practice in many places I've worked, with the challenges of additional load and performance concerns on the data store. Modern distributed SQL Servers can use AGs or (after SQL Server 2017), Kubernetes, to scale out and potentially handle the loads, but those choices aren't without their own resource costs and challenges.

Ultimately, we aren't going to get away from moving data around. Certainly we have needs to deal with dev/test environments even if we don't have any other data movement. While I do think the future of large data workloads will involve less movement, we aren't going to eliminate movement.  We may build more applications that connect to a single data store, which is likely as our platforms become more powerful and enable scale-out capabilities to meet workload growth.

We also need to ensure that copies of data made for different purposes as well protected. Most businesses need to develop better skills and habits to limit sensitive data in dev and test environments, as well as proper access controls for data copies that are used in production environments.

Steve Jones - SSC Editor

Join the debate, and respond to today's editorial on the forums

 
  Featured Contents

Using a Surrogate vs Natural Key

steve@thesqlguy.com from SQLServerCentral

A common debate in table architecture is whether to use a natural or surrogate key. In this article, I discuss some of the advantages to each method.

Database Tools and Extensions for Visual Studio 2019

Additional Articles from Database Journal

In this article, you will explore tools and extensions that can be used with Visual Studio 2019 to improve your database experience irrespective of what database you use.

From the SQL Server Central Blogs - Employees don’t work for you; you work for them

jphillips 46546 from Another SQL Geek

Alex Yates (b | t) is hosting this month’s T-SQL Tuesday (the 119th edition) with asking us to write about something in our IT career that we have changes our minds...

From the SQL Server Central Blogs - Dynamic Column Level Security with Power BI and SSAS

Dustin Ryan from SQL with Dustin Ryan

Last week I was asked to tackle a requirement by a customer adopting Analysis Services to enable data exploration and ad hoc analysis by their users. One of their...

 

  Question of the Day

Today's question (by Steve Jones - SSC Editor):

 

More Computed Column Indexes

I have a table with a column defined like this:
SalePrice FLOAT
I then created a computed column on this field:
ALTER TABLE dbo.MonthlySales ADD EstSalePrice AS ROUND(SalePrice, 0)
I want to index this column with the following code.
CREATE INDEX MonthlySales_EstSalePrice ON dbo.MonthlySales (EstSalePrice)
What happens when I run this?

Think you know the answer? Click here, and find out if you are right.

 

 

  Yesterday's Question of the Day (by Steve Jones - SSC Editor)

Another Typo

I am creating more test data. I accidentally execute this set of statements in SSMS.

 INSERT dbo.Claim
 (
     PharmNo,
     ClaimNo,
     ClaimDate
 )
 VALUES
 (
     'LMNOP', '999', GETDATE()
 )
 GO
 5
SELECT * FROM dbo.Claim AS c
GO

What happens?

Answer: One row is inserted, a syntax error near '5' is returned, and the SELECT is not executed

Explanation: While T-SQL doesn't often care about whitespace, you cannot put the parameter for GO on another line. In this case, the INSERT is executed and then a syntax error is returned near '5'. The SELECT batch is not executed. Ref: GO - https://docs.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-go?view=sql-server-2017

Discuss this question and answer on the forums

 

Featured Script

Generic trigger for maintaining the Audit Log

Brahmanand Shukla from SQLServerCentral

This trigger will work even if you don't have the primary key on any table. Mechanism to configure the primary key in config table has been provided. This can be very helpful to solve the system audit requirements without much efforts.

Disclaimer : Audit Log trigger has performance overhead. It’s meant for table which is not frequently updated, deleted or inserted. Please take precaution when creating the trigger on the table having too many columns.

/*
AUTHOR : BRAHMANAND SHUKLA
DATE : 18-MAY-2018
PURPOSE : Generic trigger for maintaining the Audit Log

STEPS FOR IMPLEMENTATION:

STEP 1: Create the following tables on the same database or on seperate Audit Log Database.
I have used seperate database for Audit Log with name "Audit_Log".

CREATE TABLE [AUDIT_LOG_TABLE_PRIMARY_KEY]
(
[ID] NUMERIC NOT NULL IDENTITY(1, 1) CONSTRAINT PK_AUDIT_LOG_TABLE_PRIMARY_KEY_ID PRIMARY KEY CLUSTERED
, [TABLE_SCHEMA] SYSNAME NOT NULL
, [TABLE_NAME] SYSNAME NOT NULL
, [PRIMARY_KEY_COLUMN_NAME] SYSNAME NOT NULL
, CONSTRAINT UQ_AUDIT_LOG_TABLE_PRIMARY_KEY_TABLE_SCHEMA_TABLE_NAME UNIQUE ([TABLE_SCHEMA], [TABLE_NAME])
)

CREATE TABLE [AUDIT_LOG_HEADER]
(
[ID] NUMERIC NOT NULL IDENTITY(1, 1) CONSTRAINT PK_AUDIT_LOG_HEADER_ID PRIMARY KEY CLUSTERED
, [TABLE_NAME] SYSNAME NOT NULL
, [PRIMARY_KEY_COLUMN_NAME] SYSNAME NOT NULL
, [PRIMARY_KEY_COLUMN_VALUE] NVARCHAR(MAX)
, [AUDIT_ACTION] CHAR(1) NOT NULL
, [HOST_NAME] NVARCHAR(100) NOT NULL
, [APP_NAME] NVARCHAR(100) NOT NULL
, [AUDIT_USERID] NVARCHAR(50) NOT NULL
, [AUDIT_DATETIME] DATETIME NOT NULL CONSTRAINT DF_AUDIT_LOG_HEADER_AUDIT_DATETIME DEFAULT(GETDATE())
)

CREATE TABLE [AUDIT_LOG_DETAIL]
(
[ID] NUMERIC NOT NULL IDENTITY(1, 1) CONSTRAINT PK_AUDIT_LOG_DETAIL_ID PRIMARY KEY CLUSTERED
, [HEADERID] NUMERIC NOT NULL CONSTRAINT FK_AUDIT_LOG_DETAIL_HEADERID FOREIGN KEY REFERENCES AUDIT_LOG_HEADER (ID)
, [COLUMN_NAME] SYSNAME NOT NULL
, [COLUMN_OLD_VALUE] NVARCHAR(MAX)
, [COLUMN_NEW_VALUE] NVARCHAR(MAX)
)

STEP 2: Change the table name in the trigger name and on clause with the desired table name.
STEP 3: Change the database name and schema name of the "AUDIT_LOG_TABLE_PRIMARY_KEY", "AUDIT_LOG_HEADER" & "AUDIT_LOG_DETAIL" tables in the trigger.
STEP 4: Create the trigger on the desired table.
STEP 5: If your table doesn't has the Primary Key column then add an entry in "AUDIT_LOG_TABLE_PRIMARY_KEY" table.
STEP 6: You are all set. Check "AUDIT_LOG_HEADER" & "AUDIT_LOG_DETAIL" tables for Audit Logs by performing Insert, Update and Delete on the source table.

IMPORTANT NOTE:
1) The trigger will write the Old & New values in "AUDIT_LOG_DETAIL" table in case of update and only if there is change in value.
2) The trigger will write the columns and their values as New value in "AUDIT_LOG_DETAIL" table in case of insert.
3) The trigger will not write any row in "AUDIT_LOG_DETAIL" table in case of delete.
4) The trigger will write every event/action such as insert, update and delete in "AUDIT_LOG_HEADER" table.
*/
CREATE TRIGGER TR_Audit_Log_Hdr_Client
ON Hdr_Client
FOR INSERT, UPDATE, DELETE
AS
BEGIN TRY
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

IF OBJECT_ID('tempdb..#COLUMNS') IS NOT NULL DROP TABLE #COLUMNS;
CREATE TABLE #COLUMNS
(
[COLUMN_NAME] SYSNAME
, [ORDINAL_POSITION] INT PRIMARY KEY CLUSTERED
)

IF OBJECT_ID('tempdb..#unique_primary_key_values') IS NOT NULL DROP TABLE #unique_primary_key_values;
CREATE TABLE #unique_primary_key_values
(
[PRIMARY_KEY_COLUMN_NAME] SYSNAME
, [ROWID] NUMERIC PRIMARY KEY CLUSTERED
)

IF OBJECT_ID('tempdb..#inserted') IS NOT NULL DROP TABLE #inserted;
IF OBJECT_ID('tempdb..#deleted') IS NOT NULL DROP TABLE #deleted;

DECLARE @TABLE_SCHEMA SYSNAME
DECLARE @TABLE_NAME SYSNAME
DECLARE @AUDIT_ACTION CHAR(1)

DECLARE @PRIMARY_KEY_COLUMN_NAME SYSNAME
DECLARE @PRIMARY_KEY_COLUMN_VALUE NVARCHAR(MAX)

DECLARE @PARAMETER_DEFINITION NVARCHAR(MAX)
DECLARE @SQL_QUERY NVARCHAR(MAX)

DECLARE @COLUMN_NAME SYSNAME
DECLARE @ORDINAL_POSITION INT

DECLARE @COLUMN_OLD_VALUE NVARCHAR(MAX)
DECLARE @COLUMN_NEW_VALUE NVARCHAR(MAX)

DECLARE @HEADERID NUMERIC

DECLARE @ROWID NUMERIC

IF EXISTS(SELECT 1 FROM inserted)
AND EXISTS(SELECT 1 FROM deleted)
BEGIN
SET @AUDIT_ACTION = 'U';
END
ELSE
BEGIN
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
SET @AUDIT_ACTION = 'D';
END
ELSE IF EXISTS(SELECT 1 FROM inserted)
BEGIN
SET @AUDIT_ACTION = 'I';
END
END

IF @AUDIT_ACTION IS NULL RETURN;

SELECT @TABLE_SCHEMA = OBJECT_SCHEMA_NAME(parent_id)
, @TABLE_NAME = OBJECT_NAME(parent_id)
FROM sys.triggers
WHERE object_id = @@PROCID;

-- Get the Primary Key column name
BEGIN
SELECT @PRIMARY_KEY_COLUMN_NAME = KEY_COLUMN_USAGE.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE
ON TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND TABLE_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME
WHERE TABLE_CONSTRAINTS.TABLE_SCHEMA = @TABLE_SCHEMA
AND TABLE_CONSTRAINTS.TABLE_NAME = @TABLE_NAME

IF @PRIMARY_KEY_COLUMN_NAME IS NULL
BEGIN
SELECT @PRIMARY_KEY_COLUMN_NAME = [PRIMARY_KEY_COLUMN_NAME]
FROM Audit_Log.[dbo].[AUDIT_LOG_TABLE_PRIMARY_KEY]
WHERE [TABLE_SCHEMA] = @TABLE_SCHEMA
AND [TABLE_NAME] = @TABLE_NAME
END
END

SELECT * INTO #inserted FROM inserted;
SELECT * INTO #deleted FROM deleted;

-- Get all the columns of the table
INSERT INTO #COLUMNS (COLUMN_NAME, ORDINAL_POSITION)
SELECT [COLUMN_NAME], [ORDINAL_POSITION]
FROM INFORMATION_SCHEMA.COLUMNS
WHERE [TABLE_SCHEMA] = @TABLE_SCHEMA
AND [TABLE_NAME] = @TABLE_NAME;

SET @SQL_QUERY = 'WITH cte_unique_primary_key_values
AS
(
SELECT [' + @PRIMARY_KEY_COLUMN_NAME + '] AS PRIMARY_KEY_COLUMN_NAME
FROM #inserted
UNION
SELECT [' + @PRIMARY_KEY_COLUMN_NAME + '] AS PRIMARY_KEY_COLUMN_NAME
FROM #deleted
)

INSERT INTO #unique_primary_key_values (PRIMARY_KEY_COLUMN_NAME, ROWID)
SELECT PRIMARY_KEY_COLUMN_NAME,
ROW_NUMBER() OVER(ORDER BY (SELECT PRIMARY_KEY_COLUMN_NAME)) AS ROWID
FROM cte_unique_primary_key_values;'

EXECUTE sp_executesql @SQL_QUERY

SET @ROWID = 1
WHILE EXISTS (SELECT 1 FROM #unique_primary_key_values WHERE ROWID = @ROWID)
BEGIN
-- Get Primary Key Column Value
BEGIN
SET @PRIMARY_KEY_COLUMN_VALUE = NULL

SET @SQL_QUERY = 'SELECT @PRIMARY_KEY_COLUMN_VALUE = PRIMARY_KEY_COLUMN_NAME
FROM #unique_primary_key_values
WHERE ROWID = @ROWID';

SET @PARAMETER_DEFINITION = '@ROWID NUMERIC
, @PRIMARY_KEY_COLUMN_NAME SYSNAME
, @PRIMARY_KEY_COLUMN_VALUE NVARCHAR(MAX) OUTPUT';

EXECUTE sp_executesql @SQL_QUERY
, @PARAMETER_DEFINITION
, @ROWID
, @PRIMARY_KEY_COLUMN_NAME
, @PRIMARY_KEY_COLUMN_VALUE OUTPUT;
END

-- Maintain Audit Log Header
INSERT INTO Audit_Log.[dbo].[AUDIT_LOG_HEADER]
(
[TABLE_NAME]
, [PRIMARY_KEY_COLUMN_NAME]
, [PRIMARY_KEY_COLUMN_VALUE]
, [AUDIT_ACTION]
, [HOST_NAME]
, [APP_NAME]
, [AUDIT_USERID]
, [AUDIT_DATETIME]
)
VALUES
(
@TABLE_NAME
, @PRIMARY_KEY_COLUMN_NAME
, @PRIMARY_KEY_COLUMN_VALUE
, @AUDIT_ACTION
, HOST_NAME()
, APP_NAME()
, SUSER_SNAME()
, GETDATE()
)

SET @HEADERID = SCOPE_IDENTITY();

-- Iterate through the columns and maintain the Audit Log Detail (Old and New values of the column against the supplied Primary Key).
SET @ORDINAL_POSITION = 1;
WHILE EXISTS (SELECT 1 FROM #COLUMNS WHERE [ORDINAL_POSITION] = @ORDINAL_POSITION)
BEGIN
SELECT @COLUMN_NAME = [COLUMN_NAME]
FROM #COLUMNS
WHERE ORDINAL_POSITION = @ORDINAL_POSITION;

-- Get the old value of the column against the supplied Primary Key
IF @AUDIT_ACTION IN ('U', 'D')
BEGIN
SET @COLUMN_OLD_VALUE = NULL

SET @SQL_QUERY = 'SELECT @COLUMN_OLD_VALUE = [' + @COLUMN_NAME + ']
FROM #deleted
WHERE [' + @PRIMARY_KEY_COLUMN_NAME + '] = @PRIMARY_KEY_COLUMN_VALUE';

SET @PARAMETER_DEFINITION = '@PRIMARY_KEY_COLUMN_NAME SYSNAME
, @PRIMARY_KEY_COLUMN_VALUE NVARCHAR(MAX)
, @COLUMN_NAME SYSNAME
, @COLUMN_OLD_VALUE NVARCHAR(MAX) OUTPUT';

EXECUTE sp_executesql @SQL_QUERY
, @PARAMETER_DEFINITION
, @PRIMARY_KEY_COLUMN_NAME
, @PRIMARY_KEY_COLUMN_VALUE
, @COLUMN_NAME
, @COLUMN_OLD_VALUE OUTPUT;
END

-- Get the new value of the column against the supplied Primary Key
IF @AUDIT_ACTION IN ('U', 'I')
BEGIN
SET @COLUMN_NEW_VALUE = NULL

SET @SQL_QUERY = 'SELECT @COLUMN_NEW_VALUE = [' + @COLUMN_NAME + ']
FROM #inserted
WHERE [' + @PRIMARY_KEY_COLUMN_NAME + '] = @PRIMARY_KEY_COLUMN_VALUE';

SET @PARAMETER_DEFINITION = '@PRIMARY_KEY_COLUMN_NAME SYSNAME
, @PRIMARY_KEY_COLUMN_VALUE NVARCHAR(MAX)
, @COLUMN_NAME SYSNAME
, @COLUMN_NEW_VALUE NVARCHAR(MAX) OUTPUT';

EXECUTE sp_executesql @SQL_QUERY
, @PARAMETER_DEFINITION
, @PRIMARY_KEY_COLUMN_NAME
, @PRIMARY_KEY_COLUMN_VALUE
, @COLUMN_NAME
, @COLUMN_NEW_VALUE OUTPUT;
END

-- In case of Insert, maintain New Value
-- In case of Update, maintain both Old and New Value but only if both Old and New Value are different
-- In case of Delete, don't maintain either of the values
IF ((@AUDIT_ACTION = 'I') OR (@AUDIT_ACTION = 'U' AND ISNULL(@COLUMN_OLD_VALUE, '') <> ISNULL(@COLUMN_NEW_VALUE, '')))
BEGIN
INSERT INTO Audit_Log.[dbo].[AUDIT_LOG_DETAIL]
(
HEADERID
, COLUMN_NAME
, COLUMN_OLD_VALUE
, COLUMN_NEW_VALUE
)
VALUES
(
@HEADERID
, @COLUMN_NAME
, @COLUMN_OLD_VALUE
, @COLUMN_NEW_VALUE
)
END

SET @ORDINAL_POSITION = @ORDINAL_POSITION + 1;
END

SET @ROWID = @ROWID + 1;
END
END TRY

BEGIN CATCH
DECLARE @ErrorNumber INT = ERROR_NUMBER();
DECLARE @ErrorLine INT = ERROR_LINE();
DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
DECLARE @ErrorState INT = ERROR_STATE();
DECLARE @ErrorProcedure VARCHAR(500) = ERROR_PROCEDURE();

SET @ErrorMessage = ISNULL(@ErrorMessage, '')
+ ' Procedure Name: ' + ISNULL(@ErrorProcedure, '')
+ ' Error Number: ' + CAST(ISNULL(@ErrorNumber, 0) AS VARCHAR(10))
+ ' Line Number: ' + CAST(ISNULL(@ErrorLine, 0) AS VARCHAR(10));

RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

More »

 

Database Pros Who Need Your Help

Here's a few of the new posts today on the forums. To see more, visit the forums.


SQL Server 2017 - Administration
SQL Server 2017 Remote BLOB Storage - I've been reading about SQL Server RBS for about a day and feel that it would be an ideal solution for our needs. Considering we are storing very large files in our database using varbinary(max) data type. My question here is, are we able to utilize our existing storage array for storing our files? My […]
SQL Server 2017 - Development
Remove Carriage Return for NULL values in column - Folks: I would like to replace CHAR(13) (Carriage Return) with space and I use this SQL to replace it, but the problem is when the column has NULL values it would not replace it.  How can I remove carriage return from null values? UPDATESET = REPLACE(Column Name, CHAR(13), '')
Put result of LMXL into DB table - How can I put the result of HTML into a DB table? I extracted the info using LMXL, Python.   Thanks
SQL Server 2016 - Administration
Audit Security - Is there a way to find any process or someone who could have removed a Domain login against sql server? Otherwise using server level trigger would be a best option to audit in the future to an event like this? Please advise? Thanks in advance!
Standalone install with database mirroring and virtual IP - Hello all, I have a standalone install and need to set  up a database mirror to another node although the caveat is that the application cannot change the instance connection IP ? What are my options Thanks  
SQL Server 2016 - Development and T-SQL
get around invalid date - I have a query like below, need to manipulate servicedatekey field as a date with a convert (is integer now) which should have date data such as '20180901' but has about 25K records which have a -1.  Get arithmetic overflow with this select count(servicedatekey) from FactClaimSummary where isdate(SERVICEDATEKEY) = 1 and convert(datetime,servicedatekey) > '2018/01/01'
Administration - SQL Server 2014
Alwayson error - Hi, We configured the Ag for 15 dbs in SQL 2014. Primary,seconadry and DR node. Morning i found that the databases are not sync in DR Node. When i tried to join the db to AG (Resume data movement) i got the below error. Failed to resume data movement in database 'abc', which resides on […]
Logs were not shrinking - Hello, I am trying to shrink my production log file as its occupying lot of space in L drive. Its in Alwayson setup. two days back i already removed the db from alwayon and made it simple recovery mode and shrinked the log file.After this i added back to AG. Now again the logs were […]
Development - SQL Server 2014
Effect of force update stat on tables used by query when db auto update stat on - Hi, Developer complain that in  dynamic AX  production ,application query execution time degrade runs 3 minutes or more   or " never finish "   SELECT SUM(T2.POSTEDQTY), SUM(T2.DEDUCTED), SUM(T2.RECEIVED), SUM(T2.RESERVPHYSICAL), T2.ITEMID, T3.INVENTSITEID, T3.INVENTLOCATIONID FROM INVENTTABLE ~ 59987 row T1 CROSS JOIN INVENTSUM  ~ 1411479 rows T2 CROSS JOIN INVENTDIM     ~ 9628399 rows T3 CROSS JOIN PDSBATCHATTRIBUTES […]
Using SELECT LEFT and WHERE IN in the same SELECT Statement. - Greetings ~ I'm trying to get the following query to work in SQL 2014 [Code] SELECT LEFT(AKey, 3) AS Foo FROM dbo.AKey WHERE AKey IN ('AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'GGG', 'JJJ') AND LEN(AKey) = 9 [/Code] The issue isn't that the query throws an error - but rather it returns an empty set - […]
Saving Hebrew text as nvarchar - Is there something special I need to do to be able to save some Hebrew text in my table?    I have the column set as nvarchar(max) yet when i do something like this   It returns ScriptID LanguageID SectionID ScriptText 67 11 13 ?????? ??? ?? ??????, ???? ???
SQL 2012 - General
Installing .net 3.5 feature on windows 2012 server stand. fails with 0x800F081F - Hello, I need some help. I have a new Windows 2012 Standard server which is a VM machine on vSphere 6.5 and I am trying to install SQL Server 2012 Standard, which seems to require .net 3.5.  The Windows server build is 6.3 (Build 9600). I found this article which I followed, however removing one […]
SQL Server 2012 - T-SQL
Greater-Than Empty String Comparison Yielding Unexpected Results - I'm not sure when, but years ago as a BASIC programmer, I picked up the habit of checking to see if a string was not an empty string by asking if it was "greater-than" an empty string. Something like this: IF(myString > "") When I began writing T-SQL this naturally transitioned into: WHERE myColumn > […]
Reporting Services
SSRS: 2 data sets in 2 tablix on same page - Hi, I want to show 2 tablix on same page one after another. but second tablix goes and seats as report footer (at the end of last page) instead of after first tablix ends. more like a page footer. I tried to move my 2nd tablix in 1st one but it is complaining as datasets […]
SSRS 2017 Subscriptions and SQL Server Agents - We have recently setup a new SSRS 2017 server along with a new SQL Server 2017 instance. Everything works as expected but when I tried to add subscriptions to a report, the subscription will fail to execute. When I review sql server agent log, I get the following message The job failed. The owner (domain\ssrs_service_account) […]
 

 

RSS FeedTwitter

This email has been sent to {email}. To be removed from this list, please click here. If you have any problems leaving the list, please contact the webmaster@sqlservercentral.com. This newsletter was sent to you because you signed up at SQLServerCentral.com.
©2019 Redgate Software Ltd, Newnham House, Cambridge Business Park, Cambridge, CB4 0WZ, United Kingdom. All rights reserved.
webmaster@sqlservercentral.com

 

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