AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / user-57105

Vladimir Baranov's questions

Martin Hope
Vladimir Baranov
Asked: 2024-07-31 18:05:55 +0800 CST

用户无法访问数据库,因为他属于多个 AD 组

  • 6

我们有一个 SQL Server 2016,上面有 20 个不同的数据库,供公司内的不同应用程序和不同团队使用。Active Directory 中有用户组,这些用户组将负责某些项目的员工组合在一起。问题是一名员工可以同时成为多个团队的成员。同一个人可以属于多个不同的 AD 组。

一位用户抱怨他无法访问某个数据库。当他尝试刷新查询该数据库的 Power BI 报告时,他收到一条错误消息“我们无法使用提供的凭据进行身份验证”。

下面是一张简化的图片。一台服务器有两个数据库:DB1和。SQL Server 中DB2有两个 AD 组App_Excel_Reporter和以及两个相应的登录名:DB2_User

CREATE LOGIN [LegacyDomain\App_Excel_Reporter] FROM WINDOWS WITH DEFAULT_DATABASE=[master]

CREATE LOGIN [Domain\DB2_User] FROM WINDOWS WITH DEFAULT_DATABASE=[master]

不确定这是否重要,但该服务器是主公司收购的另一家公司的旧服务器。因此,这台 SQL Server 计算机位于域中LegacyDomain。

两个 AD 组LegacyDomain\App_Excel_Reporter都有Domain\DB2_User自己的用户集,但一个用户同时Jack.Universal属于这两个 AD 组。

属于 的用户App_Excel_Reporter应该有权访问DB1。 属于 的用户DB2_User应该有权访问DB2。

其中DB1有一个用户映射到相应的登录名:

USE [DB1]
GO
CREATE USER [LegacyDomain\App_Excel_Reporter] FOR LOGIN [LegacyDomain\App_Excel_Reporter] WITH DEFAULT_SCHEMA=[dbo]
GO

其中DB2有一个用户映射到相应的登录名:

USE [DB2]
GO
CREATE USER [Domain\DB2_User] FOR LOGIN [Domain\DB2_User] WITH DEFAULT_SCHEMA=[dbo]
GO

当用户Jack.Universal尝试刷新 Power BI 时,他会在笔记本电脑上登录 Windows,并且Domain\Jack.Universal我在 SQL Server 日志中看到具有相同时间戳的这些消息:

Login succeeded for user 'Domain\Jack.Universal'. Connection made using Windows authentication, [CLIENT: <ip address>]
Error: 18456, Severity: 14, State: 38.
Login failed for user 'Domain\Jack.Universal'. Reason: Failed to open the explicitly specified database 'DB2'. [CLIENT: <ip address>]

当我跑步时

xp_logininfo 'Domain\Jack.Universal', @option = 'all'

表明

+-----------------------+------+-----------+-----------------------+---------------------------------+
|     account name      | type | privilege |   mapped login name   |         permission path         |
+-----------------------+------+-----------+-----------------------+---------------------------------+
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | LegacyDomain\App_Excel_Reporter |
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | Domain\DB2_User                 |
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | Domain\DB3_User                 |
+-----------------------+------+-----------+-----------------------+---------------------------------+

(是的,有两个以上的数据库,并且该用户属于三个 AD 组)

如果我跑

xp_logininfo 'Domain\Jack.Universal'

如果没有选项“all”,则只返回第一行:

+-----------------------+------+-----------+-----------------------+---------------------------------+
|     account name      | type | privilege |   mapped login name   |         permission path         |
+-----------------------+------+-----------+-----------------------+---------------------------------+
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | LegacyDomain\App_Excel_Reporter |
+-----------------------+------+-----------+-----------------------+---------------------------------+

Domain\Jack.Universal因此,当 Windows 用户登录到 SQL Server 时,SQL Server似乎会选择登录LegacyDomain\App_Excel_Reporter来让该用户进入,但是当它尝试访问数据库时,DB2此尝试会失败,因为登录LegacyDomain\App_Excel_Reporter仅映射到中的用户DB1。

“明确指定的数据库”一定是由于 Power BI 的连接字符串明确指定了DB2。

我们如何配置,以便属于两个 AD 组的用户能够访问两个数据库?仅属于一个 AD 组的用户只能访问相应的数据库。

我不是域管理员,但我可以向他们询问任何需要的信息。公司没有 DBA,我只是一名对 SQL Server 略知一二的程序员。


我挖得更深了一点。当我跑

EXEC xp_logininfo 'Domain\DB2_User', @option = 'members'

我获得了该组中的用户列表:

+-----------------------+------+-----------+-----------------------+-----------------+
|     account name      | type | privilege |   mapped login name   | permission path |
+-----------------------+------+-----------+-----------------------+-----------------+
| Domain\user1          | user | user      | Domain\user1          | Domain\DB2_User |
| Domain\user2          | user | user      | Domain\user2          | Domain\DB2_User |
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | Domain\DB2_User |
+-----------------------+------+-----------+-----------------------+-----------------+

并且用户也Domain正如预期的那样。

当我跑步时

EXEC xp_logininfo 'LegacyDomain\App_Excel_Reporter', @option = 'members'

我明白了:

+-----------------------------+------+-----------+-----------------------------+---------------------------------+
|        account name         | type | privilege |      mapped login name      |         permission path         |
+-----------------------------+------+-----------+-----------------------------+---------------------------------+
| LegacyDomain\user3          | user | user      | LegacyDomain\user3          | LegacyDomain\App_Excel_Reporter |
| LegacyDomain\user4          | user | user      | LegacyDomain\user4          | LegacyDomain\App_Excel_Reporter |
| LegacyDomain\Jack.Universal | user | user      | LegacyDomain\Jack.Universal | LegacyDomain\App_Excel_Reporter |
+-----------------------------+------+-----------+-----------------------------+---------------------------------+

这里权限路径在LegacyDomain,账户名也在LegacyDomain,但是上面的结果中xp_logininfo 'Domain\Jack.Universal'账户名在Domain,但是权限路径在LegacyDomain。

再次:

EXEC xp_logininfo 'Domain\Jack.Universal', @option = 'all'

+-----------------------+------+-----------+-----------------------+---------------------------------+
|     account name      | type | privilege |   mapped login name   |         permission path         |
+-----------------------+------+-----------+-----------------------+---------------------------------+
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | LegacyDomain\App_Excel_Reporter |
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | Domain\DB2_User                 |
| Domain\Jack.Universal | user | user      | Domain\Jack.Universal | Domain\DB3_User                 |
+-----------------------+------+-----------+-----------------------+---------------------------------+
EXEC xp_logininfo 'LegacyDomain\Jack.Universal', @option = 'all'

+-----------------------------+------+-----------+-----------------------------+---------------------------------+
|        account name         | type | privilege |      mapped login name      |         permission path         |
+-----------------------------+------+-----------+-----------------------------+---------------------------------+
| LegacyDomain\Jack.Universal | user | user      | LegacyDomain\Jack.Universal | LegacyDomain\App_Excel_Reporter |
+-----------------------------+------+-----------+-----------------------------+---------------------------------+

Active Directory 中一定存在一些神奇的功能,可以将用户从旧域映射到主域。

sql-server
  • 2 个回答
  • 65 Views
Martin Hope
Vladimir Baranov
Asked: 2024-07-10 15:11:42 +0800 CST

如何“本地”针对 AWS RDS 运行查询?

  • 5

我们公司在 AWS 上有一个 Postgres 数据库的 RDS 实例。我对 AWS 不太熟悉。

我想对数据库 (VACUUM FULL) 运行一个非常长的查询,这可能需要几个小时甚至几天的时间。我可以使用名为“passport”的工具创建到 AWS 的隧道,从我的笔记本电脑连接到数据库。

问题是,如果我从笔记本电脑发起查询,然后我的互联网连接断开,或者笔记本电脑重新启动,或者我的笔记本电脑和亚马逊计算机之间的网络发生其他事情,查询将被中断,我将丢失查询可能生成的任何输出。

在过去的美好时光里,当我拥有一台运行操作系统的服务器时,我可以连接到该操作系统(远程桌面或 ssh 或其他方式),然后在该服务器上运行一个程序(pgAdmin、psql 等),该程序将本地连接到数据库,即“localhost”。然后,我可以让它继续运行,断开与服务器的连接,第二天再回来,重新连接到它,并查看仍在该服务器上运行的 pgAdmin 实例中的结果。在这种情况下,运行查询的应用程序和数据库之间没有网络。

使用 RDS 有可能实现类似的功能吗?怎样实现?

postgresql
  • 1 个回答
  • 27 Views
Martin Hope
Vladimir Baranov
Asked: 2022-10-06 22:30:11 +0800 CST

Postgres 可以向后扫描索引吗?

  • 10

我们使用 Amazon RDS 实例

x86_64-pc-linux-gnu 上的 PostgreSQL 11.13,由 gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-12) 编译,64 位

我有一个简单的经典 top-1-per-group 查询。我需要为每个creativeScheduleId.

这是一个表和索引定义:

CREATE TABLE IF NOT EXISTS public.creative_schedule_status_histories (
  id serial PRIMARY KEY,
  "creativeScheduleId" text NOT NULL,
  -- other columns
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_creativescheduleid_id
  ON public.creative_schedule_status_histories ("creativeScheduleId" ASC, id ASC);

当id ASC引擎的查询命令只读取索引而不做任何额外的排序时:

EXPLAIN (ANALYZE) 
SELECT history.id, history."creativeScheduleId"
FROM  (
    SELECT cssh.id, cssh."creativeScheduleId"
         , ROW_NUMBER() OVER (PARTITION BY cssh."creativeScheduleId"
                              ORDER BY cssh.id ASC) AS rn  -- !
    FROM creative_schedule_status_histories as cssh
    ) AS history
WHERE history.rn = 1;
"Subquery Scan on history  (cost=0.56..511808.63 rows=26377 width=41) (actual time=0.047..4539.058 rows=709030 loops=1)"
"  Filter: (history.rn = 1)"
"  Rows Removed by Filter: 4579766"
"  ->  WindowAgg  (cost=0.56..445866.24 rows=5275391 width=49) (actual time=0.046..4165.835 rows=5288796 loops=1)"
"        ->  Index Only Scan using idx_creativescheduleid_id on creative_schedule_status_histories cssh  (cost=0.56..353546.90 rows=5275391 width=41) (actual time=0.037..1447.490 rows=5288796 loops=1)"
"              Heap Fetches: 2372"
"Planning Time: 0.072 ms"
"Execution Time: 4568.235 ms"

当我订购时,我希望看到完全相同的查询计划id DESC,但是计划中有一个明确的排序溢出到磁盘,显然一切都变慢了。

EXPLAIN (ANALYZE) 
SELECT history.id, history."creativeScheduleId"
FROM  (
    SELECT cssh.id, cssh."creativeScheduleId"
         , ROW_NUMBER() OVER (PARTITION BY cssh."creativeScheduleId"
                              ORDER BY cssh.id DESC) AS rn  -- !
    FROM creative_schedule_status_histories as cssh
    ) AS history
WHERE history.rn = 1;
"Subquery Scan on history  (cost=1267132.63..1438582.84 rows=26377 width=41) (actual time=11974.827..15840.338 rows=709046 loops=1)"
"  Filter: (history.rn = 1)"
"  Rows Removed by Filter: 4579802"
"  ->  WindowAgg  (cost=1267132.63..1372640.45 rows=5275391 width=49) (actual time=11974.825..15529.679 rows=5288848 loops=1)"
"        ->  Sort  (cost=1267132.63..1280321.11 rows=5275391 width=41) (actual time=11974.814..13547.038 rows=5288848 loops=1)"
"              Sort Key: cssh.""creativeScheduleId"", cssh.id DESC"
"              Sort Method: external merge  Disk: 263992kB"
"              ->  Index Only Scan using idx_creativescheduleid_id on creative_schedule_status_histories cssh  (cost=0.56..353550.90 rows=5275391 width=41) (actual time=0.015..1386.310 rows=5288848 loops=1)"
"                    Heap Fetches: 2508"
"Planning Time: 0.078 ms"
"Execution Time: 15949.877 ms"

我希望给定的索引在查询的两个变体中同样有用。
Postgres 不能在这里向后扫描索引?
我在这里想念什么?


当我对特定的给定进行查询时creativeScheduleId,Postgres 对索引ASC和DESC排序顺序都同样有效。在任何变体中都没有明确的排序:

EXPLAIN (ANALYZE) 
SELECT id, "creativeScheduleId"
FROM   creative_schedule_status_histories AS cssh
WHERE  "creativeScheduleId" = '24238370-a64c-4b30-ac8e-27eb2b693aca'
ORDER  BY id DESC  -- or ASC, no sort
LIMIT  1
"Limit  (cost=0.56..0.71 rows=1 width=41) (actual time=0.022..0.022 rows=1 loops=1)"
"  ->  Index Only Scan Backward using idx_creativescheduleid_id on creative_schedule_status_histories cssh  (cost=0.56..14.06 rows=86 width=41) (actual time=0.021..0.021 rows=1 loops=1)"
"        Index Cond: (""creativeScheduleId"" = '24238370-a64c-4b30-ac8e-27eb2b693aca'::text)"
"        Heap Fetches: 0"
"Planning Time: 0.064 ms"
"Execution Time: 0.033 ms"

在这里我们实际上看到Index Only Scan Backward了,所以 Postgres 能够做到。但不适用于整张桌子。

任何想法如何鼓励引擎为读取整个表的第一个查询向后扫描整个索引?

postgresql
  • 3 个回答
  • 130 Views
Martin Hope
Vladimir Baranov
Asked: 2021-03-19 18:18:07 +0800 CST

如何提示远程扫描操作员估计超过 10000 行?

  • 2

我需要从链接服务器读取数据并插入到本地表中。我需要删除数据中的重复项,并且我需要在本地服务器上执行此操作,因为远程服务器已超载。因此,我添加了DISTINCT按照我想要的方式执行 Distinct Sort 的子句。

问题是 Remote Scan 算子总是估计行数为 10000,而实际行数约为 3M。因此,排序溢出到磁盘并变得很慢。

如果有办法向优化器提示实际行数远远超过 10K?

我应该将原始数据加载到本地临时表中,然后从本地表中运行 DISTINCT 吗?我不想两次写入磁盘。

重复的行数很少——3M 中只有几百行。我的意思是,在删除重复项之前,大约有 3,000,000 行;删除重复项后,大约有 2,999,800 行。因此,删除远程服务器上的重复数据不会显着减少通过网络传输的数据量。

目标表在插入之前被截断,所以我总是插入一个空表。此外,目标表没有任何索引、触发器或约束。表中有很多列。大约 110 列。在下面的查询中,我ManyManyColumns改为写了。

查询:

WITH
CTE_Raw
AS
(
SELECT
    [ManyManyColumns]
FROM OpenQuery([remote_server],'
SELECT
    [ManyManyColumns]
FROM
    [DB].[dbo].[remote_view]
')
)
,CTE_Converted
AS
(
    SELECT DISTINCT
        [ManyManyColumns]
    FROM
        CTE_Raw
)
INSERT INTO [dbo].[TestVBFast2]
    ([ManyManyColumns]
    )
SELECT
    [ManyManyColumns]
FROM
    CTE_Converted
;

执行计划

远程扫描

SQL Server 版本:

Microsoft SQL Server 2012 (SP4) (KB4018073) - 11.0.7001.0 (X64)
        2017 年 8 月 15 日 10:23:29
        版权所有 (c) 微软公司
        Windows NT 6.3(内部版本 9600:)(管理程序)上的标准版(64 位)

我想在DISTINCT本地执行,因为远程服务器超载,我想减少它的负载。DISTINCT将仅删除 3M 中的几百行,因此通过网络传输的数据量不会有太大变化。

sql-server sql-server-2012
  • 2 个回答
  • 310 Views
Martin Hope
Vladimir Baranov
Asked: 2021-03-18 19:31:17 +0800 CST

部分用户无法通过“用户 NT AUTHORITY\ANONYMOUS LOGON 登录失败”查询链接服务器

  • 1

当我尝试运行涉及链接服务器的简单查询时,它失败了:

SELECT * FROM [server2].[DWH].[dbo].[SomeTable]

Msg 18456, Level 14, State 1, Line 1
Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'.

但是,还有另一个用户可以毫无问题地运行此查询。


我们公司没有 SQL Server DBA,收购后我们从另一家公司继承了一些 SQL Server。

我是一名尝试运行一些查询的开发人员,我很难弄清楚如何正确配置访问。我真的不知道在哪里看,所以我会尽我所能解释当前的设置。

有Server1:

Microsoft SQL Server 2012 (SP4) (KB4018073) - 11.0.7001.0 (X64) 
    Aug 15 2017 10:23:29 
    Copyright (c) Microsoft Corporation
    Standard Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)

有Server2:

Microsoft SQL Server 2016 (SP1-CU15-GDR) (KB4505221) - 13.0.4604.0 (X64) 
    Jun 15 2019 07:56:34 
    Copyright (c) Microsoft Corporation
    Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2016 Datacenter 10.0 <X64> (Build 14393: ) (Hypervisor)

我们公司有一个域MAIN_DOMAIN,我以MAIN_DOMAIN\my.name. 当我在笔记本电脑上运行 SSMS 时,我可以同时连接到两者Server1并Server2使用 Windows 身份验证。

据我了解,我在两个 SQL Server 中的登录几乎都具有所有权限:

服务器1:

CREATE LOGIN [MAIN_DOMAIN\my.name] FROM WINDOWS WITH DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english]
ALTER SERVER ROLE [sysadmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [serveradmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [setupadmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [processadmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [diskadmin] ADD MEMBER [MAIN_DOMAIN\my.name]

服务器2:

CREATE LOGIN [MAIN_DOMAIN\my.name] FROM WINDOWS WITH DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english]
ALTER SERVER ROLE [sysadmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [serveradmin] ADD MEMBER [MAIN_DOMAIN\my.name]
ALTER SERVER ROLE [setupadmin] ADD MEMBER [MAIN_DOMAIN\my.name]

Server1 和 Server2 本身不在MAIN_DOMAIN,它们在other_domain.com

other_domain\my.name我可以使用与我的主域用户密码不同的用户远程桌面到他们两个。

这是链接服务器的配置方式Server1:

EXEC master.dbo.sp_addlinkedserver @server = N'server2', @srvproduct=N'SQL Server'
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'server2',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL

EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'collation compatible', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'data access', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'dist', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'pub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'rpc', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'rpc out', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'sub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'connect timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'collation name', @optvalue=null
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'lazy schema validation', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'query timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'use remote collation', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'server2', @optname=N'remote proc transaction promotion', @optvalue=N'true'

在Server1MSSQLSERVER 服务 (sqlservr.exe) 上运行在名为NT Service\MSSQLSERVER.

在Server2MSSQLSERVER 服务 (sqlservr.exe) 上运行在名为NT Service\MSSQLSERVER.

sql server 服务用户


这是 的输出setspn -l。server1当我在or上运行它时,我得到相同的输出server2:

C:\Windows\system32>setspn -l SERVER2
Registered ServicePrincipalNames for CN=SERVER2,OU=Azure Resources,OU=Corporate,DC=other_domain,DC=com,DC=au:
        MSSQLSvc/SERVER2.other_domain.com.au:1433
        MSSQLSvc/SERVER2.other_domain.com.au
        WSMAN/SERVER2
        WSMAN/SERVER2.other_domain.com.au
        TERMSRV/SERVER2
        TERMSRV/SERVER2.other_domain.com.au
        RestrictedKrbHost/SERVER2
        HOST/SERVER2
        RestrictedKrbHost/SERVER2.other_domain.com.au
        HOST/SERVER2.other_domain.com.au

C:\Windows\system32>setspn -l SERVER1
Registered ServicePrincipalNames for CN=SERVER1,OU=Azure Resources,OU=Corporate,DC=other_domain,DC=com,DC=au:
        MSSQLSvc/SERVER1.other_domain.com.au:1433
        MSSQLSvc/SERVER1.other_domain.com.au
        Microsoft Virtual Console Service/SERVER1.other_domain.com.au
        Microsoft Virtual Console Service/SERVER1
        Microsoft Virtual System Migration Service/SERVER1.other_domain.com.au
        Microsoft Virtual System Migration Service/SERVER1
        Hyper-V Replica Service/SERVER1.other_domain.com.au
        Hyper-V Replica Service/SERVER1
        WSMAN/SERVER1
        WSMAN/SERVER1.other_domain.com.au
        TERMSRV/SERVER1.other_domain.com.au
        TERMSRV/SERVER1
        RestrictedKrbHost/SERVER1
        HOST/SERVER1
        RestrictedKrbHost/SERVER1.other_domain.com.au
        HOST/SERVER1.other_domain.com.au

不幸的是,我不明白这意味着什么。我在这里唯一能理解的是MAIN_DOMAIN在任何地方都没有提到。


其他用户可以毫无问题地运行涉及链接服务器的查询。

他使用远程桌面登录Server1并使用other_domain\his.name. 他在上面运行 SSMSServer1并使用 Windows 身份验证连接到 SQL Server。

他的登录Server1也具有所有权限:

CREATE LOGIN [other_domain\his.name] FROM WINDOWS WITH DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english]
ALTER SERVER ROLE [sysadmin] ADD MEMBER [other_domain\his.name]
ALTER SERVER ROLE [serveradmin] ADD MEMBER [other_domain\his.name]

我需要配置什么,以便在我使用笔记本电脑连接到 SQL Server 时运行涉及链接服务器的查询MAIN_DOMAIN\my.name?

sql-server linked-server
  • 1 个回答
  • 1739 Views
Martin Hope
Vladimir Baranov
Asked: 2021-02-16 02:50:46 +0800 CST

在数据库备份之前和之后缩小事务日志文件的原因可能是什么?

  • 0

我是一个偶然的 DBA。我是一家公司中唯一一个对 SQL Server 有一定经验的人,我必须处理从另一家公司继承的系统在被收购后。最初设置的人不再可用。

服务器版本:Microsoft SQL Server 2016 (SP1-CU15-GDR) (KB4505221) - 13.0.4604.0 (X64) Jun 15 2019 07:56:34 版权所有 (c) Microsoft Corporation Enterprise Edition:基于内核的许可(64 位)在 Windows Server 2016 Datacenter 10.0(内部版本 14393:)(管理程序)上

当我查看服务器时,首先引起我注意的事情之一是一个 SQL Server 代理作业,它对主数据库进行备份。数据库处于简单恢复模式。这项工作分为三个步骤:

-- step 1
DBCC SHRINKFILE (N'DWH_Main_log' , 0)

-- step 2
declare @disk varchar(100) = replace(N'G:\DB_Backup\DWH_Main_&d&.bak','&d&', convert(varchar, getdate(), 112))
--print @disk
;
BACKUP DATABASE [DWH_Main] 
    TO  DISK = @disk
    WITH  RETAINDAYS = 2
        , NOFORMAT
        , NOINIT
        ,  NAME = N'DWH_Main-Full Database Backup'
        , SKIP
        , NOREWIND
        , NOUNLOAD
        , COMPRESSION
        ,  STATS = 10
GO

-- step 3
WAITFOR DELAY '00:05'
;
DBCC SHRINKFILE (N'DWH_Main_log' , 0)

数据文件大小约为 400GB,目前的日志文件大小约为 50GB。

CREATE DATABASE [DWH_Main]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'DWH_Main', FILENAME = N'E:\Data\DWH_Main.mdf' , SIZE = 411114496KB , 
  MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'DWH_Main_log', FILENAME = N'F:\Log\DWH_Main_log.ldf' , SIZE = 53941184KB , 
  MAXSIZE = 2048GB , FILEGROWTH = 65536KB )

我可以看到很多日志文件自动增长事件,这并不奇怪。DBCC LOGINFO返回 858 行,即 858 个片段,几乎每个片段 64MB。

对于这种情况,我应该对我的老板说些什么?我不明白为什么有人会希望在完整备份后每天都缩小事务日志文件,尤其是对于处于简单恢复模式的数据库。

我的第一反应是简单地DBCC SHRINKFILE从脚本中删除命令,然后重新配置事务日志文件的初始大小。以 8GB 块手动将其增长到 ~64GB 以减少碎片,但也许我遗漏了什么?

sql-server sql-server-2016
  • 2 个回答
  • 79 Views
Martin Hope
Vladimir Baranov
Asked: 2021-02-15 18:10:19 +0800 CST

无法从链接服务器的 OLE DB 提供程序“SQLNCLI11”获取行

  • 1

我在云中某处(可能是 Azure)的两台不同机器上有两个 SQL Server。

一种是 Microsoft SQL Server 2012 (SP3-CU10) (KB4025925) - 11.0.6607.3 (X64) Jul 8 2017 16:43:40 版权所有 (c) Microsoft Corporation Standard Edition (64-bit) o​​n Windows NT 6.3 (Build 9600: )(管理程序)

在这台服务器上有一个到第二台服务器的链接。

第二台服务器 ( aae-sqldw-02) 是 Microsoft SQL Server 2016 (SP1-CU15-GDR) (KB4505221) - 13.0.4604.0 (X64) Jun 15 2019 07:56:34 版权所有 (c) Microsoft Corporation Enterprise Edition:基于内核的许可 (64 -bit) 在 Windows Server 2016 Datacenter 10.0 (Build 14393:) (Hypervisor) 上

在第一台服务器上,我们正在运行“简单”查询:

TRUNCATE TABLE [dbo].[LocalTable]

INSERT INTO [dbo].[LocalTable]
    ([DatabaseName]
    ,[SalesContractNumber]
    ,... 60 columns
    )
SELECT
    convert(varchar(128), DatabaseName) collate Latin1_General_CI_AS
    ,convert(varchar(60), SalesContractNumber) collate Latin1_General_CI_AS
    ,... 60 columns
FROM [aae-sqldw-02].[Fin_DWH].[dbo].[RemoteView]
WHERE DatabaseName = 'somename'

此查询有时会失败并出现错误:

Cannot fetch a row from OLE DB provider "SQLNCLI11" for linked server "aae-sqldw-02".

或出现此错误:

Cannot fetch the rowset from OLE DB provider "SQLNCLI11" for linked server "aae-sqldw-02". .

我知道第二台服务器一天中的大部分时间都承受着非常重的负载。它实际上最大化了它的磁盘 IO(255MB/秒)。蛮力解决方案是简单地将其移至具有更多 IO 的更昂贵的计划。这种变化需要大量的官僚主义,并且需要很长时间。此外,不能保证下一层就足够了。

服务器负载

我现在可以用给定的资源做些什么吗?

查询成功完成后,可能需要 1-3 小时。该查询返回大约 3M 行,大约 4GB 的数据,所以不会太多。

当查询失败时Cannot fetch a row,最后几次在 9294 秒(2.5 小时)、12326 秒(3.5 小时)后失败。

当查询失败时Cannot fetch the rowset,它在 606 秒、611 秒后失败。

因此,600 秒建议一些默认的 10 分钟超时(用于连接?)在连接成功的情况下,它开始获取数据,但在此过程中失败。也许链接服务器无法足够快地发送下一行,并且出现了其他一些超时。

查询成功时,上次耗时 3841 秒。

以下是链接服务器的设置:

EXEC master.dbo.sp_addlinkedserver @server = N'aae-sqldw-02', @srvproduct=N'SQL Server'
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'aae-sqldw-02',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL
GO

EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'collation compatible', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'data access', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'dist', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'pub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'rpc', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'rpc out', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'sub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'connect timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'collation name', @optvalue=null
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'lazy schema validation', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'query timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'use remote collation', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'aae-sqldw-02', @optname=N'remote proc transaction promotion', @optvalue=N'true'

query timeout你怎么看,如果我明确地将选项设置为 5 小时,会有什么不同吗?它能让事情变得更糟吗?

显然,正确的解决方法是查看服务器上正在发生的事情并优化查询以减少整体负载,但是我可以在更高的服务器/数据库级别做些什么,以便查询完成,即使真的需要很长时间吗?

我们需要每周运行一次这个查询,现在我们必须重试几次,直到它成功完成。

sql-server sql-server-2012
  • 1 个回答
  • 3756 Views
Martin Hope
Vladimir Baranov
Asked: 2016-07-15 01:31:30 +0800 CST

为每个更改的行增加一个计数器

  • 8

我正在使用没有SEQUENCE功能的 SQL Server 2008 Standard。

外部系统从主数据库的几个专用表中读取数据。外部系统保留数据副本并定期检查数据的更改并刷新其副本。

为了使同步有效,我只想传输自上次同步以来更新或插入的行。(这些行永远不会被删除)。要知道自上次同步以来更新或插入了哪些行,每个表中都有一bigint列RowUpdateCounter。

这个想法是,每当插入或更新一行时,其RowUpdateCounter列中的数字就会改变。进入该RowUpdateCounter列的值应取自不断增加的数字序列。列中的值RowUpdateCounter应该是唯一的,并且存储在表中的每个新值都应该大于任何先前的值。

请查看显示所需行为的脚本。

架构

CREATE TABLE [dbo].[Test](
    [ID] [int] NOT NULL,
    [Value] [varchar](50) NOT NULL,
    [RowUpdateCounter] [bigint] NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
    [ID] ASC
))
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_RowUpdateCounter] ON [dbo].[Test]
(
    [RowUpdateCounter] ASC
)
GO

插入一些行

INSERT INTO [dbo].[Test]
    ([ID]
    ,[Value]
    ,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);

预期结果

+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
|  1 | A     |                1 |
|  2 | B     |                2 |
|  3 | C     |                3 |
|  4 | D     |                4 |
+----+-------+------------------+

中生成的值RowUpdateCounter可以不同,例如5, 3, 7, 9. 它们应该是唯一的并且应该大于 0,因为我们是从空表开始的。

插入和更新一些行

DECLARE @NewValues TABLE (ID int NOT NULL, Value varchar(50));
INSERT INTO @NewValues (ID, Value) VALUES
(3, 'E'),
(4, 'F'),
(5, 'G'),
(6, 'H');

MERGE INTO dbo.Test WITH (HOLDLOCK) AS Dst
USING
(
    SELECT ID, Value
    FROM @NewValues
)
AS Src ON Dst.ID = Src.ID
WHEN MATCHED THEN
UPDATE SET
     Dst.Value            = Src.Value
    ,Dst.RowUpdateCounter = ???
WHEN NOT MATCHED BY TARGET THEN
INSERT
    (ID
    ,Value
    ,RowUpdateCounter)
VALUES
    (Src.ID
    ,Src.Value
    ,???)
;

预期结果

+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
|  1 | A     |                1 |
|  2 | B     |                2 |
|  3 | E     |                5 |
|  4 | F     |                6 |
|  5 | G     |                7 |
|  6 | H     |                8 |
+----+-------+------------------+
  • RowUpdateCounter具有 ID 的行1,2应保持原样,因为这些行没有更改。
  • RowUpdateCounter具有 ID 的行3,4应该更改,因为它们已更新。
  • RowUpdateCounter对于具有 ID 的行5,6应该更改,因为它们已被插入。
  • RowUpdateCounter对于所有更改的行应大于 4(RowUpdateCounter序列中的最后一个)。

将新值 ( 5,6,7,8) 分配给更改的行的顺序并不重要。新值可以有间隙,例如15,26,47,58,但它们不应减少。

数据库中有几个带有此类计数器的表。不管他们是否都使用单一的全局序列作为他们的数字,或者每个表都有自己的单独序列。


我不想使用带有日期时间戳的列而不是整数计数器,因为:

  • 服务器上的时钟可以向前和向后跳跃。特别是当它在虚拟机上时。

  • 系统函数返回的值SYSDATETIME对于所有受影响的行都是相同的。同步过程应该能够批量读取更改。例如,如果批量大小为 3 行,则在上述MERGE步骤之后,同步过程将只读行E,F,G。下次运行同步过程时,它将从 row 继续H。


我现在这样做的方式相当丑陋。

由于SEQUENCESQL Server 2008 中没有,因此我SEQUENCE通过一个专用表来模拟,IDENTITY如this answer所示。这本身就很丑陋,而且由于我需要一次生成的不是一个数字,而是一批数字,这一事实更加严重。

然后,我INSTEAD OF UPDATE, INSERT在每个表上都有一个触发器,RowUpdateCounter并在那里生成所需的数字集。

在INSERT,UPDATE和MERGE查询中,我设置RowUpdateCounter为 0,它被触发器中的正确值替换。上述???查询中的 是0。

它有效,但有更简单的解决方案吗?

sql-server sql-server-2008
  • 2 个回答
  • 2864 Views
Martin Hope
Vladimir Baranov
Asked: 2016-06-23 18:07:14 +0800 CST

ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) 不在 (A,B,C) 上使用索引

  • 15

考虑这两个函数:

ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)

ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)

据我了解,它们产生完全相同的结果。换句话说,您在PARTITION BY子句中列出列的顺序无关紧要。

如果有一个索引,(A,B,C)我希望优化器在两个变体中都使用这个索引。

但是,令人惊讶的是,优化器决定在第二个变体中做一个额外显式的排序。

我在 SQL Server 2008 Standard 和 SQL Server 2014 Express 上见过它。

这是我用来重现它的完整脚本。

已在 Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 2014 年 2 月 20 日 20:04:26 上试用 版权所有 (c) Microsoft Corporation Express Edition (64-bit) o​​n Windows NT 6.1 (Build 7601: Service Pack 1)

和 Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 2016 年 5 月 27 日 15:33:17 版权所有 (c) Microsoft Corporation Express Edition (64-bit) o​​n Windows NT 6.1 (Build 7601: Service包装 1)

OPTION (QUERYTRACEON 9481)通过使用和使用新旧基数估计器OPTION (QUERYTRACEON 2312)。

设置表、索引、样本数据

CREATE TABLE [dbo].[T](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [A] [int] NOT NULL,
    [B] [int] NOT NULL,
    [C] [int] NOT NULL,
    CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, 
STATISTICS_NORECOMPUTE = OFF, 
IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_ABC] ON [dbo].[T]
(
    [A] ASC,
    [B] ASC,
    [C] ASC
)WITH (PAD_INDEX = OFF, 
STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, 
ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON)
GO

INSERT INTO [dbo].[T] ([A],[B],[C]) VALUES
(10, 20, 30),
(10, 21, 31),
(10, 21, 32),
(10, 21, 33),
(11, 20, 34),
(11, 21, 35),
(11, 21, 36),
(12, 20, 37),
(12, 21, 38),
(13, 21, 39);

查询

SELECT -- AB
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);

SELECT -- BA
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);

SELECT -- both
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);

执行计划

A、B 分区

AB

B、A 分区

文学学士

两个都

两个都

如您所见,第二个计划有一个额外的排序。它按 B、A、C 订购。显然,优化器不够聪明,无法意识到这与数据PARTITION BY B,A相同PARTITION BY A,B并重新排序。

有趣的是,第三个查询有两个变体ROW_NUMBERin 它并且没有额外的排序!该计划与第一个查询相同。(序列项目在额外列的输出列表中有额外的表达式,但没有额外的排序)。因此,在这种更复杂的情况下,优化器似乎足够聪明,可以意识到PARTITION BY B,A与PARTITION BY A,B.

在第一个和第三个查询中,索引扫描运算符具有属性 Ordered:True,在第二个查询中为 False。

更有趣的是,如果我像这样重写第三个查询(交换两列):

SELECT -- both
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);

然后额外的排序再次出现!

有人可以解释一下吗?这里的优化器发生了什么?

sql-server sql-server-2008
  • 1 个回答
  • 12072 Views
Martin Hope
Vladimir Baranov
Asked: 2016-04-23 05:40:44 +0800 CST

将每日计划分组到 [开始日期; 结束日期] 与工作日列表的间隔

  • 18

我需要在两个系统之间转换数据。

第一个系统将日程表存储为一个简单的日期列表。计划中包含的每个日期都是一行。日期顺序中可能存在各种间隙(周末、公共假期和较长的停顿,一周中的某些日子可能会被排除在日程表之外)。完全不能有空档,甚至可以包括周末。时间表最长可达 2 年。通常是几周之久。

这是一个简单的计划示例,该计划跨越两周,不包括周末(下面的脚本中有更复杂的示例):

+----+------------+------------+---------+--------+
| ID | ContractID |     dt     | dowChar | dowInt |
+----+------------+------------+---------+--------+
| 10 |          1 | 2016-05-02 | Mon     |      2 |
| 11 |          1 | 2016-05-03 | Tue     |      3 |
| 12 |          1 | 2016-05-04 | Wed     |      4 |
| 13 |          1 | 2016-05-05 | Thu     |      5 |
| 14 |          1 | 2016-05-06 | Fri     |      6 |
| 15 |          1 | 2016-05-09 | Mon     |      2 |
| 16 |          1 | 2016-05-10 | Tue     |      3 |
| 17 |          1 | 2016-05-11 | Wed     |      4 |
| 18 |          1 | 2016-05-12 | Thu     |      5 |
| 19 |          1 | 2016-05-13 | Fri     |      6 |
+----+------------+------------+---------+--------+

ID是唯一的,但不一定是顺序的(它是主键)。每个合约中的日期都是唯一的(在 上有唯一的索引(ContractID, dt))。

第二个系统将时间表存储为带有作为时间表一部分的工作日列表的间隔。每个间隔由其开始日期和结束日期(包括)和日程表中包含的工作日列表定义。在这种格式中,您可以有效地定义重复的每周模式,例如周一至周三,但是当模式被打乱时(例如公共假期)就会变得很痛苦。

上面的简单示例如下所示:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          1 | 2016-05-02 | 2016-05-13 |       10 | Mon,Tue,Wed,Thu,Fri, |
+------------+------------+------------+----------+----------------------+

[StartDT;EndDT]属于同一合约的区间不应重叠。

我需要将来自第一个系统的数据转换为第二个系统使用的格式。目前,我正在 C# 中为单个给定合同在客户端解决此问题,但我想在服务器端的 T-SQL 中执行此操作,以便在服务器之间进行批量处理和导出/导入。最有可能的是,它可以使用 CLR UDF 来完成,但在这个阶段我不能使用 SQLCLR。

这里的挑战是使间隔列表尽可能短且人性化。

例如,这个时间表:

+-----+------------+------------+---------+--------+
| ID  | ContractID |     dt     | dowChar | dowInt |
+-----+------------+------------+---------+--------+
| 223 |          2 | 2016-05-05 | Thu     |      5 |
| 224 |          2 | 2016-05-06 | Fri     |      6 |
| 225 |          2 | 2016-05-09 | Mon     |      2 |
| 226 |          2 | 2016-05-10 | Tue     |      3 |
| 227 |          2 | 2016-05-11 | Wed     |      4 |
| 228 |          2 | 2016-05-12 | Thu     |      5 |
| 229 |          2 | 2016-05-13 | Fri     |      6 |
| 230 |          2 | 2016-05-16 | Mon     |      2 |
| 231 |          2 | 2016-05-17 | Tue     |      3 |
+-----+------------+------------+---------+--------+

应该变成这样:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          2 | 2016-05-05 | 2016-05-17 |        9 | Mon,Tue,Wed,Thu,Fri, |
+------------+------------+------------+----------+----------------------+

,不是这个:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          2 | 2016-05-05 | 2016-05-06 |        2 | Thu,Fri,             |
|          2 | 2016-05-09 | 2016-05-13 |        5 | Mon,Tue,Wed,Thu,Fri, |
|          2 | 2016-05-16 | 2016-05-17 |        2 | Mon,Tue,             |
+------------+------------+------------+----------+----------------------+

我试图对gaps-and-islands这个问题应用一种方法。我试着分两次做。在第一遍中,我找到了简单连续天的岛屿,即岛屿的末端是天序列中的任何间隙,无论是周末、公共假期还是其他什么。对于每个这样找到的岛,我建立一个以逗号分隔的 distinct 列表WeekDays。在第二遍中,我小组通过查看周数序列中的差距或WeekDays.

使用这种方法,每个部分周都会以一个额外的间隔结束,如上所示,因为即使周数是连续的,也会WeekDays发生变化。此外,一周内可能存在定期间隔(参见ContractID=3示例数据,其中仅包含 的数据Mon,Wed,Fri,),并且这种方法会在这样的时间表中为每一天生成单独的间隔。从好的方面来说,如果日程安排完全没有任何间隙(参见ContractID=7包含周末的示例数据),它会生成一个间隔,在这种情况下,开始周或结束周是否部分无关紧要。

请查看下面脚本中的其他示例,以更好地了解我所追求的。您可以看到,周末经常被排除在外,但一周中的任何其他日子也可能被排除在外。仅在示例 3Mon中,Wed并且Fri是计划的一部分。此外,还可以包括周末,如示例 7。该解决方案应平等对待一周中的所有日子。一周中的任何一天都可以包含在日程表中或排除在日程表之外。

要验证生成的间隔列表是否正确描述了给定的时间表,您可以使用以下伪代码:

  • 循环遍历所有间隔
  • 对于每个间隔,循环遍历开始日期和结束日期(包括)之间的所有日历日期。
  • 对于每个日期,检查其星期几是否列在WeekDays. 如果是,则该日期包含在日程表中。

希望这可以阐明在什么情况下应该创建新的间隔。在示例 4 和 52016-05-09中,从日程表的中间删除了一个星期一 ( ),这样的日程表不能由单个间隔表示。在示例 6 中,时间表中有很长的间隔,因此需要两个间隔。

间隔代表计划中的每周模式,当模式被中断/更改时,必须添加新的间隔。例 11 前三周有一个模式Tue,然后这个模式变为Thu。因此,我们需要两个间隔来描述这样的时间表。


我目前使用的是 SQL Server 2008,所以解决方案应该在这个版本中工作。如果 SQL Server 2008 的解决方案可以使用更高版本的功能进行简化/改进,那将是一个奖励,请同时展示它。

我有一个Calendar表格(日期列表)和Numbers表格(从 1 开始的整数列表),所以如果需要,可以使用它们。也可以创建临时表并有多个查询分阶段处理数据。但是,算法中的阶段数必须固定,游标和显式WHILE循环都不行。


示例数据和预期结果的脚本

-- @Src is sample data
-- @Dst is expected result

DECLARE @Src TABLE (ID int PRIMARY KEY, ContractID int, dt date, dowChar char(3), dowInt int);
INSERT INTO @Src (ID, ContractID, dt, dowChar, dowInt) VALUES

-- simple two weeks (without weekend)
(110, 1, '2016-05-02', 'Mon', 2),
(111, 1, '2016-05-03', 'Tue', 3),
(112, 1, '2016-05-04', 'Wed', 4),
(113, 1, '2016-05-05', 'Thu', 5),
(114, 1, '2016-05-06', 'Fri', 6),
(115, 1, '2016-05-09', 'Mon', 2),
(116, 1, '2016-05-10', 'Tue', 3),
(117, 1, '2016-05-11', 'Wed', 4),
(118, 1, '2016-05-12', 'Thu', 5),
(119, 1, '2016-05-13', 'Fri', 6),

-- a partial end of the week, the whole week, partial start of the week (without weekends)
(223, 2, '2016-05-05', 'Thu', 5),
(224, 2, '2016-05-06', 'Fri', 6),
(225, 2, '2016-05-09', 'Mon', 2),
(226, 2, '2016-05-10', 'Tue', 3),
(227, 2, '2016-05-11', 'Wed', 4),
(228, 2, '2016-05-12', 'Thu', 5),
(229, 2, '2016-05-13', 'Fri', 6),
(230, 2, '2016-05-16', 'Mon', 2),
(231, 2, '2016-05-17', 'Tue', 3),

-- only Mon, Wed, Fri are included across two weeks plus partial third week
(310, 3, '2016-05-02', 'Mon', 2),
(311, 3, '2016-05-04', 'Wed', 4),
(314, 3, '2016-05-06', 'Fri', 6),
(315, 3, '2016-05-09', 'Mon', 2),
(317, 3, '2016-05-11', 'Wed', 4),
(319, 3, '2016-05-13', 'Fri', 6),
(330, 3, '2016-05-16', 'Mon', 2),

-- a whole week (without weekend), in the second week Mon is not included
(410, 4, '2016-05-02', 'Mon', 2),
(411, 4, '2016-05-03', 'Tue', 3),
(412, 4, '2016-05-04', 'Wed', 4),
(413, 4, '2016-05-05', 'Thu', 5),
(414, 4, '2016-05-06', 'Fri', 6),
(416, 4, '2016-05-10', 'Tue', 3),
(417, 4, '2016-05-11', 'Wed', 4),
(418, 4, '2016-05-12', 'Thu', 5),
(419, 4, '2016-05-13', 'Fri', 6),

-- three weeks, but without Mon in the second week (no weekends)
(510, 5, '2016-05-02', 'Mon', 2),
(511, 5, '2016-05-03', 'Tue', 3),
(512, 5, '2016-05-04', 'Wed', 4),
(513, 5, '2016-05-05', 'Thu', 5),
(514, 5, '2016-05-06', 'Fri', 6),
(516, 5, '2016-05-10', 'Tue', 3),
(517, 5, '2016-05-11', 'Wed', 4),
(518, 5, '2016-05-12', 'Thu', 5),
(519, 5, '2016-05-13', 'Fri', 6),
(520, 5, '2016-05-16', 'Mon', 2),
(521, 5, '2016-05-17', 'Tue', 3),
(522, 5, '2016-05-18', 'Wed', 4),
(523, 5, '2016-05-19', 'Thu', 5),
(524, 5, '2016-05-20', 'Fri', 6),

-- long gap between two intervals
(623, 6, '2016-05-05', 'Thu', 5),
(624, 6, '2016-05-06', 'Fri', 6),
(625, 6, '2016-05-09', 'Mon', 2),
(626, 6, '2016-05-10', 'Tue', 3),
(627, 6, '2016-05-11', 'Wed', 4),
(628, 6, '2016-05-12', 'Thu', 5),
(629, 6, '2016-05-13', 'Fri', 6),
(630, 6, '2016-05-16', 'Mon', 2),
(631, 6, '2016-05-17', 'Tue', 3),
(645, 6, '2016-06-06', 'Mon', 2),
(646, 6, '2016-06-07', 'Tue', 3),
(647, 6, '2016-06-08', 'Wed', 4),
(648, 6, '2016-06-09', 'Thu', 5),
(649, 6, '2016-06-10', 'Fri', 6),
(655, 6, '2016-06-13', 'Mon', 2),
(656, 6, '2016-06-14', 'Tue', 3),
(657, 6, '2016-06-15', 'Wed', 4),
(658, 6, '2016-06-16', 'Thu', 5),
(659, 6, '2016-06-17', 'Fri', 6),

-- two weeks, no gaps between days at all, even weekends are included
(710, 7, '2016-05-02', 'Mon', 2),
(711, 7, '2016-05-03', 'Tue', 3),
(712, 7, '2016-05-04', 'Wed', 4),
(713, 7, '2016-05-05', 'Thu', 5),
(714, 7, '2016-05-06', 'Fri', 6),
(715, 7, '2016-05-07', 'Sat', 7),
(716, 7, '2016-05-08', 'Sun', 1),
(725, 7, '2016-05-09', 'Mon', 2),
(726, 7, '2016-05-10', 'Tue', 3),
(727, 7, '2016-05-11', 'Wed', 4),
(728, 7, '2016-05-12', 'Thu', 5),
(729, 7, '2016-05-13', 'Fri', 6),

-- no gaps between days at all, even weekends are included, with partial weeks
(805, 8, '2016-04-30', 'Sat', 7),
(806, 8, '2016-05-01', 'Sun', 1),
(810, 8, '2016-05-02', 'Mon', 2),
(811, 8, '2016-05-03', 'Tue', 3),
(812, 8, '2016-05-04', 'Wed', 4),
(813, 8, '2016-05-05', 'Thu', 5),
(814, 8, '2016-05-06', 'Fri', 6),
(815, 8, '2016-05-07', 'Sat', 7),
(816, 8, '2016-05-08', 'Sun', 1),
(825, 8, '2016-05-09', 'Mon', 2),
(826, 8, '2016-05-10', 'Tue', 3),
(827, 8, '2016-05-11', 'Wed', 4),
(828, 8, '2016-05-12', 'Thu', 5),
(829, 8, '2016-05-13', 'Fri', 6),
(830, 8, '2016-05-14', 'Sat', 7),

-- only Mon-Wed included, two weeks plus partial third week
(910, 9, '2016-05-02', 'Mon', 2),
(911, 9, '2016-05-03', 'Tue', 3),
(912, 9, '2016-05-04', 'Wed', 4),
(915, 9, '2016-05-09', 'Mon', 2),
(916, 9, '2016-05-10', 'Tue', 3),
(917, 9, '2016-05-11', 'Wed', 4),
(930, 9, '2016-05-16', 'Mon', 2),
(931, 9, '2016-05-17', 'Tue', 3),

-- only Thu-Sun included, three weeks
(1013,10,'2016-05-05', 'Thu', 5),
(1014,10,'2016-05-06', 'Fri', 6),
(1015,10,'2016-05-07', 'Sat', 7),
(1016,10,'2016-05-08', 'Sun', 1),
(1018,10,'2016-05-12', 'Thu', 5),
(1019,10,'2016-05-13', 'Fri', 6),
(1020,10,'2016-05-14', 'Sat', 7),
(1021,10,'2016-05-15', 'Sun', 1),
(1023,10,'2016-05-19', 'Thu', 5),
(1024,10,'2016-05-20', 'Fri', 6),
(1025,10,'2016-05-21', 'Sat', 7),
(1026,10,'2016-05-22', 'Sun', 1),

-- only Tue for first three weeks, then only Thu for the next three weeks
(1111,11,'2016-05-03', 'Tue', 3),
(1116,11,'2016-05-10', 'Tue', 3),
(1131,11,'2016-05-17', 'Tue', 3),
(1123,11,'2016-05-19', 'Thu', 5),
(1124,11,'2016-05-26', 'Thu', 5),
(1125,11,'2016-06-02', 'Thu', 5),

-- one week, then one week gap, then one week
(1210,12,'2016-05-02', 'Mon', 2),
(1211,12,'2016-05-03', 'Tue', 3),
(1212,12,'2016-05-04', 'Wed', 4),
(1213,12,'2016-05-05', 'Thu', 5),
(1214,12,'2016-05-06', 'Fri', 6),
(1215,12,'2016-05-16', 'Mon', 2),
(1216,12,'2016-05-17', 'Tue', 3),
(1217,12,'2016-05-18', 'Wed', 4),
(1218,12,'2016-05-19', 'Thu', 5),
(1219,12,'2016-05-20', 'Fri', 6);

SELECT ID, ContractID, dt, dowChar, dowInt
FROM @Src
ORDER BY ContractID, dt;


DECLARE @Dst TABLE (ContractID int, StartDT date, EndDT date, DayCount int, WeekDays varchar(255));
INSERT INTO @Dst (ContractID, StartDT, EndDT, DayCount, WeekDays) VALUES
(1, '2016-05-02', '2016-05-13', 10, 'Mon,Tue,Wed,Thu,Fri,'),
(2, '2016-05-05', '2016-05-17',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(3, '2016-05-02', '2016-05-16',  7, 'Mon,Wed,Fri,'),
(4, '2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(4, '2016-05-10', '2016-05-13',  4, 'Tue,Wed,Thu,Fri,'),
(5, '2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(5, '2016-05-10', '2016-05-20',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(6, '2016-05-05', '2016-05-17',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(6, '2016-06-06', '2016-06-17', 10, 'Mon,Tue,Wed,Thu,Fri,'),
(7, '2016-05-02', '2016-05-13', 12, 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,'),
(8, '2016-04-30', '2016-05-14', 15, 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,'),
(9, '2016-05-02', '2016-05-17',  8, 'Mon,Tue,Wed,'),
(10,'2016-05-05', '2016-05-22', 12, 'Sun,Thu,Fri,Sat,'),
(11,'2016-05-03', '2016-05-17',  3, 'Tue,'),
(11,'2016-05-19', '2016-06-02',  3, 'Thu,'),
(12,'2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(12,'2016-05-16', '2016-05-20',  5, 'Mon,Tue,Wed,Thu,Fri,');

SELECT ContractID, StartDT, EndDT, DayCount, WeekDays
FROM @Dst
ORDER BY ContractID, StartDT;

答案比较

真正的表@Src有不同的403,555行。所有答案都会产生正确的结果(至少对于我的数据而言),并且所有答案都相当快,但它们的最优性不同。生成的间隔越少越好。出于好奇,我将运行时间包括在内。主要关注的是正确和最佳的结果,而不是速度(除非花费太长时间 - 我在 10 分钟后停止了 Ziggy Crueltyfree Zeitgeister 的非递归查询)。15,857ContractIDs

+--------------------------------------------------------+-----------+---------+
|                         Answer                         | Intervals | Seconds |
+--------------------------------------------------------+-----------+---------+
| Ziggy Crueltyfree Zeitgeister                          |     25751 |    7.88 |
| While loop                                             |           |         |
|                                                        |           |         |
| Ziggy Crueltyfree Zeitgeister                          |     25751 |    8.27 |
| Recursive                                              |           |         |
|                                                        |           |         |
| Michael Green                                          |     25751 |   22.63 |
| Recursive                                              |           |         |
|                                                        |           |         |
| Geoff Patterson                                        |     26670 |    4.79 |
| Weekly gaps-and-islands with merging of partial weeks  |           |         |
|                                                        |           |         |
| Vladimir Baranov                                       |     34560 |    4.03 |
| Daily, then weekly gaps-and-islands                    |           |         |
|                                                        |           |         |
| Mikael Eriksson                                        |     35840 |    0.65 |
| Weekly gaps-and-islands                                |           |         |
+--------------------------------------------------------+-----------+---------+
| Vladimir Baranov                                       |     25751 |  121.51 |
| Cursor                                                 |           |         |
+--------------------------------------------------------+-----------+---------+
sql-server sql-server-2008
  • 7 个回答
  • 3792 Views
Martin Hope
Vladimir Baranov
Asked: 2016-03-16 16:56:03 +0800 CST

登录名只是 db_datareader 和公共角色的成员,但它不是只读的

  • 0

我是一家没有专门 DBA 的小公司的偶然 DBA。

我需要使用 SQL Server 身份验证为 SQL Server 2008 数据库设置只读登录名/用户,高级用户可以使用该身份验证来运行一些临时查询/报告。

我发现许多描述设置只读用户的帖子和文章,例如:

如何授予用户对所有数据库的只读访问权限

https://www.itsupportguides.com/server-side-tips/sql-management-studio-how-to-create-read-only-users/

http://www.joellipman.com/articles/microsoft/sql-server/create-read-only-database-user-in-sql-server.html

我尝试遵循这些指南并确保有问题的登录名是 onlydb_datareader和public角色的成员,但是当我以这个“只读”用户身份登录时,我仍然可以更改数据。

例如,我可以在 SSMS 中运行此查询

UPDATE [dbo].[SoftwareRunLogs]
SET [RunDateTime] = '2010-10-29 13:31:06.133'
WHERE ID = 1

它更新表中的行:

(1 row(s) affected)

我希望这样的查询会失败。

当我尝试运行一个CREATE TABLE语句时,它会失败并显示CREATE TABLE permission denied in database ...我期望的消息UPDATE,INSERT并且DELETE语句会以类似的方式失败,但它不会发生。

也许我无意中向该用户授予了写权限,但我不知道如何检查以及在哪里查看。我一定错过了一些微不足道的事情。

有任何想法吗?


有一个名为的服务器登录ReadOnlyUser,这里是它的属性:

登录属性 01

登录属性 02

登录属性 03

登录属性 04

登录属性 05

在数据库中有一个名为的用户ReadOnlyUser,这里是它的属性:

用户属性 01

用户属性 02

用户属性 03

用户属性 04

用户属性 05

sql-server sql-server-2008
  • 1 个回答
  • 2711 Views
Martin Hope
Vladimir Baranov
Asked: 2016-02-08 16:51:31 +0800 CST

当 XACT_ABORT 设置为 ON 时,在什么情况下可以从 CATCH 块内部提交事务?

  • 18

我一直在阅读 MSDNTRY...CATCH和XACT_STATE.

它具有以下示例,用于XACT_STATE在构造CATCH块中TRY…CATCH确定是提交还是回滚事务:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Test XACT_STATE for 0, 1, or -1.
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should 
    --     be rolled back.
    -- XACT_STATE = 0 means there is no transaction and
    --     a commit or rollback operation would generate an error.

    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT 'The transaction is in an uncommittable state.' +
              ' Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is active and valid.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT 'The transaction is committable.' + 
              ' Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

我不明白的是,我为什么要关心并检查XACT_STATE返回的内容?

请注意,该标志在示例XACT_ABORT中设置为ON。

如果块内有足够严重的错误TRY,控制将传递到CATCH. 所以,如果我在里面CATCH,我知道事务有问题,在这种情况下唯一明智的做法就是回滚它,不是吗?

但是,来自 MSDN 的这个例子暗示了在某些情况下,控制权被传递到CATCH,但提交事务仍然是有意义的。有人可以提供一些实际的例子,什么时候发生,什么时候有意义?

我看不到在什么情况下可以通过CATCH设置为.XACT_ABORTON

MSDN文章SET XACT_ABORT有一个示例,当事务中的某些语句成功执行而某些语句XACT_ABORT设置为时失败OFF,我理解。但是,SET XACT_ABORT ON如何在块XACT_STATE()内返回 1呢?CATCH

最初,我会这样编写这段代码:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

考虑到 Max Vernon 的回答,我会这样编写代码。他表明,在尝试之前检查是否存在活动事务是有意义的ROLLBACK。尽管如此,区块可以有注定SET XACT_ABORT ON的交易或根本没有交易。CATCH所以,无论如何,没有什么可做的COMMIT。我错了吗?

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    IF (XACT_STATE()) <> 0
    BEGIN
        -- There is still an active transaction that should be rolled back
        PRINT 'Rolling back transaction.';
        ROLLBACK TRANSACTION;
    END;

END CATCH;
GO
sql-server sql-server-2008
  • 4 个回答
  • 9528 Views
Martin Hope
Vladimir Baranov
Asked: 2015-11-04 21:29:05 +0800 CST

SQL Server 2016 标准版中是否会包含临时表?

  • 5
这个问题的答案是社区的努力。编辑现有答案以改进这篇文章。它目前不接受新的答案或交互。

新版本的 SQL Server 2016 将支持所谓的Temporal Tables。

我找不到任何文档可以说明此功能是仅限企业版还是在标准版中也可用。

一方面,临时表注意事项和限制表明它将仅限于企业:

  • 默认情况下,历史表是 PAGE 压缩的。
  • 最佳索引策略将包括聚集列存储索引 [...] 默认历史表具有聚集行存储索引

据我所知,数据压缩是企业独有的功能,但从技术上讲,标准版可以创建非压缩历史表。

列存储索引是企业独有的功能,但默认会创建行存储索引,所以还是有希望的。

另一方面,sys.dm_db_persisted_sku_features的文档在 2014 年和 2016 年是相同的,并且企业功能列表没有临时表(也许文档还没有更新?)。

  • 有谁知道,也许是非正式的,可能的变种是什么?
  • 如果有人安装了最新的 2016 RC,如果数据库有临时表,他们能否告诉我们sys.dm_db_persisted_sku_features返回什么?

如果这个功能很有可能会包含在标准版中,我可以等半年直到 2016 年发布,然后再将我的 2008 年数据库迁移到最新版本。如果没有希望,我会将它迁移到 2014 年。如果我迁移到 2014 年,它可能会在接下来的 10 年中保持这个版本。

关于搁置这个问题:

我不想要这个问题的“基于意见”的答案,我想知道事实。我曾希望这种信息会在某个地方发布,但我不知道该去哪里找。例如,直到今天我还不知道sys.dm_db_persisted_sku_features。

问题可以调整为:

  • SKU 决策是如何以及何时做出的?
  • 怎么会知道做出了这个决定?
  • 如果不是这个版本,以前的版本是怎么做的?
  • 这种信息是在RTM版本发布的时候发布的,还是更早发布的?

如果不知道一个确凿的事实(还没有做出决定,甚至 SQL Server 开发人员也不知道),那么有根据的猜测也会很好,例如,基于在以前的版本中是如何做出这个决定的。

sql-server sql-server-2016
  • 1 个回答
  • 2981 Views
Martin Hope
Vladimir Baranov
Asked: 2015-09-02 03:45:24 +0800 CST

使用 sp_getapplock 实现队列。这是对的吗?有没有更好的办法?

  • 5

我一直在阅读 Paul White 关于SQL Server Isolation Levels的一系列帖子,并遇到了一个短语:

为了强调这一点,用 T-SQL 编写的伪约束必须正确执行,无论可能发生什么并发修改。应用程序开发人员可能会使用 lock 语句来保护类似的敏感操作。T-SQL 程序员最接近风险存储过程和触发代码的工具是相对很少使用的sp_getapplock系统存储过程。这并不是说它是唯一的,甚至是首选的选择,只是它存在并且在某些情况下可能是正确的选择。

我正在使用sp_getapplock,这让我想知道我是否正确使用它,或者有更好的方法来获得预期的效果。

我有一个 C++ 应用程序,可以 24/7 循环处理所谓的“构建服务器”。有一张表格,其中列出了这些建筑服务器(大约 200 行)。可以随时添加新行,但并不经常发生。行永远不会被删除,但它们可以被标记为非活动状态。处理一个服务器可能需要几秒到几十分钟,每个服务器都不一样,有的“小”,有的“大”。一旦服务器被处理,应用程序必须等待至少 20 分钟才能再次处理它(服务器不应该被轮询太频繁)。应用程序启动了 10 个并行执行处理的线程,但我必须保证没有两个线程尝试同时处理同一个服务器. 两台不同的服务器可以而且应该同时处理,但每台服务器的处理频率不得超过 20 分钟一次。

这是一个表的定义:

CREATE TABLE [dbo].[PortalBuildingServers](
    [InternalIP] [varchar](64) NOT NULL,
    [LastCheckStarted] [datetime] NOT NULL,
    [LastCheckCompleted] [datetime] NOT NULL,
    [IsActiveAndNotDisabled] [bit] NOT NULL,
    [MaxBSMonitoringEventLogItemID] [bigint] NOT NULL,
CONSTRAINT [PK_PortalBuildingServers] PRIMARY KEY CLUSTERED 
(
    [InternalIP] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_LastCheckCompleted] ON [dbo].[PortalBuildingServers]
(
    [LastCheckCompleted] ASC
)
INCLUDE 
(
    [LastCheckStarted],
    [IsActiveAndNotDisabled],
    [MaxBSMonitoringEventLogItemID]
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

应用程序中工作线程的主循环如下所示:

for(;;)
{
    // Choose building server for checking
    std::vector<SBuildingServer> vecBS = GetNextBSToCheck();
    if (vecBS.size() == 1)
    {
        // do the check and don't go to sleep afterwards
        SBuildingServer & bs = vecBS[0];
        DoCheck(bs);
        SetCheckComplete(bs);
    }
    else
    {
        // Sleep for a while
        ...
    }
}

这里有两个函数GetNextBSToCheck,SetCheckComplete分别调用对应的存储过程。

GetNextBSToCheck返回 0 或 1 行,其中包含接下来应处理的服务器的详细信息。它是一个很长时间没有被处理的服务器。如果这个“最旧的”服务器在不到 20 分钟前被处理,则不会返回任何行,线程将等待一分钟。

SetCheckComplete设置处理完成的时间,因此可以在 20 分钟后再次选择此服务器进行处理。

最后是存储过程的代码:

GetNextToCheck:

CREATE PROCEDURE [dbo].[GetNextToCheck]
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;
    BEGIN TRY
        DECLARE @VarInternalIP varchar(64) = NULL;
        DECLARE @VarMaxBSMonitoringEventLogItemID bigint = NULL;

        DECLARE @VarLockResult int;
        EXEC @VarLockResult = sp_getapplock
            @Resource = 'PortalBSChecking_app_lock',
            @LockMode = 'Exclusive',
            @LockOwner = 'Transaction',
            @LockTimeout = 60000,
            @DbPrincipal = 'public';

        IF @VarLockResult >= 0
        BEGIN
            -- Acquired the lock
            -- Find BS that wasn't checked for the longest period
            SELECT TOP 1
                @VarInternalIP = InternalIP
                ,@VarMaxBSMonitoringEventLogItemID = MaxBSMonitoringEventLogItemID
            FROM
                dbo.PortalBuildingServers
            WHERE
                LastCheckStarted <= LastCheckCompleted
                -- this BS is not being checked right now
                AND LastCheckCompleted < DATEADD(minute, -20, GETDATE())
                -- last check was done more than 20 minutes ago
                AND IsActiveAndNotDisabled = 1
            ORDER BY LastCheckCompleted
            ;

            -- Start checking the found BS
            UPDATE dbo.PortalBuildingServers
            SET LastCheckStarted = GETDATE()
            WHERE InternalIP = @VarInternalIP;
            -- There is no need to explicitly verify if we found anything.
            -- If @VarInternalIP is null, no rows will be updated
        END;

        -- Return found BS, 
        -- or no rows if nothing was found, or failed to acquire the lock
        SELECT
            @VarInternalIP AS InternalIP
            ,@VarMaxBSMonitoringEventLogItemID AS MaxBSMonitoringEventLogItemID
        WHERE
            @VarInternalIP IS NOT NULL
            AND @VarMaxBSMonitoringEventLogItemID IS NOT NULL
        ;

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH;

END

SetCheckComplete:

CREATE PROCEDURE [dbo].[SetCheckComplete]
    @ParamInternalIP varchar(64)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;
    BEGIN TRY

        DECLARE @VarLockResult int;
        EXEC @VarLockResult = sp_getapplock
            @Resource = 'PortalBSChecking_app_lock',
            @LockMode = 'Exclusive',
            @LockOwner = 'Transaction',
            @LockTimeout = 60000,
            @DbPrincipal = 'public';

        IF @VarLockResult >= 0
        BEGIN
            -- Acquired the lock
            -- Completed checking the given BS
            UPDATE dbo.PortalBuildingServers
            SET LastCheckCompleted = GETDATE()
            WHERE InternalIP = @ParamInternalIP;
        END;

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH;

END

如您所见,我曾经sp_getapplock保证在任何给定时间只有这两个存储过程的一个实例在运行。我想我需要sp_getapplock在这两个过程中使用,因为选择“最旧”服务器的查询使用LastCheckCompleted时间,该时间由SetCheckComplete.

我认为这段代码确实保证没有两个线程同时尝试处理同一个服务器,但如果您能指出这段代码和整体方法的任何问题,我将不胜感激。那么,第一个问题:这种方法正确吗?

另外,我想知道不使用 sp_getapplock. 第二个问题:有没有更好的方法?

sql-server sql-server-2008
  • 2 个回答
  • 7540 Views

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve