英文:
How to use FOR SYSTEM_TIME clause on a view in a TVF?
问题
我正在尝试在一个包含系统版本表的视图上使用FOR SYSTEM_TIME子句,该视图位于一个表值函数中。
根据微软的文档,当查询视图时,使用FOR SYSTEM_TIME子句应该是可能的:https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table?view=sql-server-ver16
然而,我遇到了以下错误:
临时FOR SYSTEM_TIME子句只能与系统版本表一起使用。'dbo.AccountView'不是系统版本表。
函数、视图和表的定义:
-- 账户表
CREATE TABLE [dbo].[Account](
[Account No] [int] IDENTITY(1,1) NOT NULL,
[Global ID] AS ('A'+CONVERT(nvarchar,[Account No])) PERSISTED,
[User] varchar NULL,
[Version No] [tinyint] NULL,
[Version Comment] varchar NULL,
[Valid From] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
[Valid To] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Account] PRIMARY KEY CLUSTERED
(
[Account No] 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],
PERIOD FOR SYSTEM_TIME ([Valid From], [Valid To])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[hst_Account])
)
GO
-- 其他表和视图...
-- 表值函数
CREATE FUNCTION [dbo].[GetAsOfDate_AccountView] (@tblID int, @asOfDate datetime2(2))
RETURNS TABLE
AS RETURN
SELECT av.*
FROM [dbo].[AccountView] FOR SYSTEM_TIME AS OF @asOfDate AS av
WHERE av.[Account No] = @tblID
GO
*已编辑以添加视图和表的脚本。
英文:
I am trying to use the FOR SYSTEM_TIME clause on a view, which contains system-versioned tables, in a table-valued function.
Per Microsoft's documentation, using the FOR SYSTEM_TIME clause when querying a view should be possible: https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table?view=sql-server-ver16
However, I am getting the following error:
> Temporal FOR SYSTEM_TIME clause can only be used with system-versioned tables. 'dbo.AccountView' is not a system-versioned table.
Function, view, and table definitions:
--Account Table
CREATE TABLE [dbo].[Account](
[Account No] [int] IDENTITY(1,1) NOT NULL,
[Global ID] AS ('A'+CONVERT([nvarchar](10),[Account No])) PERSISTED,
[User] [varchar](50) NULL,
[Version No] [tinyint] NULL,
[Version Comment] [varchar](280) NULL,
[Valid From] [datetime2](2) GENERATED ALWAYS AS ROW START NOT NULL,
[Valid To] [datetime2](2) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Account] PRIMARY KEY CLUSTERED
(
[Account No] 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],
PERIOD FOR SYSTEM_TIME ([Valid From], [Valid To])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[hst_Account])
)
GO
ALTER TABLE [dbo].[Account] ADD CONSTRAINT [DF_Account_ValidFrom] DEFAULT (sysutcdatetime()) FOR [Valid From]
GO
ALTER TABLE [dbo].[Account] ADD CONSTRAINT [DF_Account_ValidTo] DEFAULT (CONVERT([datetime2](2),'9999-12-31 23:59:59.99')) FOR [Valid To]
GO
--Account_General Table
CREATE TABLE [dbo].[Account_General](
[Account No] [int] NOT NULL,
[Name] [varchar](100) NULL,
[Main Phone] [varchar](12) NULL,
[General Email] [varchar](100) NULL,
[Website] [varchar](100) NULL,
[Company] [varchar](50) NULL,
[Valid From] [datetime2](2) GENERATED ALWAYS AS ROW START NOT NULL,
[Valid To] [datetime2](2) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Account_General] PRIMARY KEY CLUSTERED
(
[Account No] 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],
PERIOD FOR SYSTEM_TIME ([Valid From], [Valid To])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[hst_Account_General])
)
GO
ALTER TABLE [dbo].[Account_General] ADD CONSTRAINT [DF_Account_General_ValidFrom] DEFAULT (sysutcdatetime()) FOR [Valid From]
GO
ALTER TABLE [dbo].[Account_General] ADD CONSTRAINT [DF_Account_General_ValidTo] DEFAULT (CONVERT([datetime2](2),'9999-12-31 23:59:59.99')) FOR [Valid To]
GO
ALTER TABLE [dbo].[Account_General] WITH CHECK ADD CONSTRAINT [FK_Account_General_AccountNo] FOREIGN KEY([Account No])
REFERENCES [dbo].[Account] ([Account No])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Account_General] CHECK CONSTRAINT [FK_Account_General_AccountNo]
GO
--Account_Temp Table
CREATE TABLE [dbo].[Account_Temp](
[Account No] [int] NOT NULL,
[Status] [varchar](30) NULL,
[Locked By] [varchar](50) NULL,
[Time Locked] [datetime2](2) NULL,
CONSTRAINT [PK_Account_Temp] PRIMARY KEY CLUSTERED
(
[Account No] 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].[Account_Temp] WITH CHECK ADD CONSTRAINT [FK_Account_Temp_AccountNo] FOREIGN KEY([Account No])
REFERENCES [dbo].[Account] ([Account No])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Account_Temp] CHECK CONSTRAINT [FK_Account_Temp_AccountNo]
GO
--AccountView
CREATE VIEW [dbo].[AccountView] AS
SELECT a.[Account No]
,a.[Global ID]
,atemp.[Status]
,agen.[Name]
,agen.[Main Phone]
,agen.[General Email]
,agen.[Website]
,agen.[Company]
,a.[Version No]
,atemp.[Locked by]
,atemp.[Time Locked]
,a.[User] 'Modified By'
,CONVERT(datetime2(2), a.[Valid From] AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') 'Time Modified'
,ISNULL(ahist.[User], a.[User]) 'Created By'
,CONVERT(datetime2(2), ISNULL(ahist.[Valid From], a.[Valid From]) AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') 'Time Created'
FROM [dbo].[Account] a
LEFT JOIN [dbo].[Account_Temp] atemp
ON a.[Account No] = atemp.[Account No]
LEFT JOIN [dbo].[Account_General] agen
ON a.[Account No] = agen.[Account No]
LEFT JOIN (
SELECT ahist.*
,rn = ROW_NUMBER() OVER (PARTITION BY ahist.[Account No] ORDER BY ahist.[Valid From])
FROM [dbo].hst_Account ahist) ahist
ON ahist.[Account No] = a.[Account No] AND ahist.rn = 1
GO
--Table-Valued Function
CREATE FUNCTION [dbo].[GetAsOfDate_AccountView] (@tblID int, @asOfDate datetime2(2))
RETURNS TABLE
AS RETURN
SELECT av.*
FROM [dbo].[AccountView] FOR SYSTEM_TIME AS OF @asOfDate AS av
WHERE av.[Account No] = @tblID
GO
*Edited to add script for view and tables.
答案1
得分: 1
正如评论中提到的,经过其他人的多次尝试和错误来创建一个最小可重现示例,您不能在FOR SYSTEM_TIME AS OF
子句中使用一个不是时间对象本身的对象中的变量/参数。因此,即使您引用的“VIEW”包含时间对象,您仍会收到一个有点误导性的错误:
> 仅可使用时间 FOR SYSTEM_TIME 子句与系统版本表一起使用。“Sandbox.dbo.PartiallyTemporalView”不是系统版本表。
为什么会这样工作,诚实地说,只有微软的某人可能能够回答。您可能最好通过他们开一个工单(可以通过您的支持计划或通过Azure 365反馈SQL社区)提出它作为一个“bug”;他们可能会提供一些见解,甚至可能会在SQL Server的将来版本中“修复”这个问题。
至于解决方案,那就是在您的“FUNCTION”中重新定义“VIEW”的定义,并针对相关的表使用“FOR SYSTEM_TIME AS OF”。在这种情况下,似乎只是“dbo.Account_General”:
CREATE FUNCTION [dbo].[GetAsOfDate_AccountView] (@tblID int,
@asOfDate datetime2(2))
RETURNS table
AS
RETURN SELECT a.[Account No],
a.[Global ID],
atemp.[Status],
agen.[Name],
agen.[Main Phone],
agen.[General Email],
agen.[Website],
agen.[Company],
a.[Version No],
atemp.[Locked by],
atemp.[Time Locked],
a.[User] AS [Modified By],
CONVERT(datetime2(2), a.[Valid From] AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') AS [Time Modified],
ISNULL(ahist.[User], a.[User]) AS [Created By],
CONVERT(datetime2(2), ISNULL(ahist.[Valid From], a.[Valid From])AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') AS [Time Created]
FROM [dbo].[Account] a
LEFT JOIN [dbo].[Account_Temp] atemp ON a.[Account No] = atemp.[Account No]
LEFT JOIN [dbo].[Account_General] FOR SYSTEM_TIME AS OF @asOfDate agen ON a.[Account No] = agen.[Account No]
LEFT JOIN (SELECT ahist.*,
ROW_NUMBER() OVER (PARTITION BY ahist.[Account No]
ORDER BY ahist.[Valid From]) AS rn
FROM [dbo].hst_Account ahist) ahist ON ahist.[Account No] = a.[Account No]
AND ahist.rn = 1;
GO
英文:
As have been mentioned in the comments, and after much trial and error by others to create a minimal reproducible example, you can't use a variable/parameter in a (i)TVF in the FOR SYSTEM_TIME AS OF
clause against a object that isn't itself isn't a temporal object. As such, even if the VIEW
you are referencing does contain temporal objects you'll get a (some what misleading) error:
> Temporal FOR SYSTEM_TIME clause can only be used with system-versioned tables. 'Sandbox.dbo.PartiallyTemporalView' is not a system-versioned table.
Why it works this way, honestly only someone from Microsoft could likely answer. You would probably be best opening a ticket (either via your support plan or via the Azure 365 Feedback SQL Community) with them to raise it as a "bug"; they may give some insight or they might even "fix" the problem (in a future version of SQL Server).
As for a solution, that would be to repeat the definition of the VIEW
in your FUNCTION
instead, and use FOR SYSTEM_TIME AS OF
against the relevant table(s). That appears to just be dbo.Account_General
in this case:
CREATE FUNCTION [dbo].[GetAsOfDate_AccountView] (@tblID int,
@asOfDate datetime2(2))
RETURNS table
AS
RETURN SELECT a.[Account No],
a.[Global ID],
atemp.[Status],
agen.[Name],
agen.[Main Phone],
agen.[General Email],
agen.[Website],
agen.[Company],
a.[Version No],
atemp.[Locked by],
atemp.[Time Locked],
a.[User] AS [Modified By],
CONVERT(datetime2(2), a.[Valid From] AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') AS [Time Modified],
ISNULL(ahist.[User], a.[User]) AS [Created By],
CONVERT(datetime2(2), ISNULL(ahist.[Valid From], a.[Valid From])AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') AS [Time Created]
FROM [dbo].[Account] a
LEFT JOIN [dbo].[Account_Temp] atemp ON a.[Account No] = atemp.[Account No]
LEFT JOIN [dbo].[Account_General] FOR SYSTEM_TIME AS OF @asOfDate agen ON a.[Account No] = agen.[Account No]
LEFT JOIN (SELECT ahist.*,
ROW_NUMBER() OVER (PARTITION BY ahist.[Account No]
ORDER BY ahist.[Valid From]) AS rn
FROM [dbo].hst_Account ahist) ahist ON ahist.[Account No] = a.[Account No]
AND ahist.rn = 1;
GO
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论