我正在编写一个快速实用的应用程序以将销售计划数据加载到SQL Server(2008 FWIW,尽管我认为特定版本并不重要)。
数据集是公司的销售计划:客户,零件号和月份的每种组合的数千行单位,美元和价格。这些数据每隔几周更新一次,因此重要的是跟踪谁进行了更改以及进行了哪些更改。
-- 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
我认为执行此操作的简单方法是使用时间戳,用户ID和对NEWID()的调用来构造单行结果,以创建批处理GUID。然后,执行CROSS JOIN将该单行附加到要插入的每一行。我尝试了几种不同的方法,但是查询执行引擎实际上只执行了一次GETDATE(),因为所有行中都出现了一个时间戳(即使对于500万行测试用例也是如此)。但是,对于结果集中的每一行,我得到一个不同的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到1000万的整数表。该查询大约需要10秒钟才能在具有冷高速缓存的服务器上运行,因此,如果SQL Server对主表的每一行执行GETDATE()函数,则它至少在毫秒列中肯定会有不同的值,但是所有1000万行的时间戳都相同。但是我为每一行获得了不同的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是一个具有600万行和135列的表,以双重确保运行时足够长,如果SQL Server完全优化了表值构造函数并每次仅调用该函数,则GETDATE()将递增查询运行。
我已经潜伏了一段时间,这是我的第一个问题,因此,我绝对想做好研究,避免因提出一个问题而受到批评。本网站上的以下问题涉及GUID,但并不直接相关。我还花了半个小时用各种短语组合搜索Google,但似乎没有发现任何问题。
Azure实际上可以满足我的要求,正如以下问题我所证明的 在我的研究中出现: Guid.NewGuid() always return same Guid for all rows。 但是,我不在Azure上,也不会很快去那里。
有人试图在SSIS中做同样的事情 (How to insert the same guid in SSIS import) 但是该查询的答案又回来了,您在其中生成了GUID 将SSIS作为变量并将其插入到每一行中。我当然可以做 在存储过程中等效,但为了优雅起见, 可维护性(我的同事对SQL Server查询的经验较少 (而不是我),我希望保留在批处理GUID的创建中 查询,并尽可能简化任何存储过程。
顺便说一句,我在SQL Server担任数据分析师/ SQL开发人员的经验是1-2年,这是花10多年编写代码的一部分,但是在过去的20年中,我主要是从事数字工作,而不是IT家伙。在我职业生涯的早期,我曾作为查询优化器的开发人员之一在一家开拓性数据库供应商那里工作,因此我非常清楚查询优化器的功能,但是却没有时间去深入研究SQL Server的工作方式。 。因此,我可能会完全错过其他人显而易见的东西。
在此先感谢您的帮助。