Я пишу быстрое и грязное приложение для загрузки данных плана продаж в SQL Server (FWIW 2008, хотя я не думаю, что конкретная версия имеет значение).
Набор данных представляет собой корпоративный план продаж: несколько тысяч строк единиц, долларов и цены для каждой комбинации клиента, номера детали и месяца. Эти данные обновляются каждые несколько недель, и важно отслеживать, кто их изменил и какие изменения были.
-- Metadata columns are suffixed with ' ##', to enable an automated
-- tool I wrote to handle repetitive tasks such as de-duplication of
-- records whose values didn't change in successive versions of the
-- forecast.
CREATE TABLE [SlsPlan].[PlanDetail]
(
[CustID] [char](15) NOT NULL,
[InvtID] [char](30) NOT NULL,
[FiscalYear] [int] NOT NULL,
[FiscalMonth] [int] NOT NULL,
[Version Number ##] [int] IDENTITY(1,1) NOT NULL,
[Units] [decimal](18, 6) NULL,
[Unit Price] [decimal](18, 6) NULL,
[Dollars] [decimal](18, 6) NULL,
[Batch GUID ##] [uniqueidentifier] NOT NULL,
[Record GUID ##] [uniqueidentifier] NOT NULL DEFAULT (NEWSEQUENTIALID()),
[Time Created ##] [datetime] NOT NULL,
[User ID ##] [varchar](64) NULL DEFAULT (ORIGINAL_LOGIN()),
CONSTRAINT [PlanByProduct_PK] PRIMARY KEY CLUSTERED
([CustID], [InvtID], [FiscalYear], [FiscalMonth], [Version Number ##])
)
Чтобы отслеживать изменения, я использую столбец IDENTITY как часть первичного ключа, чтобы включить несколько версий с одним и тем же первичным ключом. Чтобы отследить, кто внес изменение, а также чтобы разрешить откат всего плохого обновления, если кто-то делает что-то совершенно глупое, я вставляю в Active Directory вход в систему создателя этой версии записи, отметку времени и два идентификатора GUID.
Столбец «Пакетный GUID» должен быть одинаковым для всех записей в пакете; столбец «GUID записи», очевидно, уникален для этой конкретной записи и используется только для дедупликации, а не для какого-либо запроса.
Я бы предпочел сгенерировать пакетный GUID внутри запроса, а не писать хранимую процедуру, которая делает очевидное:
DECLARE @BatchGUID UNIQUEIDENTIFIER = NEWID()
INSERT INTO MyTable
SELECT I.*, @BatchGUID
FROM InputTable I
Я решил, что простой способ сделать это - создать однострочный результат с отметкой времени, идентификатором пользователя и вызовом NEWID () для создания пакетного GUID. Затем выполните CROSS JOIN, чтобы добавить эту строку к каждой из вставляемых строк. Я попытался сделать это несколькими разными способами, и оказалось, что механизм выполнения запросов по существу выполняет GETDATE () один раз, потому что одна отметка времени появляется во всех строках (даже для теста с 5 миллионами строк). Тем не менее, я получаю разные GUID для каждой строки в наборе результатов.
Приведенные ниже примеры просто фокусируются на запросе и опускают логику вставки вокруг них.
WITH MySingleRow AS
(
Select NewID() as [Batch GUID ##],
ORIGINAL_LOGIN() as [User ID ##],
getdate() as [Time Created ##]
)
SELECT N.*, R1.*
FROM util.zzIntegers N
CROSS JOIN MySingleRow R1
WHERE N.Sequence < 10000000
В приведенном выше запросе "util.zzIntegers" - это просто таблица целых чисел от 0 до 10 миллионов. Для выполнения запроса на моем сервере с холодным кешем требуется около 10 секунд, поэтому, если бы SQL Server выполнял функцию GETDATE () для каждой строки основной таблицы, он, безусловно, имел бы другое значение, по крайней мере, в столбце миллисекунд, но все 10 миллионов строк имеют одинаковую метку времени. Но я получаю разные GUID для каждой строки. Как я уже говорил, цель состоит в том, чтобы иметь одинаковый GUID в каждой строке.
Я также решил попробовать версию с явным конструктором табличных значений в надежде, что смогу обмануть оптимизатора, чтобы он поступил правильно. Я также сравнил его с реальной таблицей, а не с относительно «синтетическим» тестом, таким как список целых чисел из одного столбца. Следующее дало тот же результат.
WITH AnotherSingleRow AS
(
SELECT SingleRow.*
FROM (
VALUES (NewID(), Original_Login(), getdate())
)
AS SingleRow(GUID, UserID, TimeStamp)
)
SELECT R1.*, S.*
FROM SalesOrderLineItems S
CROSS JOIN AnotherSingleRow R1
SalesOrderLineItems - это таблица с 6 миллионами строк и 135 столбцами, чтобы вдвойне убедиться, что время выполнения было достаточно продолжительным, чтобы GETDATE () увеличивался, если бы SQL Server полностью оптимизировал конструктор табличных значений и просто вызывал функцию каждый раз запрос выполняется.
Я прятался здесь некоторое время, и это мой первый вопрос, поэтому я определенно хотел провести хорошее исследование и избежать критики за то, что просто выбросил вопрос. Следующие вопросы на этом сайте касаются GUID, но не имеют прямого отношения. Я также потратил полчаса на поиск в Google с различными комбинациями фраз, которые, кажется, ничего не нашли.
Azure фактически делает то, что я хочу, о чем свидетельствует следующий вопрос I
оказался в моем исследовании:
Guid.NewGuid () всегда возвращает один и тот же Guid для всех строк .
Тем не менее, я не на Azure и не собираюсь идти туда в ближайшее время.
Кто-то пытался сделать то же самое в SSIS
( Как вставить тот же guid в импорт SSIS )
но ответ на этот запрос вернулся, что вы генерируете GUID в
SSIS в качестве переменной и вставьте ее в каждую строку. Я мог бы, конечно, сделать
эквивалент в хранимой процедуре, но ради элегантности иСопровождаемость (мои коллеги имеют меньше опыта с запросами к SQL Server, чем я), я бы предпочел сохранить создание GUID пакета в запросе и максимально упростить любые хранимые процедуры.
Кстати, мой уровень опыта составляет 1-2 года с SQL Server в качестве аналитика данных / разработчика SQL в рамках более 10 лет, потраченных на написание кода, но в течение последних 20 лет яЯ был в основном специалистом по цифрам, а не специалистом по ИТ.В начале своей карьеры я работал на одного из ведущих поставщиков баз данных в качестве одного из разработчиков оптимизатора запросов, поэтому у меня есть довольно хорошее представление о том, что делает оптимизатор запросов, но у меня не было времени по-настоящему разобраться в том, как это делает SQL Server.,Так что я мог бы упустить что-то очевидное для других.
Заранее благодарю за помощь.