问题目前影响 C#/.NET、基于 ADO.NET 和 SQL Server 2008 R2 的 DB 访问,但我认为它也适用于其他数据库。
我注意到系统的一些旧模块具有非最佳 SQL 查询,使用多个串联的值字符串而不是参数占位符。他们对表进行轮询,例如每 10 秒一次,以获取在过去几分钟内添加的项目,从而在每次执行时生成新的查询计划。
它们的性能还不错,没有 SQL 注入风险(没有 Web/用户表单),它们很旧,将它们的查询更改为正确的参数化需要做很多工作。我建议做这个改变,但有争论说这会浪费时间,其他事情更重要。
编辑:数据库应该使用大部分参数化查询(所有较新的模块都使用)运行,所以我想避免“优化临时”选项。部分参数化查询无论如何都会创建一个计划。在临时优化模式下运行时是否有缺点,主要是参数化查询?
对我来说,这些旧模块似乎占用了数据库资源的很大一部分,尽管它们很少。随着时间的推移,即使是这种类型的单个模块也会创建数千个查询计划,而所有较新的模块加在一起则更少。
更改这些是否重要,还是我可以将它们保留在它们的状态,仅在当前/未来模块中使用优化/参数化查询?
SQL 是这样的:
select ItemId, ItemName from Items
where ItemType=3 and ItemCreator=1234
and ItemDate >= '2013-11-23 12:30:00'
Run Code Online (Sandbox Code Playgroud)
其中值会有所不同,日期是几分钟之前。在少数情况下,日期已更改为“@startDate”之类的参数,以避免出现格式问题,但 ItemType 和 ItemCreator 值仍然是串联字符串。
在使用 DMV 或 Activity Monitor(最近昂贵的查询 - 计划计数列)监视查询计划时,我注意到其中一些查询在缓存中有 8000 多个等效的查询计划:
select count(*), query_plan_hash
from sys.dm_exec_query_stats
group by query_plan_hash
order by count(*) desc
Run Code Online (Sandbox Code Playgroud)
然后使用 sys.dm_exec_query_plan 上的 CROSS APPLY 选择计划 XML,并通过查询计划哈希选择计划句柄。
编辑/临时结论: 似乎最好让非常旧的应用程序保持原样,即使在创建大量临时查询时也是如此。我最担心的是,大量的一次性临时查询会导致好的、多用途的参数化和准备好的查询计划从缓存中被驱逐。这不会发生,因为当清理完成时,临时计划首先被驱逐,其他计划根据复杂性、使用次数等因素进行评级。因此,无论有多少使用率,参数化查询都可能被保留临时或部分参数化的计划大量涌入。临时优化减少了计划大小(实际上,第一次使用时没有存储真正的计划),但可能会保留更多计划,使用类似的内存(这是正确的吗?)。即使是部分参数化的 SQL(避免本地格式问题的 DateTime 参数)如果不再使用,也会很快被驱逐,即使使用 sp_executesql 发送,这会强制参数化和计划缓存。拥有大量(5000 …
我对以下类型的 Oracle 11g 进行了查询,这导致了一系列非常低效的全表扫描:
select t.Id, t.ObjectId, t...[other Tasks columns]
, od1...[ObjectData1 columns]
, od2...[ObjectData2 columns]
from Tasks t
left join ObjectData1 od1 on t.ObjectId=od1.ObjectId
left join ObjectData2 od2 ...
... [more leftjoins] ...
where t.ObjectId = 12345
or t.Id in (
select TaskId from ObjectAffectingTasks where ObjectId=12345
)
Run Code Online (Sandbox Code Playgroud)
任务在 Id、ObjectId 上有索引;ObjectAffectingTasks在 ObjectId 和 TaskId 上都有索引。所有连接的表也有适当的索引。该ObjectAffectingTasks表包含任务ID影响的对象,但对象ID有另一个,所以影响对象ID为12345的所有任务应选择。
在分析查询时,似乎是 OR 条件破坏了执行计划。只有 ObjectId 或只有子查询的 where 子句使用了所有索引。另一种解决方法是创建一个 Union 子查询,它也使用索引:
where t.Id in (
select TaskId from ObjectAffectingTasks where ObjectId=12345 …Run Code Online (Sandbox Code Playgroud) 编辑: 这个问题是关于如何处理在整个系统设计中出现的许多问题,这使得系统的某些部分偏离了通用标准。例如,使用自己的程序代码管理业务模型中的所有内容,甚至关系完整性。这给数据库和持久层带来了一种糟糕设计的味道,将其用作“将某些内容转入并以某种方式再次取出”的地方,而不是结构化存储。我问了这个问题,因为在我看来,NoSQL 文档存储就像一个选项,可以将已经无模式(或非常松散的模式)数据库移动到默认情况下没有模式的数据库。此外,我必须指出,尽管这里描述了一些缺陷,但整个系统根本不是一个坏系统。此外,一些问题,例如版本控制,已经有了解决方案或已经实施。
想想你看到的一个软件系统,基于经典的关系数据库(SQL Server、Oracle)、NHibernate作为对象关系映射器(ORM)、顶部的业务逻辑模型层和大量模块(几百个) ,主要是基于 .NET 的服务和一些 Web 服务(带有客户端,每个系统/客户最多约 100 个,公司网络,非公共)。操作方式主要是OLTP,写/CUD访问是工作负载的重要组成部分。生产数据库通常大约为 10GB,但大小总是远低于 100GB(因此没有“大数据”)。它确实工作得很好,但对我来说,数据库和 ORM 实现有几种反模式(对于关系数据库)的味道。也许这些实现对于另一种数据库会更好——面向文档(“NoSQL”)或内存数据库。
省略了许多关系数据库和支持 ORM 的功能:表被强烈反规范化,外键关系丢失或不可能,例如由于元数据表引用不同的主表,列如IdInTable INT, OwnerTable INT. NHibernate 几乎没有映射对象关系(并且通常存在不适合它的表结构的问题)。相反,这些是在业务逻辑中实现的(有时会导致孤立的子对象或低效的数据库访问,见下文)。
基础之下的非规范化:增加非 1st NF 数据的使用:带有 XML、逗号分隔列表或复合数值列的 nclob/nvarchar(max) 列(例如,任务类型 123 的 123、10123、40123,但模块配置不同由 0,1,4 * 10000 标识)。前两个包含数据库相关、逻辑“外键”和数据模型相关值,例如<UserType>AdminUser</UserType>(要检查LIKE '%...%')。这主要是由于许多快速发布、短暂存在和自定义的值不应该进入主模式,或者更容易通过 XML 值实现。
非 2nd NF 数据,包括由触发器、后续存储过程或应用程序复制到其他表中的表内容。例如,将表列值复制到“垂直”元数据表,这再次复制到元数据的“水平”或“旋转”表示(每个元数据类型为一列),因为某些应用程序只能使用元数据或水平元数据. 经常要求使用“垃圾箱结构”(将从各种来源收集的数据转储到一个 nclob/nvarchar(max)“垃圾箱”列中,并让应用程序搜索它,而不是许多不同的来源)。
业务逻辑模型和应用程序中的“单一对象疾病”: 单个对象的迭代和立即加载/保存:业务层主要使用 Load/Save() 方法来处理单个对象和少量基于批量/集合的操作。一个常见的工作是通过 SQL 或者它的 NHibernate 表示来获取对象 ID,然后遍历所有检索到的 Id 并以foreach (oneId in Ids) { myObjects.Add( BizModel.GetMyObjectById(oneId) ); }. 这包括所有元数据、依赖对象集合等,这是典型的 SELECT N+1 情况。此外,大多数 NHibernate …
nosql document-oriented orm relational-theory denormalization
在 SQL Server 2012 上,我有一个用于将现有数据与新数据合并的中间/暂存表,我想在其中为新创建的行插入数字 ID:
ID NaturalID Comment
1 franknfurther03071972 blahblah
2 chrisrock12081980 nonsense
null clairecampbell24121990 merry christmas
3 walterhermes22032001 young guy
4 tanjaolsen16051996 nice
null timharris20041999 came late
Run Code Online (Sandbox Code Playgroud)
带有“空”ID 的行是新的,编号的 ID 是那些已经存在于主目标表中的行。NaturalID 可以唯一标识一个条目(实际上是多列)。我想在当前最大 ID 之后将“空”ID 设置为增量值,此处为:5 和 6,在找到更多空 ID 时增加。
目前,我使用游标遍历 ID 为 null 的行并使用值更新每个 ID,但由于它确实是一个非常大的表,因此需要几天时间。
我尝试使用 row_number() 进行更新,但它给了我一个错误“窗口函数只能出现在 SELECT 或 ORDER BY 子句中。”:
update StagingTable set ID=ROW_NUMBER() over (order by NaturalId)
from StagingTable where id is null -- fails
Run Code Online (Sandbox Code Playgroud)
我该怎么做?
cache ×1
identity ×1
index ×1
nosql ×1
oracle ×1
orm ×1
parameter ×1
performance ×1
row ×1
sql-server ×1
statistics ×1
update ×1