I have a relatively small table (for now). It works as a fancy queue. Jobs that execute every /second/, ask this table for more work and whenever work completes, they tell the table that work is completed.
Table has ~1000 entries or so entries and long-term will hopefully have 100k+ rows
Each entry signifies a job that needs to be executed once per minute. Table is hosted in SQL Azure (S2 plan)
Job Starter executes a stored proc that requests work from this table. Basically, the proc looks at the table, sees which tasks are not in progress and are overdue, marks them as "in progress" and returns them to job starter.
When task completes, a simple update is executed to tell that task completed and will be available for another cycle of work in a minute (field called Frequency controls this)
PROBLEM: I get deadlocks quiet frequently when asking this table for more work, or trying to mark entries as completed. Looks like ROWLOCK hint is not working. Do I need an indexing structure on this table?
Here is a Stored Procedure that retrieves records (usually up to 20 at a time, governed by #count parameter
CREATE PROCEDURE [dbo].[sp_GetScheduledItems]
#activity NVARCHAR (50), #count INT, #timeout INT=300, #dataCenter NVARCHAR (50)
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DECLARE #batchId uniqueidentifier
SELECT #batchId = NEWID()
DECLARE #result int;
DECLARE #process nvarchar(255);
BEGIN TRAN
-- Update rows
UPDATE Schedule
WITH (ROWLOCK)
SET
LastBatchId = #batchId,
LastStartedProcessingId = NEWID(),
LastStartedProcessingTime = GETUTCDATE()
WHERE
ActivityType = #activity AND
IsEnabled = 1 AND
ItemId IN (
SELECT TOP (#count) ItemId
FROM Schedule
WHERE
(LastStartedProcessingId = LastCompletedProcessingId OR LastCompletedProcessingId IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > #timeout) AND
IsEnabled = 1 AND ActivityType = #activity AND DataCenter = #dataCenter AND
(LastStartedProcessingTime IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > Frequency)
ORDER BY (DATEDIFF(SECOND, ISNULL(LastStartedProcessingTime, '1/1/2000'), GETUTCDATE()) - Frequency) DESC
)
COMMIT TRAN
-- Return the updated rows
SELECT ItemId, ParentItemId, ItemName, ParentItemName, DataCenter, LastStartedProcessingId, Frequency, LastProcessTime, ActivityType
FROM Schedule
WHERE LastBatchId = #batchId
END
GO
Here is a stored procedure that marks entries as completed (it does so one-at-a-time)
CREATE PROCEDURE [dbo].[sp_CompleteScheduledItem]
#activity NVARCHAR (50), #itemId UNIQUEIDENTIFIER, #processingId UNIQUEIDENTIFIER, #status NVARCHAR (50), #lastProcessTime DATETIME, #dataCenter NVARCHAR (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE Schedule WITH (ROWLOCK)
SET
LastCompletedProcessingId = LastStartedProcessingId,
LastCompletedProcessingTime = GETUTCDATE(),
LastCompletedStatus = #status,
LastProcessTime = #lastProcessTime
WHERE
ItemId = #itemId AND
LastStartedProcessingId = #processingId AND
DataCenter = #dataCenter AND
ActivityType = #activity
END
GO
Here is the table itself
CREATE TABLE [dbo].[Schedule](
[ItemId] [uniqueidentifier] NOT NULL,
[ParentItemId] [uniqueidentifier] NOT NULL,
[ActivityType] [nvarchar](50) NOT NULL,
[Frequency] [int] NOT NULL,
[LastBatchId] [uniqueidentifier] NULL,
[LastStartedProcessingId] [uniqueidentifier] NULL,
[LastStartedProcessingTime] [datetime] NULL,
[LastCompletedProcessingId] [uniqueidentifier] NULL,
[LastCompletedProcessingTime] [datetime] NULL,
[LastCompletedStatus] [nvarchar](50) NULL,
[IsEnabled] [bit] NOT NULL,
[LastProcessTime] [datetime] NULL,
[DataCenter] [nvarchar](50) NOT NULL,
[ItemName] [nvarchar](255) NOT NULL,
[ParentItemName] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Schedule] PRIMARY KEY CLUSTERED
(
[DataCenter] ASC,
[ItemId] ASC,
[ActivityType] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
This is a good question :-) As usual you can do many things but in your case I think we can simplify your query quite a bit. Note that the suggestion below doesn't use SERIALIZABLE isolation level which in your case in all likelihood is causing table level locks to prevent phantom read from occurring (and also makes all write access to your table, well, serialized. You also don't actually need to specify BEGIN & COMMIT TRAN as you are only issuing one statement within your transaction (although it doesn't hurt either in your case). In this example we leverage the fact that we can actually issue your update directly against the sub query (in this case in the shape of a CTE) and we can also remove your last SELECT as we can return the result set directly from the UPDATE statement.
HTH,
-Tobias
SQL Server Team
CREATE PROCEDURE [dbo].[sp_GetScheduledItems]
#activity NVARCHAR (50), #count INT, #timeout INT=300, #dataCenter NVARCHAR (50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #batchId uniqueidentifier
SELECT #batchId = NEWID()
DECLARE #result int;
DECLARE #process nvarchar(255);
-- Update rows
WITH a AS (
SELECT TOP (#count)
*
FROM Schedule
WHERE
(LastStartedProcessingId = LastCompletedProcessingId OR LastCompletedProcessingId IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > #timeout) AND
IsEnabled = 1 AND ActivityType = #activity AND DataCenter = #dataCenter AND
(LastStartedProcessingTime IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > Frequency)
ORDER BY (DATEDIFF(SECOND, ISNULL(LastStartedProcessingTime, '1/1/2000'), GETUTCDATE()) - Frequency) DESC
)
UPDATE a SET
LastBatchId = #batchId,
LastStartedProcessingId = NEWID(),
LastStartedProcessingTime = GETUTCDATE()
OUTPUT INSERTED.ItemId, INSERTED.ParentItemId, INSERTED.ItemName, INSERTED.ParentItemName, INSERTED.DataCenter, INSERTED.LastStartedProcessingId, INSERTED.Frequency, INSERTED.LastProcessTime, INSERTED.ActivityType
END
Related
We have the following stored procedure:
ALTER procedure [dbo].[spMergePPObjectBlobProperty]
(
#values dbo.udtPPObjectBlobProperty readonly
)
as
begin
begin try
declare #updatetime datetime
set #updatetime = GetUTCDate()
merge tblPPObjectBlobProperty as t
using (select * from #values) as s
on t.InsertionId = s.InsertionId and t.PropertyMapNameId = s.PropertyMapNameId
when matched then
update set UpdateTime = #updatetime, [Value] = s.BlobValue, UpdateId = s.UpdateId
when not matched then
insert (PropertyMapNameId, Value, UpdateId, UpdateTime, InsertionId)
values(s.PropertyMapNameId, s.BlobValue, s.UpdateId, #updatetime, s.InsertionId)
option (loop join);
end try
begin catch
declare #errormessage varchar(256)
-- Get the error message
select #errormessage = ERROR_MESSAGE()
-- Raise an error and return
raiserror('Error updating entries in the tblPPObjectBlobProperty Table. %s', 16, 1, #errormessage)
end catch
end
Occasionally, this sp can take a few seconds to run with only several rows to insert. Other times it is extremely fast.
We have a number of these sps and this is the only one that appears to be slow sometimes and it is the only one that inserts into a table with a varbinary(max) column.
The type used here is defined as follows:
CREATE TYPE [dbo].[udtPPObjectBlobProperty] AS TABLE(
[InsertionId] [bigint] NOT NULL,
[PropertyMapNameId] [int] NOT NULL,
[BlobValue] [varbinary](max) NULL,
[UpdateId] [bigint] NULL,
PRIMARY KEY CLUSTERED
(
[InsertionId] ASC,
[PropertyMapNameId] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO
Can this stored procedure be optimised in any way?
I have a table that looks like this :
CREATE TABLE [dbo].[akut_prioritering]
(
[behandling_id] [int] NOT NULL,
[akutstatus] [int] NOT NULL,
[nasta_dag] [bit] NOT NULL,
[sort_order] [bigint] NOT NULL,
[rowversion] [timestamp] NOT NULL,
CONSTRAINT [XPKakut_prioritering]
PRIMARY KEY CLUSTERED ([behandling_id] ASC)
) ON [PRIMARY]
And then I have this stored procedure that tries to update rows in this table :
ALTER PROCEDURE [dbo].[akutlistaSave]
#behandlingSortOrder dbo.akutlista_sortorder_tabletype READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #behandlingId INT;
DECLARE #sortOrder BIGINT;
DECLARE #rowversion ROWVERSION;
DECLARE sortOrderCursor CURSOR LOCAL SCROLL STATIC FOR
SELECT behandling_id, sort_order FROM #behandlingSortOrder
OPEN sortOrderCursor
BEGIN TRAN
FETCH NEXT FROM sortOrderCursor INTO #behandlingId, #sortOrder, #rowversion
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT *
FROM akut_prioritering ap
WHERE ap.behandling_id = #behandlingId
AND ap.rowversion = #rowversion)
BEGIN
UPDATE akut_prioritering
SET sort_order = #sortOrder
WHERE behandling_id = #behandlingId;
END
ELSE
BEGIN
RAISERROR ('Rowversion not correct.', 16, 1);
END
FETCH NEXT FROM sortOrderCursor INTO #behandlingId, #sortOrder, #rowversion
END
CLOSE sortOrderCursor
SELECT
ap.behandling_id, ap.rowversion
FROM
akut_prioritering ap
INNER JOIN
#behandlingSortOrder bso ON ap.behandling_id = bso.behandling_id;
DEALLOCATE sortOrderCursor
END
The inparameter type looks like this :
CREATE TYPE [dbo].[akutlista_sortorder_tabletype] AS TABLE
(
[behandling_id] [int] NULL,
[sort_order] [bigint] NULL,
[rowversion] [timestamp] NULL
)
When running this I get a SqlException :
Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.
From what I understand the rowversion column should be updated with a new value automatically, there is no reason in my case to set it manual.
You can't set the rowversion value in dbo.akutlista_sortorder_tabletype because it is not updateable: it is auto generated
However, rowversion (a.k.a deprecated timestamp) is simply a (var)binary(8) with some special rules. You can define and set a (var)binary(8) in dbo.akutlista_sortorder_tabletype and compare on that in the UPDATE
From the first link
A nonnullable rowversion column is semantically equivalent to a binary(8) column. A nullable rowversion column is semantically equivalent to a varbinary(8) column.
It looks like you are trying to insert a timestamp value in a custom table type and then passing that to your stored procedure. As your error suggests, you cannot insert explicit timestamp values into timestamp columns.
You will need to find a different way of passing you table values to this stored procedure to work.
I have 5 meta tables that have the same format but depend on 5 other tables. Each Meta table looks like this:
CREATE TABLE [dbo].[SiteMetas]
(
[SiteMetaId] [bigint] IDENTITY(1,1) NOT NULL,
[SiteId] [bigint] NOT NULL,
FOREIGN KEY([SiteId]) REFERENCES [dbo].[Sites] ([SiteId]),
[MetaGroup] [nvarchar] (64) NOT NULL,
[MetaName] [nvarchar] (128) NOT NULL,
[MetaType] [char] NOT NULL DEFAULT 0, -- t, i, r, d, s, b
[MetaBool] [bit] DEFAULT NULL, -- t
[MetaInteger] [bigint] DEFAULT NULL, -- i
[MetaReal] [real] DEFAULT NULL, -- r
[MetaDateTime] [datetime] DEFAULT NULL, -- d
[MetaString] [nvarchar] (MAX) DEFAULT NULL, -- s
[MetaBinary] [varbinary] (MAX) DEFAULT NULL, -- b
[MetaCreated] [datetime] NOT NULL DEFAULT (GETUTCDATE()),
[MetaExpires] [datetime] DEFAULT NULL,
[MetaUpdated] [datetime] DEFAULT NULL,
PRIMARY KEY CLUSTERED ([SiteMetaId] ASC) WITH (IGNORE_DUP_KEY = ON),
UNIQUE NONCLUSTERED ([SiteId] ASC, [MetaGroup] ASC, [MetaName] ASC) WITH (IGNORE_DUP_KEY = ON)
);
This is for Site but there's 4 more. Like Users, ...
And I want to read the Binary meta value from Site. So wrote this stored procedure:
CREATE PROCEDURE [dbo].[GetSiteMetaBinary]
#SiteId AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128)
AS
BEGIN
SELECT TOP 1 [MetaBinary]
FROM [dbo].[SiteMetas]
WHERE [SiteId] = #SiteId
AND [MetaGroup] = #Group
AND [MetaName] = #Name
AND [MetaType] = 'b';
END;
This stored procedure has duplicates for User too... and the rest of the tables. That just replaces Site with User in its body.
But thinking that I have too many of these I wrote this one:
CREATE PROCEDURE [dbo].[GetMeta]
#Set AS nvarchar(64),
#Id AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128),
#Type AS nvarchar(16)
AS
BEGIN
DECLARE #Flag nchar(1);
DECLARE #Sql nvarchar(MAX);
SET #Flag = CASE #Type
WHEN 'Bool' THEN 't'
WHEN 'Integer' THEN 'i'
WHEN 'Real' THEN 'r'
WHEN 'DateTime' THEN 'd'
WHEN 'String' THEN 's'
WHEN 'Binary' THEN 'b'
ELSE NULL
END;
SET #Sql = N'SELECT TOP 1 [Meta' + #Type + N'] FROM [dbo].[' + #Set + N'Metas]' +
N'WHERE [' + #Set + N'Id] = #Id AND [MetaGroup] = #Group AND [MetaName] = #Name AND [MetaType] = #Flag;';
-- SELECT #Sql; -- DEBUG
EXEC sp_executesql #Sql,
N' #Id AS bigint, #Group AS nvarchar(64), #Name AS nvarchar(128), #Flag AS nchar(1)',
#Id, #Group, #Name, #Flag
;
END;
which is a general use stored procedure to read any data typed stored in a column based on input arguments. I use it like this [dbo].[GetMeta] 'Site', 1, 'group', 'name', 'Binary' the difference being that the actual query is dynamically generated so it's not known before hand by SQL Server like the first specialized variant.
Which of the two choices is better from a performance point of view and friendlier to SQL Server's internals? A dedicated one for each table and column data type of a general one that internally builds a query based on fed arguments.
I can use either. I like the last as it does not pollute my stored procedure space. :) The first one is more clear and SQL Server might be able to optimize it better. Not sure...
PS: I'm quite new to SQL Server
Static procedures are usually faster because the SQL engine can cache the compiled SP's execution plan.
However, unless this SP will be called a lot or is time-critical, it probably isn't worth worrying about it because the time savings of only having to maintain one SP will make up for the very small amount of time difference spent waiting for the SP to finish.
I have a question with regards to performance currently I have a table that is having trouble with query performance whenever the table rows in already millions of record.
This is the table:
CREATE TABLE [dbo].[HistorySampleValues]
(
[HistoryParameterID] [int] NOT NULL,
[SourceTimeStamp] [datetime2](7) NOT NULL,
[ArchiveTimestamp] [datetime2](7) NOT NULL CONSTRAINT [DF__HistorySa__Archi__2A164134] DEFAULT (getutcdate()),
[ValueStatus] [int] NOT NULL,
[ArchiveStatus] [int] NOT NULL,
[IntegerValue] [bigint] SPARSE NULL,
[DoubleValue] [float] SPARSE NULL,
[StringValue] [varchar](100) SPARSE NULL,
[EnumNamedSetName] [varchar](100) SPARSE NULL,
[EnumNumericValue] [int] SPARSE NULL,
[EnumTextualValue] [varchar](256) SPARSE NULL
) ON [PRIMARY]
CREATE CLUSTERED INDEX [Source_HistParameterID_Index] ON [dbo].[HistorySampleValues]
(
[HistoryParameterID] ASC,
[SourceTimeStamp] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
It's fairly flat with a clustered index on HistoryParameterID and SourceTimeStamp.
This is the stored procedure that I'm using
SET NOCOUNT ON;
DECLARE #SqlCommand NVARCHAR(MAX)
SET #SqlCommand = 'SELECT HistoryParameterID,
SourceTimestamp, ArchiveTimestamp,ValueStatus,ArchiveStatus,
IntegerValue,DoubleValue,StringValue,EnumNumericValue,
EnumTextualValue,EnumNamedSetName
FROM [HistorySampleValues] WITH(NOLOCK)
WHERE ([HistoryParameterID] =' + #ParamIds + '
AND
[SourceTimeStamp] >= ''' + CONVERT(VARCHAR(30),#StartTime, 25) + '''
AND
[SourceTimeStamp] <= ''' + CONVERT(VARCHAR(30),#EndTime, 25) + ''')
AND ValueStatus = ' + #ValueStatus
EXECUTE( #SqlCommand )
As you can see the HistoryParameterID and SourceTimestamp are being used as the parameters for the first query. And retrieving 8hrs worth of records which is ~28k records, it returns with an erratic performance, 1.8seconds - 700ms
Will the design scale? whenever it reaches 77 billion records? or is there any strategy to be used? the version of SQL Server is Standard Edition so there is no partitioning, columnstore to be used. Or have I reached the maximum performance of SQL Server Standard Edition?
this is the updated stored proc
#ParamIds int,
#StartTime datetime,
#EndTime datetime,
#ValueStatus int
AS
BEGIN
SET NOCOUNT ON;
SELECT HistoryParameterID,
SourceTimestamp, ArchiveTimestamp,ValueStatus,ArchiveStatus,
IntegerValue,DoubleValue,StringValue,EnumNumericValue,
EnumTextualValue,EnumNamedSetName
FROM [HistorySampleValues] WITH(NOLOCK)
WHERE
HistoryParameterID = #ParamIds
AND (SourceTimeStamp >= #StartTime AND SourceTimeStamp <=#EndTime)
AND (#ValueStatus = -1 OR ValueStatus = #ValueStatus)
I got a 1.396 second client processing time in retrieving 41213 rows to a ~849600000 rows in the table.
is there a way to improve this?
Everytime you execute a new SQL command, it has to be compiled by the MS SQL Server. If you re-use the command, you save on compilation time. You need to directly execute the command in the stored procedure something like this, which should allow compilation and give you more consistent results.
SELECT ...
WHERE ([HistoryParameterID] = #ParamIds
AND [SourceTimeStamp] >= #StartTime
AND [SourceTimeStamp] <= #EndTime
AND ValueStatus = #ValueStatus
This will give you also an opportunity to monitor the performance of the command.
I have two table Old_Table and New_Table. My old table has 13 million records in it currently and I need to process the data in old table and dump it into new. Data will be continuously inserted into my old table. So, I wrote a stored procedure and created a sql job to run nightly and process the data that is inserted into the old table for that day.
My procedure is taking hell lot of a time. Like, my procedure need to process 13 million records per day and the sql job is running never endingly since it started. How can I optimise my below procedure to make it faster
select #From = Max(InsertTime) From [New_Table];
set #To = GETDATE();
declare #ID as int;
set #ID = 0;
SET #ID = (SELECT MIN(Id) FROM Old_Table where TimeStamp > #From and TimeStamp < #To)
WHILE #ID IS NOT NULL
BEGIN
--Get the row data
SELECT #col1 = COLUMN1,
#col2 = case when CHARINDEX('?', COLUMN2) > 0
THEN SUBSTRING(COLUMN2, 1,CHARINDEX('?', COLUMN2)-1)
else COLUMN2
END,
#col3 = [dbo].[ModifyString] (COLUMN3)
from Old_Table with(nolock)
where Id = #ID;
--Few if conditions
select #rowID = ID from [dbo].[New_Table] with(nolock)
where [COL1] = #col1 and [COL2] = #col2 and [COL3] = #col3
--If exists update the row else insert as new row
If #rowID > 0
Begin
-- Update my New_Table
End
Else
Begin
-- Insert into my new table
End
--delete from Old_Table
delete from [dbo].[Old_Table] where id = #ID
--Fetch next record
SET #ID = (SELECT MIN(Id) FROM Old_Table where TimeStamp > #From and TimeStamp < #To);
END
My function [dbo].[ModifyString] has 5 IF conditions which uses CHARINDEX and STUFF functions with in it.
Adding Table defenitions:
[New_Table](
[ID] [int] IDENTITY(1,1) NOT NULL, --Primary Key
[COL1] [varchar](max) NULL,
[COL2] [varchar](max) NULL,
[COL3] [varchar](max) NULL,
[Count] [int] NULL,
[TimeImported] [datetime] NULL, -- Non-Clustered Index
[COL5_NEW] [bit] NULL,
[COL6_NEW] [bit] NULL,
[COL7_NEW] [bit] NULL,
[Old_Table](
[id] [int] IDENTITY(1,1) NOT NULL, -- Primary Key
[TimeStamp] [datetime] NOT NULL, -- Non-Clustered Index
[COL1] [varchar](max) NULL,
[COL2] [varchar](max) NULL,
[COL3] [varchar](max) NULL,
Edit: My old table has duplicate records and [Count] in the new table needs to be incremented for each duplicate record in the old table.
Thanks.
Convert the query to set-based operations. I see nothing preventing that from working. Iteration-based code is not recommended in T-SQL for performance and maintainability reasons.
You can either use the MERGE statement to execute all changes at once, or run one insert, update and delete statement.
I see no reason why you cannot update all 13 million records in one (or three) statements. This will realize huge efficiency gains (orders of magnitude).
Loops are no bueno in SQL. Avoid them if you can... This psudocode should get you started down the right road:
--Insert new
INSERT INTO NewTable
SELECT * FROM OldTable
EXCEPT
SELECT * FROM NewTable
--Update existing
UPDATE NewTable as nt
INNER JOIN OldTable as ot
ON nt.id = ot.id
SET nt.value = ot.value
Most modern DBMSs are designed to handle set based operations so if you see a loop you can expect performance problems.
As well as the RBAR others have mentioned (which needs addressing), you're also searching New_Table for a match against 3 varchar(MAX) fields, which aren't indexable.
I'd suggest adding OldTableID as an indexed int column to New_Table, then amending the script to use it as the link (populating it as part of the MERGE or INSERT used) instead of the three varchar columns (as well as sorting the RBAR). An index on Old_Table.TimeStamp would also help.
This is a template for the insert
insert into New_Table
SELECT Old_Table.ID, Old_Table.COLUMN1,
case when CHARINDEX('?', Old_Table.COLUMN2) > 0
THEN SUBSTRING(Old_Table.COLUMN2, 1,CHARINDEX('?', COLUMN2)-1)
else Old_Table.COLUMN2
END,
Old_Table.[ModifyString] (COLUMN3)
from Old_Table
left join New_Table
on Old_Table.[COL1] = New_Table.[COL1]
and Old_Table.[COL2] = New_Table.[COL2]
and Old_Table.[COL3] = New_Table.[COL3]
where Old_Table,TimeStamp > #From and Old_Table.TimeStamp < #To
and New_Table.ID is null
See this for combining the delete and insert
enter link description here
Then the update is a join (no left) and no New_Table.ID is null