有没有办法纯粹在 T-SQL 中从现有表生成创建脚本(即不使用 SMO,因为 T-SQL 无法访问 SMO)。假设一个存储过程接收一个表名并返回一个包含给定表的创建脚本的字符串?
现在让我描述一下我面临的情况,因为可能有不同的方法来解决这个问题。我有一个包含几十个数据库的实例。这些数据库都具有相同的架构、相同的表、索引等。它们是作为第三方软件安装的一部分创建的。我需要有一种与他们合作的方法,以便我可以以特别的方式从他们那里收集数据。dba.se 的好人已经在这里帮助了我如何在不同的数据库中创建触发器?
目前我需要找到一种方法来从所有数据库的表中进行选择。我已将所有数据库名称记录到一个名为的表中,Databasees并编写了以下脚本来对所有数据库执行 select 语句:
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp
select * into #tmp from Database1.dbo.Table1 where 1=0
DECLARE @statement nvarchar(max) =
N'insert into #tmp select * from Table1 where Column1=0 and Cloumn2 =1'
DECLARE @LastDatabaseID INT
SET @LastDatabaseID = 0
DECLARE @DatabaseNameToHandle varchar(60)
DECLARE @DatabaseIDToHandle int
SELECT TOP 1 @DatabaseNameToHandle = Name,
@DatabaseIDToHandle = Database_Ref_No
FROM Databasees
WHERE Database_Ref_No > @LastDatabaseID
ORDER BY Database_Ref_No
WHILE @DatabaseIDToHandle …Run Code Online (Sandbox Code Playgroud) 当我遇到这个东西时,我正在研究其他东西。我正在生成包含一些数据的测试表并运行不同的查询,以了解编写查询的不同方式如何影响执行计划。这是我用来生成随机测试数据的脚本:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('t') AND type in (N'U'))
DROP TABLE t
GO
CREATE TABLE t
(
c1 int IDENTITY(1,1) NOT NULL
,c2 int NULL
)
GO
insert into t
select top 1000000 a from
(select t1.number*2048 + t2.number a, newid() b
from [master]..spt_values t1
cross join [master]..spt_values t2
where t1.[type] = 'P' and t2.[type] = 'P') a
order by b
GO
update t set c2 = null
where c2 < 2048 * 2048 …Run Code Online (Sandbox Code Playgroud) 我需要按表定义顺序列出表中的列:
select * from syscolumns
where id = object_id('MyTable')
--order by colid
Run Code Online (Sandbox Code Playgroud)
通过检查syscolumns表,两列看起来相关:colid和colorder。关于 syscolumns 的 MSDN 文章说:
colid | smallint | Column or parameter ID.
colorder | smallint | Identified for informational purposes only.
| Not supported. Future compatibility is not guaranteed.
Run Code Online (Sandbox Code Playgroud)
我试着跑
select * from syscolumns where colorder <> colid
Run Code Online (Sandbox Code Playgroud)
这没有产生任何行,这让我认为这些列在大多数情况下具有相同的值。
看起来最安全的选择是使用colid。但是我很想知道:这两列之间是否有区别,如果有,区别是什么?
MSDN 文章也没有确认,colid 反映了表定义的顺序。虽然可以合理地假设情况确实如此,但您能否告诉我,如果您确定确实如此,您是如何知道是这样的?
如何以最自然的方式在 Sql Server 中建模零或一到零或一的关系?
有一个“危害”表,列出了现场的危害。有一个“任务”表用于需要在站点上完成的工作。有些任务是为了修复一个危险,没有一个任务可以处理多个危险。有些危害有修复它们的任务。任何危险都不能有两项相关的任务。
以下是我能想到的最好的:
CREATE TABLE [dbo].[Hazard](
[HazardId] [int] IDENTITY(1,1) NOT NULL,
[TaskId] [int] NULL,
[Details] [varchar](max) NULL,
CONSTRAINT [PK_Hazard] PRIMARY KEY CLUSTERED
(
[HazardId] ASC
))
GO
ALTER TABLE [dbo].[Hazard] WITH CHECK ADD CONSTRAINT [FK_Hazard_Task] FOREIGN KEY([TaskId])
REFERENCES [dbo].[Task] ([TaskId])
GO
CREATE TABLE [dbo].[Task](
[TaskId] [int] IDENTITY(1,1) NOT NULL,
[HazardId] [int] NULL,
[Details] [varchar](max) NULL,
CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED
(
[TaskId] ASC
))
GO
ALTER TABLE [dbo].[Task] WITH CHECK ADD CONSTRAINT [FK_Task_Hazard] FOREIGN KEY([HazardId])
REFERENCES [dbo].[Hazard] …Run Code Online (Sandbox Code Playgroud) 我打开一个(可重复读)事务 ( BEGIN TRAN) 对某些记录做一些工作。我做的第一件事是检查我需要更改的数据是否在数据库中。在某些情况下,会有然后我继续我的更改。但在某些情况下,将无事可做。在这种情况下,我要么COMMIT TRAN或ROLLBACK TRAN从存储过程返回。此时还没有对数据做任何改动,所以commit和rollback的效果是一样的。
在提交和回滚之间进行选择时,我应该注意什么?是否有不同的性能成本?其他考虑?
我创建了两个数据库,第二个数据库中的一个表和第一个数据库中的一个存储过程。存储过程跨数据库访问表。我创建了一个 sql server 登录名,并将此登录名映射到每个数据库中的一个用户。我向用户授予 db_owner 权限。这是完成它的脚本(我在运行脚本时作为 SQL 系统管理员连接):
USE [master]
GO
CREATE DATABASE [TestDatabase1] ON PRIMARY
( NAME = N'TestDatabase1', FILENAME = N'd:\database\TestDatabase1.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'TestDatabase1_log', FILENAME = N'd:\database\TestDatabase1_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
CREATE DATABASE [TestDatabase2] ON PRIMARY
( NAME = N'TestDatabase2', FILENAME = N'd:\database\TestDatabase2.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB …Run Code Online (Sandbox Code Playgroud) 在系统中有一个夜间数据加载过程,其中删除所有外键,截断所有表,从外部源重新填充,并将外键放回原位。
在此过程结束时,我希望 sp_updatestats运行,以便更新所有这些新数据的统计信息。
但是,在检查代码时,我注意到以下行:
EXEC sp_MSforeachtable 'UPDATE STATISTICS ? WITH FULLSCAN'
Run Code Online (Sandbox Code Playgroud)
你能把它比作跑步sp_updatestats吗?那就是一个与另一个的区别和优点/缺点是什么?
我有一个查询sys.dm_exec_requests视图的存储过程。在存储过程中,视图只返回一行,而存储过程需要查看所有行。视图上的MSDN文章说返回什么取决于用户权限:
如果用户在服务器上有 VIEW SERVER STATE 权限,用户将看到 SQL Server 实例上所有正在执行的会话;否则,用户将只能看到当前会话。
存储过程实际上是一个broker队列的激活存储过程:
CREATE QUEUE test_queue
WITH
STATUS = ON,
RETENTION = OFF ,
ACTIVATION (
STATUS = ON,
PROCEDURE_NAME = test_procedure,
MAX_QUEUE_READERS = 1,
EXECUTE AS SELF ),
POISON_MESSAGE_HANDLING (STATUS = ON)
ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
当我阅读MSDN文章时,我改变了
EXECUTE AS SELF
Run Code Online (Sandbox Code Playgroud)
到
EXECUTE AS 'dbo'
Run Code Online (Sandbox Code Playgroud)
这没有任何区别。sys.dm_exec_requests仍然会返回一行。
我也试着做
EXECUTE AS OWNER
Run Code Online (Sandbox Code Playgroud)
这也没什么区别。据我了解,存储过程所有者是谁创建的,也就是我。由于我处于系统管理员角色中,应该可以正常工作,但事实并非如此。我将不胜感激任何故障排除提示。
特别是我想知道如何列出给定用户的服务器权限,以便我可以根据文章检查它们是否包含“查看服务器状态”权限。
令人困惑的部分是我所理解的用户是在数据库级别,所以我不清楚它如何获得服务器权限。如果你能澄清这一点,那就太好了。
这个说法:
INSERT INTO deleteme
SELECT #t.id, 'test' FROM #t
LEFT JOIN deleteme ON deleteme.id = #t.id
WHERE deleteme.id IS NULL;
Run Code Online (Sandbox Code Playgroud)
...将在并发场景中因主键冲突而失败(即,当同时从多个线程插入 deleteme 中不存在的相同键时),默认事务隔离级别为READ COMMITTED:
错误:违反 PRIMARY KEY 约束“PK_DeleteMe”。无法在对象“dbo.deleteme”中插入重复键。
防止这种情况的最佳方法是什么?
该表看起来像这样:
CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] PRIMARY KEY ([id] ASC));
Run Code Online (Sandbox Code Playgroud)
更新
来自评论:
为什么你有多个会话,显然可以从某个地方提取相同的 id,使用相同的永久表而没有某种会话密钥来区分它们?即使没有重复错误,您如何知道哪些行属于哪个会话?
这是一个由填充此表中数据的外部服务调用的存储过程。该服务生成记录 ID,但不保证不会两次发送相同的数据。或者同时发送相同的数据。
数据库代码应该丢弃所有具有相同 id 的记录,如果一个记录已经存在。该服务绝不会在同一批次中发送两个具有相同 ID 的记录。
我正在查看死锁,当两个进程在同一个表上执行 UPDATE 语句时会发生这种情况。这两个事务都是隐式的,这意味着它们仅跨越当前语句,这反过来意味着我看到的锁不是先前语句的结转。
死锁涉及两个锁和两个进程。两个锁之一是表中的页锁(一个进程有更新锁,另一个需要意图排他锁),第二个是表聚簇索引上的键锁(一个进程有排他锁,另一个进程需要更新锁)。
现在,在两个 UPDATE 语句中更新的字段都不是聚集索引的一部分。
不过,我想知道这个聚集索引有大量重复值的事实是否会导致死锁?
我很抱歉没有发布死锁跟踪、更新语句、表和索引的定义,有太多需要混淆的地方。
我的问题不是如何解决我的死锁,我的问题是关于原则上低基数聚集索引对死锁概率的影响。
是否可以创建一个存储过程,在与存储过程本身所在的数据库不同的数据库中创建表触发器 (DDL) 。数据库位于同一服务器实例上。如果是,那么如何?
例如,这不起作用:
create PROCEDURE [dbo].[CreateTriggrer]
@db varchar(60)
AS
BEGIN
SET NOCOUNT ON;
declare @statement nvarchar(max) = N'CREATE TRIGGER [dbo].[TestTrigger]
ON [$database$].[dbo].[TestTable]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
PRINT ''test''
END'
set @statement = REPLACE(@statement,'$database$',@db)
EXEC dbo.sp_executesql @statement = @statement
END
Run Code Online (Sandbox Code Playgroud)
当这样调用时:
EXEC [dbo].[CreateTriggrer] @db = N'TestDatabase'
Run Code Online (Sandbox Code Playgroud)
它返回此错误:
消息 2108,级别 15,状态 1,过程 TestTrigger,第 6 行
无法在“TestDatabase.dbo.TestTable”上创建触发器,因为目标不在当前数据库中。
这是公平的。有没有办法实现我想要的?
sql-server ×11
security ×2
concurrency ×1
dynamic-sql ×1
foreign-key ×1
performance ×1
statistics ×1
t-sql ×1
transaction ×1