Insert data into remote database table from local database table - sql

I have a problem, I use SQL Server 2014. I need to copy data from a local table to the identical table on a remote server. Right now, I can only insert static data to the remote table, select data from the remote table, but I didn't get to do what I want.
Here's my sql code
IF OBJECT_ID('tempdb..##TempTable') IS NOT NULL
DROP TABLE ##TempTable
CREATE TABLE ##TempTable
(
[Name] [nvarchar](255) NOT NULL,
[Description] [nvarchar](512) NOT NULL,
[ARXUrl] [nvarchar](1000) NOT NULL,
[IsDeleted] [bit] NULL,
[CreatedDate] [datetime] NOT NULL
);
GO
INSERT INTO ##TempTable
SELECT
[Name], [Description], [ARXUrl], [IsDeleted], [CreatedDate]
FROM [dbo].[ARXSystem]
GO
-- destination database
:SETVAR remoteDB [slic-test]
:CONNECT someserver.net\SQLEXPRESS2014 -U user -P password
--source database
USE [SLIC]
GO
SELECT *
FROM ##TempTable;
INSERT INTO $(remoteDB).[dbo].[ARXSystem]
SELECT *
FROM #TempTable
GO
and here is the message I received
(1 row(s) affected)
Connecting to someserver.net\SQLEXPRESS2014 as SLIC...
Msg 208, Level 16, State 0, Line 22
Invalid object name '##TempTable'.
Disconnecting connection from someserver.net\SQLEXPRESS2014 as SLIC...

You need to use remote server via Openquery or Openrowset, which might require a server configuration change on the source system where the query is executed. To push the data from the source to target the query would look something like this:
INSERT INTO OPENROWSET('SQLNCLI',
'Server=someserver.net\SQLEXPRESS2014;User=xxxx;Password=xxxx;',
'SELECT [Name], [Description], [ARXUrl], [IsDeleted], [CreatedDate]
FROM [slic-test].[dbo].[ARXSystem]')
SELECT [Name], [Description], [ARXUrl], [IsDeleted], [CreatedDate]
FROM [dbo].[ARXSystem]
OPENROWSET documentation can be found here: https://msdn.microsoft.com/en-us/library/ms190312.aspx

Your SQL Creates a table, but its not a temporary table. It will create it in the active database. You need to use something like
DECLARE #temptable TABLE
(
[Name] [nvarchar](255) NOT NULL,
[Description] [nvarchar](512) NOT NULL,
[ARXUrl] [nvarchar](1000) NOT NULL,
[IsDeleted] [bit] NULL,
[CreatedDate] [datetime] NOT NULL
)
Then after your switch databases you can refer to that table using
INSERT INTO $(remoteDB).[dbo].[ARXSystem]
SELECT * FROM #temptable
GO
Hopefully this will help

Related

SQL Server - SQL - INSERT VALUE from SELECT Statement

Help would be much appreciated: I want to update values to an audit table (dbo.Audit) prior to updating the same data column.
I have a SELECT statement to retrieve the values (which is created using dynamic SQL) stored in table dbo.[RuleSet], column [SelectStatement].
Issue: I am not sure how to update the Audit table.
CREATE TABLE [dbo].[Audit]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[UpdateID] [int] NULL,
[TableName] [varchar](250) NULL,
[Orig] [varchar](250) NULL
)
CREATE TABLE [dbo].[RuleSet]
(
[UpdateID] [int] IDENTITY(1,1) NOT NULL,
[VID] [int] NULL,
[TableName] [varchar](250) NOT NULL,
[SetColumn] [varchar](250) NOT NULL,
[SetValue] [varchar](250) NOT NULL,
[WhereClause] [varchar](256) NULL,
[SelectStatement] [varchar](4000) NULL
)
INSERT [dbo].[RuleSet] ([UpdateID], [VID], [TableName], [SetColumn], [SetValue], [WhereClause], [SelectStatement])
VALUES (1, 1, N'TableA', N'ColumnA', N'10', N'ColumnA > 10', N'SELECT ColumnA FROM TableA WHERE ColumnA > 10')
INSERT [dbo].[RuleSet] ([UpdateID], [VID], [TableName], [SetColumn], [SetValue], [WhereClause], [SelectStatement])
VALUES (3, 2, N'TableB', N'ColumnB', N'20', N'ColumnB > 20', N'SELECT ColumnB FROM TableB WHERE ColumnB > 20')
GO
The logic of the code I am trying to achieve is:
INSERT INTO [dbo].[Audit]([UpdateID], [TableName], [Orig])
SELECT
[UpdateID], [TableName],
--Value returned from executing the SELECT statement in column[SelectStatement]
FROM
dbo.[RuleSet]
Thank you
You can use EXECUTE sp_executesql to execute [SelectStatement] and store the result in a temp table or a variable. Then use that as a sub query to insert into [dbo].[Audit].
You could make it a lot easier on yourself if you stored your query in [SelectStatement] like this.
N'SELECT ColumnB INTO #TempB FROM TableB WHERE ColumnB > 20'
Then you can just execute it using sp_executesql and select from TempB for the insert.
EXECUTE sp_executesql (SELECT [SelectStatement] FROM [dbo].[RuleSet] where [UpdateID] = ?);
INSERT INTO [dbo].[Audit ] ([UpdateID], [TableName], [Orig])
SELECT [UpdateID], [TableName], #TempB.*
FROM dbo.[RuleSet], #TempB
WHERE [UpdateID] = ?
Note, my example is just a general suggestion and may need tweaking to execute.

How can I debug a SQL Server trigger that is giving error "Conversion failed when converting date and/or time from character string"?

I have a C# app with a SQL Server backend. In the backend I have two tables:
MyTable
MyTableHistory
I just added a trigger to put an entry in MyTableHistory when you do an update on MyTable. I am getting and error when I add this trigger:
Conversion failed when converting date and/or time from character
string
Here is the trigger:
CREATE TRIGGER [TU_MyTable]
ON dbo.[MyTable]
AFTER UPDATE
AS
SET NOCOUNT ON
INSERT INTO dbo.[MyTableHistory]
SELECT *
FROM deleted
GO
Here is my table schema
CREATE TABLE dbo.[MyTable]
(
[Id] int IDENTITY NOT NULL
CONSTRAINT [PK_MyTable] PRIMARY KEY,
[Timestamp] NOT NULL,
[IsDeleted] bit NOT NULL,
[Name] nvarchar(200) NOT NULL,
[LastUpdated] datetime NOT NULL,
[LastUpdatedBy] nvarchar(50) NOT NULL
)
GO
and here is the history table schema
CREATE TABLE dbo.[MyTableHistory]
(
[Id] int NOT NULL,
[Timestamp] binary(8) NOT NULL,
[IsDeleted] bit NOT NULL,
CONSTRAINT [PK_MyTableHistory] PRIMARY KEY ([Id], [Timestamp]),
[LastUpdated] datetime NOT NULL,
[LastUpdatedBy] nvarchar(50) NOT NULL,
[Name] nvarchar(200) NOT NULL
)
GO
Is there anyway to figure out what field is causing this issue and is there anyway to debug inside the database trigger to help me diagnose?
The error is due to conversion of NVARCHAR to DATETIME. In MyTable, the column Name is placed before the LastUpdated column. In short, the order of columns in both tables is not the same. You should specify the columns in your INSERT statement.
INSERT INTO MyTableHistory(
Id,
[Timestamp],
IsDeleted,
Name,
LastUpdated,
LastUpdatedBy
)
SELECT
Id,
[Timestamp],
IsDeleted,
Name,
LastUpdated,
LastUpdatedBy
FROM deleted
Doing an insert without a column list is dangerous. Include the list and don't use *:
Insert into dbo.[MyTableHistory]([Id], [Timestamp], [IsDeleted], [LastUpdated],
[LastUpdatedBy], [Name])
SELECT id, [Timestamp], IsDeleted, LastUpdated, LastUpdatedBy, Name
from deleted;
Do not depend on the ordering of columns in a table -- it causes bugs that are hard to find.

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012
[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED
(
[TableId] ASC
)
I'm also using an audit triggers on that table that are as follows:
trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/**************************************************************
* INSTEAD OF trigger on table [dbo].[Table] responsible
for automatically inserting audit column data
**************************************************************/
as
begin
set nocount on
declare #currentTime datetime2
set #currentTime = GETUTCDATE()
insert into [dbo].[Table]
(
Name,
CreatedBy,
CreatedDate,
ModifiedBy,
ModifiedDate
)
select
Name,
ISNULL(CreatedBy, system_user),
#currentTime,
NULL,
NULL
from inserted
select SCOPE_IDENTITY() as [TableId]
goto EOP -- end of procedure
ErrorHandler:
if (##trancount <> 0) rollback tran
EOP:
end
I used different approaches, but nothing 'SAFE' seems to work.
Using scope identity returns null
insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()
Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:
declare #tmpTable table
(
TableId int,
Name nvarchar (50)
)
INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into #tmpTable
VALUES('foo')
select * from #tmpTable
TableId Name
0 foo
I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:
declare #tmpTable table (id int)
insert #tmpTable (id )
exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'
select id from #tmpTable
I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.
It appears that the following requirements apply to your audit column data:
Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
Always use GETUTCDATE() for CreatedDate.
If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #2.
CREATE TABLE [dbo].[Table]
(
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
[CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
)
GO
CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
FROM [dbo].[Table] AS T
INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
END
GO
Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.
If the INSTEAD OF trigger is essential to your implementation, then SELECT ##IDENTITY is an alternative to SCOPE_IDENTITY.

Data Transfer Between Databases Automatically

I want to transfer data between databases automatically. For example in every one hour, data in one table in first db will be copied into a table in second db.
What do you recommend for me to do ?
By using jobs maybe?
Thanks in advance.
You can probably look at SQL Server Database Mirroring if you are already on SQL Server 2008. Avoids the need for an additional ETL tool.
Create a store procedure and schedule it for one hours:-
Create procedure copydata
AS
INSERT INTO DB1.dbo.TempTable
SELECT * FROM DB2.dbo.TempTable
Have you considered using Integration Services?
You can set up a package doing what you want it to do and then add it to a job with a scheduler.
Here is the "poor man's method".
This logic will you eliminate the need to truncate the destination table every time.
You can schedule a job to call the stored procedure every hour.
OrganizationDB, that is my db name. Yours may be different.
If your source data is on a different database, use that db's name ....
USE [OrganizationDB]
GO
DROP TABLE [dbo].[EmployeeSource]
GO
DROP TABLE [dbo].[EmployeeDestination]
GO
CREATE TABLE [dbo].[EmployeeSource](
[EmployeeUUID] [uniqueidentifier] NOT NULL,
[SSN] [char](9) NOT NULL,
[LastName] [varchar](40) NOT NULL,
[FirstName] [varchar](40) NOT NULL,
[HireDate] [smalldatetime] NOT NULL,
CONSTRAINT [PK_EmployeeSource] PRIMARY KEY NONCLUSTERED
(
[EmployeeUUID] ASC
),
CONSTRAINT [CK_EmployeeSource_SSN_Unique] UNIQUE NONCLUSTERED
(
[SSN] ASC
)
)
GO
ALTER TABLE [dbo].[EmployeeSource] ADD DEFAULT (newsequentialid()) FOR [EmployeeUUID]
GO
ALTER TABLE [dbo].[EmployeeSource] ADD DEFAULT (getdate()) FOR [HireDate]
GO
CREATE TABLE [dbo].[EmployeeDestination](
[EmployeeUUID] [uniqueidentifier] NOT NULL,
[SSN] [char](9) NOT NULL,
[LastName] [varchar](40) NOT NULL,
[FirstName] [varchar](40) NOT NULL,
[HireDate] [smalldatetime] NOT NULL,
CONSTRAINT [PK_EmployeeDestination] PRIMARY KEY NONCLUSTERED
(
[EmployeeUUID] ASC
),
CONSTRAINT [CK_EmployeeDestination_SSN_Unique] UNIQUE NONCLUSTERED
(
[SSN] ASC
)
)
GO
ALTER TABLE [dbo].[EmployeeDestination] ADD DEFAULT (newsequentialid()) FOR [EmployeeUUID]
GO
ALTER TABLE [dbo].[EmployeeDestination] ADD DEFAULT (getdate()) FOR [HireDate]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[uspPoorMansDataCopy]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[uspPoorMansDataCopy]
Go
/*
exec [dbo].[uspPoorMansDataCopy]
*/
CREATE PROCEDURE [dbo].[uspPoorMansDataCopy]
AS
SET NOCOUNT ON
/* USE SURROGATE KEY */
INSERT INTO dbo.EmployeeDestination
(
[EmployeeUUID]
, [SSN]
, [LastName]
, [FirstName]
, [HireDate]
)
Select
[EmployeeUUID]
, [SSN]
, [LastName]
, [FirstName]
, [HireDate]
From
OrganizationDB.dbo.EmployeeSource es
where
not exists (select null from dbo.EmployeeDestination innerDestination where innerDestination.EmployeeUUID = es.EmployeeUUID)
/* OR USE UNIQUE CONSTRAINT */
INSERT INTO dbo.EmployeeDestination
(
[EmployeeUUID]
, [SSN]
, [LastName]
, [FirstName]
, [HireDate]
)
Select
[EmployeeUUID]
, [SSN]
, [LastName]
, [FirstName]
, [HireDate]
From
OrganizationDB.dbo.EmployeeSource es
where
not exists (select null from dbo.EmployeeDestination innerDestination where UPPER(innerDestination.SSN) = upper(es.SSN))
SET NOCOUNT OFF
GO
GRANT EXECUTE ON [dbo].[uspPoorMansDataCopy] TO public
GO
If this is only data from one table and if both databases are on the same server you can also consider creating INSERT trigger on that table that will automatically catch all new data and copy it into secondary database.
I wouldn’t do this though if databases are not on the same server and if servers are not in the same physical location because it might cause performance issues.

SQL Server View with TOP

I have a view that is driving me absolutely crazy..
Table AlarmMsg looks like this:
[TypeID] [smallint] NULL,
[SEFNum] [int] NULL,
[ServerName] [nvarchar](20) NOT NULL,
[DBName] [varchar](20) NOT NULL,
[PointName] [varchar](50) NOT NULL,
[AppName] [varchar](50) NOT NULL,
[Description] [varchar](100) NOT NULL,
[Priority] [tinyint] NOT NULL,
[Value] [float] NOT NULL,
[Limit] [float] NOT NULL,
[Msg] [nvarchar](255) NULL,
[DateStamp] [datetime2](7) NULL,
[UID] [uniqueidentifier] NOT NULL
On top of that AlarmMsg table is a view applied looking like this:
CREATE VIEW AlarmMsgView
AS
SELECT TOP (2000) WITH TIES
SEFNum, ServerName, DBName,
PointName, AppName, Description,
Priority, Value, Limit, Msg,
DateStamp, UID
FROM dbo.AlarmMsg WITH (NOLOCK)
ORDER BY DateStamp DESC
This query straight against the table returns the expected ten (10) rows:
SELECT TOP(10) [SEFNum]
FROM [RTIME_Logs].[dbo].[AlarmMsg]
where [Priority] = 1
The same query against the view returns....nothing (!):
SELECT TOP(10) [SEFNum]
FROM [RTIME_Logs].[dbo].[AlarmMsgView]
where [Priority] = 1
The table AlarmMsg contains some 11M+ rows and has a FT index declared on column Msg.
Can someone please tell me what's going on here, I think I'm losing my wits.
NOLOCK causes this issue.
Read this and this.
Basically, NOLOCK came from SQL Server 2000 era. It needs to be forgotten. You have upgraded your SQL Server (I hope), so you need to upgrade your queries. Consider switching to READ_COMMITTED_SNAPSHOT to read data in "unblocked" manner. Read here to decide which isolation level is best for your situation
EDIT:
After reading the comments from the author, I think this is the reason:
SQL Server is not doing anything wrong. Treat your view as a subquery of the main query, something like this:
SELECT * FROM (SELECT TOP(2000) col1, col2 FROM aTable ORDER BY Date1 DESC) WHERE priority = 1.
In this case the query in the brackets will be executed first, and the WHERE clause will be applied to the resulting set