SQL Server update via "WITH" statement and join - sql

I would like to be able to update a table at once, instead of multiple statements and I don't want to make a temporary table.
To test this, I made this table:
USE [SomeSchema]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [SomeSchema].[TestTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TextField] [varchar](250) NULL,
[updateField] [varchar](20) NULL,
CONSTRAINT [Pk_TestTable_Id] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [Idx_TestTable] UNIQUE NONCLUSTERED
(
[TextField] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I tried to combine these two answers (https://stackoverflow.com/a/57965771 and https://stackoverflow.com/a/32431922/369122) into a working statement.
My last try:
WITH NewData AS
(
SELECT * FROM ( VALUES ('abc', 'A'),('def','d'),('ghi','g'))
x
(TextField, updateField)
)
update [SomeSchema].[TestTable]
set _a.updateField= _b.updateField
from
[SomeSchema].[TestTable] _a,
NewData _b
where _a.TextField=_b.TextField
Gave this error: Msg 4104, Level 16, State 1, Line 22
The multi-part identifier "_a.updateField" could not be bound.
Any suggestions? For the record; this is just a test. In practice I need to be able to join multiple columns to update one or more columns.
thanks,
Matthijs
#larnu's answer did the job:
"As for the problem, replace update [SomeSchema].[TestTable] with update _a. You're referencing a table in your FROM as the column to UPDATE, but the table your updating is defined as a different instance of [TestTable]"
WITH NewData AS
(
SELECT * FROM ( VALUES ('abc', 'a'),('def','d'),('ghi','g'))
x
(TextField, updateField)
)
update _tt
set _tt.updateField= _nd.updateField
from
[SomeSchema].[TestTable] _tt
left join
NewData _nd
on _tt.TextField=_nd.TextField

Related

Issue with re-create index/constraint after dropping it

I am using SQL Server 2008 R2.
I encountered this issue when re-create index.
As I need to alter column, so I drop constraint/index first and create back my constraint/index.
However, it shows error message saying
The operation failed because an index or statistics with name 'ABC' already exists on table 'test_table'
I wonder why would this error message shown? Since I have drop constraint
I wrote this to drop index
DROP INDEX [ABC] ON [dbo].[test_table] WITH ( ONLINE = OFF )
I then re-create index
CREATE NONCLUSTERED INDEX [ABC] ON [dbo].[test_table]
(
[col_1] ASC,
[col_2] ASC,
[col_3] ASC
)
INCLUDE ( [col_4],
[col_5]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Anyone has any idea what's wrong here?

Why is my filtered index not being used for one statement?

I have the following table (an excerpt follows w/o other columns):
USE [opg-systems-dev]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Orders]
(
[SyncChannelEngineOrder] [bit] NOT NULL,
[IsSyncing] [bit] NOT NULL,
CONSTRAINT [PK_Orders]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Orders]
ADD DEFAULT (CONVERT([bit], (0))) FOR [IsSyncing]
GO
-- -----
CREATE NONCLUSTERED INDEX [IX_Orders_SyncChannelEngineOrder]
ON [dbo].[Orders] ([SyncChannelEngineOrder] ASC)
WHERE ([SyncChannelEngineOrder] <> (0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_Orders_IsSyncing]
ON [dbo].[Orders] ([IsSyncing] ASC)
WHERE ([IsSyncing] <> (0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
I'm trying to use a filtered index here - but it seems when I'm querying that the the index for SyncChannelEngineOrder is not being used for some reason.
The setup is supposed to be the same for both columns.
The query is requesting all columns with a select *. The indexes only contain one column (ignoring the clustered key). To provide the missing column data that is not on the index, the query optimizer is calculating a lower cost to just scan all of the rows in the source table.
If SQL Server chose to use the existing indexes, it would first scan those rows, and then it would perform a bookmark lookup to get the rest of the columns from the table. If there are very few rows in the filtered index, and the table is very large, the SQL Server query optimizer could theoretically calculate a low enough cost to use the filtered index to limit the number of rows. Otherwise, bookmark lookups are expensive on a large number of rows. I am guessing the number of rows in the index is over this threshold.
SQL Server will add the clustered key to a non-clustered index to facilitate the bookmark lookup. If the query were to request only the clustered key and the index filter columns, the indexes would cover the query, and I expect the query optimizer would choose to use the filtered indexes.

Deadlock at update query only when more than two transaction at same time

When writing some SQL Server update queries for interfacing a product database, I ran into a situation where everything is fine if two transactions with these updates are running at the same time - but when more than two transactions are trying to update the same table simultaneously, some of them were deadlocked.
I broke the problem down to a "UNIQUE NONCLUSTERED" constraint to the table definition. When I remove this constraint, all transactions will wait for their needed resources and finish without errors.
Here is my sample-code to reproduce the problem:
CREATE TABLE [dbo].[profiles]
(
[ProfileID] [int] IDENTITY(1,1) NOT NULL,
[ProfileName] [nvarchar](255) NOT NULL,
[GroupFK] [int] NULL
CONSTRAINT [PK_Profile]
PRIMARY KEY CLUSTERED ([ProfileID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY],
-- !!! [UI_UniqueNameInGroup] seems problematic for me because i get deadlocks when executing more than two transactions at once !!!
CONSTRAINT [UI_UniqueNameInGroup]
UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_99')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_88')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_77')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_66')
These are samples of my transactions:
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_99'
WHERE ProfileID = 4
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_66'
WHERE ProfileID = 1
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_88'
WHERE ProfileID = 3
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_77'
WHERE ProfileID = 2
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
Here are my deadlock-graphs:
deadlockgraph1.xdl
deadlockgraph2.xdl
Please help me by explaining the problem to me - I don't get it why deadlocks only happen when executing more than two transactions simultaneously.
I'm also cool with just getting a working solution for this.
Is there a solution at my side (without changing the database scheme)?
Change constraint [UI_UniqueNameInGroup] to allow only row locks:
CONSTRAINT [UI_UniqueNameInGroup] UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = OFF
) ON [PRIMARY]
This causes a problem because if you deny page and row lock then you force SQL Server to use table lock but other session already put a table lock.

Changing cte for microsoft access

I've tried searching for an answer to this but can't find one.
I have a CTE I use for SQL queries relating to 2 data tables in a database. The primary key of one table is a foreign key in the other and can appear numerous times in the 2nd table. I want to do a count of the number of times each foreign key appears in the second table, and list this as a total field in my search results along with details from the first table. As CTEs don't work in Access I've adjusted this to use a sub select in the join, but it still doesn't like it in access.
Here are the basic parts of the tables
CREATE TABLE [dbo].[Clients](
[ClientRef] [int] NOT NULL,
[Surname] [varchar](40) NULL,
[Forenames] [varchar](50) NULL,
[Title] [varchar](40) NULL,
CONSTRAINT [CLIE_ClientRef_PK] PRIMARY KEY CLUSTERED
(
[ClientRef] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Policies](
[PolicyRef] [int] NOT NULL,
[ClientRef] [int] NULL,
CONSTRAINT [POLI_PolicyRef_PK] PRIMARY KEY CLUSTERED
(
[PolicyRef] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Here's my CTE
WITH CliPol (ClientRef, Plans) AS (SELECT ClientRef, COUNT(ClientRef) AS Plans FROM Policies GROUP BY ClientRef)
SELECT Clients.Surname, Clients.Forenames, Clients.Title, CliPol.Plans AS [No. of plans]
FROM Clients LEFT JOIN CliPol ON Clients.ClientRef = CliPol.ClientRef
ORDER BY Surname, Forenames;
And here's my adjusted query.
SELECT Clients.ClientRef, Clients.Surname, Clients.Forenames, Clients.Title , Plans.NoPlans
FROM Clients
LEFT JOIN
(SELECT ClientRef, COUNT(ClientRef) AS NoPlans FROM Policies GROUP BY ClientRef)
AS Plans ON Plans.ClientRef = Clients.ClientRef
ORDER BY Clients.Surname, Clients.Forenames
Unfortunately Access throws error #3131, "Syntax error in FROM clause", when I try to run that query.
Does anybody know how I make this work in Access?
One alternate approach would be to use the DCount() domain aggregate function
SELECT
Clients.ClientRef,
Clients.Surname,
Clients.Forenames,
Clients.Title ,
DCount("ClientRef", "Plans", "ClientRef=" & Clients.ClientRef) AS NoPlans
FROM Clients
ORDER BY Clients.Surname, Clients.Forenames

SQL Conditional Unique Constraint With Where Clause Within Same Table

I have a table where I want to ensure that a combination of five columns remain unique within that table. For example:
ALTER TABLE [dbo].[MyTable]
ADD CONSTRAINT [UQ__MyTable.MFG.Model.Class.Depiction.Iteration]
UNIQUE NONCLUSTERED
(
[ManufacturerID] ASC,
[Model] ASC,
[BlockClassID] ASC,
[BlockDepictionID] ASC,
[BlockIterationID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
GO
I want to exclude combinations where a sixth separate column has a particular value. For example, I only want to enforce this above constraint when the column [Flag] = 0 and exclude enforcement when the column [Flag] = 1 .
As workaround, You can get the proper ANSI behavior in SQL Server 2008 and above by creating a unique, filtered index.
CREATE UNIQUE NONCLUSTERED INDEX [IX__MyTable.MFG.Model.Class.Depiction.Iteration]
ON [dbo].[MyTable] ([ManufacturerID],[Model],[BlockClassID],[BlockDepictionID],[BlockIterationID])
WHERE [Flag] = 0;
TechNet article