Когда используются пространства имен, они также должны использоваться в запросе XPath. Ваш запрос XPath будет работать только с элементами без пространства имен (как это можно проверить, удалив пространство имен из вашего XML).
Вот пример, показывающий, как вы создаете и передаете менеджер пространства имен:
var xml = ... XML from your post ...;
var xmlReader = XmlReader.Create( new StringReader(xml) ); // Or whatever your source is, of course.
var myXDocument = XDocument.Load( xmlReader );
var namespaceManager = new XmlNamespaceManager( xmlReader.NameTable ); // We now have a namespace manager that knows of the namespaces used in your document.
namespaceManager.AddNamespace( "prefix", "http://www.MyNamespace.ca/MyPath" ); // We add an explicit prefix mapping for our query.
var result = myXDocument.XPathSelectElement(
"//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]",
namespaceManager
); // We use that prefix against the elements in the query.
Console.WriteLine(result); // <CommandRef ...> element is printed.
Надеюсь, это поможет.
INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
FROM <table>
WHERE NOT EXISTS
-- race condition risk here?
( SELECT 1 FROM <table> WHERE <natural keys> )
UPDATE ...
WHERE <natural keys>
Что касается второго условия гонки, можно утверждать, что ключ все равно был бы удален параллельным потоком, так что на самом деле это не потерянное обновление.
Оптимальное решение обычно состоит в том, чтобы попробовать наиболее вероятный случай и обработать ошибку в случае сбоя (конечно, внутри транзакции):
Помимо корректности, этот шаблон также оптимален с точки зрения скорости: более эффективно пытаться вставить и обработать исключение, чем создавать ложные блокировки. Блокировки означают чтение логической страницы (что может означать чтение физической страницы), а ввод-вывод (даже логический) дороже, чем SEH.
Обновление @Peter
Почему ни один оператор не является «атомарным»? Допустим, у нас есть тривиальная таблица:
create table Test (id int primary key);
Теперь, если бы я запустил этот единственный оператор из двух потоков в цикле, он был бы «атомарным», как вы говорите, условие отсутствия гонки может существовать:
insert into Test (id)
select top (1) id
from Numbers n
where not exists (select id from Test where id = n.id);
Тем не менее всего за пару секунд происходит нарушение первичного ключа:
Сообщение 2627, уровень 14, состояние 1, строка 4
Нарушение ограничения PRIMARY KEY 'PK__Test__24927208'. Невозможно вставить повторяющийся ключ в объект 'dbo.Test'.
Почему? Вы правы в том, что план запроса SQL сделает «правильную вещь» на DELETE ... FROM ... JOIN
, на WITH cte AS (SELECT ... FROM) DELETE FROM cte
и во многих других случаях.Но в этих случаях есть принципиальное различие: «подзапрос» относится к цели операции update или delete . Для таких случаев план запроса действительно будет использовать соответствующую блокировку, на самом деле такое поведение критично в определенных случаях, например, при реализации очередей Использование таблиц как очередей .
Но в исходном вопросе, а также в моем примере, подзапрос рассматривается оптимизатором запросов как подзапрос в запросе, а не как некий специальный запрос типа «сканирование для обновления», который требует специальной защиты от блокировки. В результате параллельный наблюдатель может наблюдать выполнение подзапроса как отдельную операцию, что нарушает «атомарное» поведение оператора. Если не будут приняты особые меры предосторожности, несколько потоков могут попытаться вставить одно и то же значение, оба убеждены, что они проверили, и значение еще не существует. Только один может добиться успеха, другой попадет в нарушение ПК. QED.
РЕДАКТИРОВАТЬ : Ремус верен, условная вставка с предложением где не гарантирует согласованного состояние между коррелированным подзапросом и вставкой таблицы.
Возможно, правильные подсказки таблицы могут привести к согласованному состоянию. В тривиальном тесте, подобном описанному Ремусом, ОРИГИНАЛЬНЫЙ ОТВЕТ, НЕПРАВИЛЬНЫЙ: Это атомарно? Да, условная вставка с предложением where является атомарной, а ваш Я бы добавил Отдельные операторы всегда выполняются внутри транзакции, либо их собственные ( autocommit и неявный ) или вместе с другими операторами ( явный ). У меня есть один трюк замечено - это попытаться выполнить INSERT и, если это не удастся, выполнить UPDATE. Вы можете использовать блокировки приложений: (sp_getapplock)
http://msdn.microsoft.com/en-us/library/ms189823.aspx Передавать подсказки обновления, блокировки строк и блокировки при проверке существования строки. Holdlock обеспечивает сериализацию всех вставок; rowlock разрешает одновременное обновление существующих строк. Обновления могут по-прежнему блокироваться, если ваш ПК является bigint, поскольку внутреннее хеширование является вырожденным для 64-битных значений. INSERT
WITH (TABLOCKX, HOLDLOCK) вроде работает, но я понятия не имею, является ли это оптимальным уровнем блокировки для условной вставки.
TABLOCKX, HOLDLOCK
показали ~ 5-кратный объем вставки без табличных подсказок и без ошибок PK или курса.
INSERT ... WHERE NOT EXISTS () ... UPDATE
- это правильный способ выполнить UPSERT. IF @@ ROWCOUNT = 0
между INSERT и UPDATE: INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
WHERE NOT EXISTS
-- no race condition here
( SELECT 1 FROM <table> WHERE <natural keys> )
IF @@ROWCOUNT = 0 BEGIN
UPDATE ...
WHERE <natural keys>
END
begin tran -- default read committed isolation level is fine
if not exists (select * from <table> with (updlock, rowlock, holdlock) where <PK = ...>
-- insert
else
-- update
commit
Похожие вопросы: