在 SQL Server 2014 中写入基础表后立即查询视图

Posted

技术标签:

【中文标题】在 SQL Server 2014 中写入基础表后立即查询视图【英文标题】:Querying a view immediately after writing to underlying tables in SQL Server 2014 【发布时间】:2015-08-13 23:05:07 【问题描述】:

我遇到了一个问题,如果我写入一个依赖于视图的表(使用 Linq-to-SQL),然后立即转身查询该视图以检查写入的影响(使用与数据库的新连接,因此使用新的数据上下文),写入的影响不会立即显示出来,而是需要几秒钟才能出现。这只是偶尔发生(可能10-2010,000 左右写入)。

这是视图的定义:

CREATE VIEW [Position].[Transactions]
WITH SCHEMABINDING
AS
(
  SELECT
    Account,
    Book,
    TimeAPIClient AS DateTimeUtc,
    BaseCcy AS Currency,
    ISNULL(QuantityBase, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(0 AS tinyint), 0) AS TransactionType
  FROM Trades.FxSpotMF
  WHERE IsCancelled = 0

  UNION ALL

  SELECT
    Account,
    Book,
    TimeAPIClient AS DateTimeUtc,
    QuoteCcy AS Currency,
    ISNULL(-QuantityBase * Rate, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(0 AS tinyint), 0) AS TransactionType
  FROM Trades.FxSpotMF
  WHERE IsCancelled = 0

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    BaseCcy AS Currency,
    ISNULL(QuantityBase, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(1 AS tinyint), 1) AS TransactionType
  FROM Trades.FxSpotManual
  WHERE IsCancelled = 0

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    QuoteCcy AS Currency,
    ISNULL(-QuantityBase * Rate, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(1 AS tinyint), 1) AS TransactionType
  FROM Trades.FxSpotManual
  WHERE IsCancelled = 0

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    BaseCcy AS Currency,
    ISNULL(SpotQuantityBase, 0) AS Quantity,
    SpotValueDate AS SettleDate,
    ISNULL(CAST(2 AS tinyint), 2) AS TransactionType
  FROM Trades.FxSwap

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    QuoteCcy AS Currency,
    ISNULL(-SpotQuantityBase * SpotRate, 0) AS Quantity,
    SpotValueDate AS SettleDate,
    ISNULL(CAST(2 AS tinyint), 2) AS TransactionType
  FROM Trades.FxSwap

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    BaseCcy AS Currency,
    ISNULL(ForwardQuantityBase, 0) AS Quantity,
    ForwardValueDate AS SettleDate,
    ISNULL(CAST(2 AS tinyint), 2) AS TransactionType
  FROM Trades.FxSwap

  UNION ALL

  SELECT
    Account,
    Book,
    ExecutionTimeUtc AS DateTimeUtc,
    QuoteCcy AS Currency,
    ISNULL(-ForwardQuantityBase * ForwardRate, 0) AS Quantity,
    ForwardValueDate AS SettleDate,
    ISNULL(CAST(2 AS tinyint), 2) AS TransactionType
  FROM Trades.FxSwap

  UNION ALL

  SELECT
    Account,
    c.Book,
    TimeUtc AS DateTimeUtc,
    Currency,
    ISNULL(Amount, 0) AS Quantity,
    SettleDate,
    ISNULL(CAST(3 AS tinyint), 3) AS TransactionType
  FROM Trades.Commission c
  JOIN Trades.Payment p
    ON c.UniquePaymentId = p.UniquePaymentId
    AND c.Book = p.Book
)

虽然这是由 Linq-to-SQL 生成的用于写入基础表之一的查询:

INSERT INTO [Trades].[FxSpotMF] ([UniqueTradeId], [BaseCcy], [QuoteCcy], [ValueDate], [Rate], [QuantityBase], [Account], [Book], [CounterpartyId], [Counterparty], [ExTradeId], [TimeAPIClient], [TimeAPIServer], [TimeExchange], [TimeHandler], [UniqueOrderId], [IsCancelled], [ClientId], [SequenceId], [ExOrdId], [TradeDate], [OrderCycleId], [CycleIndex])
  VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22)

这是由 Linq-to-SQL 生成的查询,用于检查写入的效果:

SELECT
  SUM([t0].[Quantity]) AS [Item2],
  [t0].[Currency] AS [Item1]
FROM [Position].[Transactions] AS [t0]
WHERE ([t0].[Book] = @p0)
AND ([t0].[DateTimeUtc] < @p1)
GROUP BY [t0].[Currency]

另外,这是生成写入的 Linq-to-SQL 代码(使用 F# 类型提供程序):

type Schema = Microsoft.FSharp.Data.TypeProviders.DbmlFile<"TradeDb.dbml", ContextTypeName="TradeDb"> 

use db = new Schema.TradeDb(connectionString)
let trade = new Schema.Trades_FxSpotMF()
(* omitted: set object properties corresponding to column values here... *)
db.Trades_FxSpotMF.InsertOnSubmit(trade)
db.SubmitChanges()

虽然这是生成读取的相应 Linq-to-SQL:

use db = new Schema.TradeDb(connectionString)
query  for t in db.Position_Transactions do
        where ( t.Book = book &&
                t.DateTimeUtc < df.MaxExecutionTimeExcl
              )
        groupBy t.Currency into group
        let total = query  for x in group do sumBy x.Quantity 
        select (group.Key, total)
      
|> Map.ofSeq

我原以为System.Data.Linq.DataContext.SubmitChanges() 只会在写入事务完成后返回,并且视图的任何后续查询都必须包含写入的效果......我错过了什么/做错了什么?

【问题讨论】:

你能粘贴你的 linq-to-sql 代码吗? 不幸的是,我不是 F# 大师,所以我有点迷路了。在调用 select 查询之前,我会尝试在用于插入操作的 db 上下文(c# 中的 using 语句)上调用 dispose 方法。我的猜测是插入语句的执行被延迟,有时 select 语句在实际插入发生之前被触发。 上面的两个sn-ps位于代码库的两个不同部分,没有一个接一个地执行。此外,db 上下文已通过“use db...”语法进行处理。我不认为这是 F# 特有的,这是相当普通的 Linq-to-SQL。 好吧,我从未使用过 F#,但很明显,当您尝试读回数据时,写入事务尚未提交。所以,我会检查SubmitChanges 上的文档并验证它何时以及如何提交事务。默认情况下,它可能取决于垃圾收集器。因此,框架中必须有一个方法可以用来显式启动和提交事务,而无需等待垃圾收集器启动, 我认为使用 sql server profiler 查看发生了什么会更好。每个事务什么时候开始,什么时候结束,等等。 【参考方案1】:

我终于明白了这一点:数据库写入是在它们自己的线程中完成的,主线程在检查结果之前等待所有写入线程完成。但是,检查所有线程是否完整的代码中存在一个错误,导致主线程过早进行检查。

【讨论】:

【参考方案2】:

检查写入效果的 SQL 链接是否可能正在查看旧的缓存数据?尝试使用上下文对象的refresh method 预先刷新缓存。在对象上使用RefreshMode.OverwriteCurrentValues

【讨论】:

【参考方案3】:

你可以试试table hints,即

CREATE VIEW [Position].[Transactions]
WITH SCHEMABINDING
AS
(
  SELECT
    Account,
    Book,
    TimeAPIClient AS DateTimeUtc,
    BaseCcy AS Currency,
    ISNULL(QuantityBase, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(0 AS tinyint), 0) AS TransactionType
  FROM Trades.FxSpotMF WITH(NOLOCK)
  WHERE IsCancelled = 0

  UNION ALL

  SELECT
    Account,
    Book,
    TimeAPIClient AS DateTimeUtc,
    QuoteCcy AS Currency,
    ISNULL(-QuantityBase * Rate, 0) AS Quantity,
    ValueDate AS SettleDate,
    ISNULL(CAST(0 AS tinyint), 0) AS TransactionType
  FROM Trades.FxSpotMF WITH(NOLOCK)
  WHERE IsCancelled = 0
  ...
)

Alos 检查this 博客条目,在我的情况下使用 nolock 提示解决问题。

【讨论】:

该博客条目表明,使用 NOLOCK 提示可能会导致行丢失或读取两次,这与我想要实现的目标完全相反。【参考方案4】:

你已经创建了一个带有模式绑定的视图

CREATE VIEW [Position].[Transactions]
WITH SCHEMABINDING

并且有 8 个联合操作,来自 4 个表的 9 个查询

FROM Trades.FxSpotMF  --2times
  WHERE IsCancelled = 0

FROM Trades.FxSpotManual --2 times
  WHERE IsCancelled = 0

FROM Trades.FxSwap -- 4 times

FROM Trades.Commission c
  JOIN Trades.Payment p
    ON c.UniquePaymentId = p.UniquePaymentId
    AND c.Book = p.Book

每次插入这些表之一后刷新视图,系统可能需要几秒钟,并且您的选择查询在插入后立即运行。 可能会在 0~1 毫秒内将插入执行到表中,但刷新视图需要 100 多毫秒,并且由于视图是从服务器的缓存中提供的,因此选择查询未命中

【讨论】:

但我认为非索引视图只是一个存储查询,即没有任何缓存?也就是说,您从查询视图中获得的结果与将视图的定义内联到您的查询中是相同的吗?如果插入事务完成,所有后续读取都应该获取新值,对吗?还是我缺少其他级别的缓存? 也许这会帮助你解释-msdn.microsoft.com/en-us/library/… 这只是在讨论已编译执行计划的缓存,而不是查询结果的缓存,不是吗? 检查一下 - 缓存中的内容 [dba.stackexchange.com/questions/43572/… @mpeac,你是对的。查询非索引视图与内联相同。如果插入事务已提交,所有后续读取都将看到新值。索引视图或非索引视图都没有关系。只有承诺或未承诺才重要。因此,您的 F# 代码必须以某种方式推迟 COMMIT

以上是关于在 SQL Server 2014 中写入基础表后立即查询视图的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server,如何在不丢失数据的情况下创建表后设置自动增量?

sqlserver用管理器建表后如何查看创建表的语句?谢谢!!

sqlserver用管理器建表后如何查看创建表的语句?谢谢!!

高可用性副本 SQL Server 2014 中的只读意图

怎么清楚sql sever中的记录

sqlserver 2008问题!